ورودی موشواره

این موضوع نحوه پیاده‌سازی ورودی موشواره برای «بازی‌های Google Play» در رایانه را برای بازی‌هایی که در آن‌ها حالت ترجمه ورودی تجربه ایده‌آلی برای بازیکن فراهم نمی‌کند پوشش می‌دهد.

بازیکنان رایانه معمولاً به‌جای صفحه‌نمایش لمسی، صفحه‌کلید و موشواره دارند، بنابراین مهم است که درنظر بگیرید آیا بازی شما از ورودی موشواره پشتیبانی می‌کند یا نه. به‌طور پیش‌فرض، «بازی‌های Google Play در رایانه» هر رویداد کلیک چپ موشواره را به یک رویداد تک‌ضرب مجازی تبدیل می‌کند. این حالت «حالت ترجمه ورودی» نامیده می‌شود.

اگرچه این حالت با تغییرات اندک باعث می‌شود بازی شما کار کند، اما تجربه‌ای با حس بومی برای بازیکنان رایانه فراهم نمی‌کند. برای این کار، توصیه می‌کنیم موارد زیر را پیاده‌سازی کنید:

  • حالت‌های شناور برای منوهای زمینه‌ای به‌جای کنش‌های فشار طولانی
  • برای کنش‌های جایگزین که با فشار طولانی یا در منو زمینه‌ای انجام می‌شوند، کلیک راست کنید
  • نگاه با ماوس برای بازی‌های کنشی اول شخص یا سوم شخص به‌جای رویداد فشار دادن و کشیدن

برای پشتیبانی از الگوهای رابط کاربری که در رایانه‌های شخصی رایج است، باید حالت ترجمه ورودی را غیرفعال کنید.

مدیریت ورودی برای «بازی‌های Google Play در رایانه» با مدیریت ورودی در ChromeOS یکسان است. تغییراتی که از رایانه‌ها پشتیبانی می‌کنند، بازی شما را برای همه بازیکنان Android بهبود می‌بخشند.

غیرفعال کردن حالت ترجمه ورودی

در فایل AndroidManifest.xml، android.hardware.type.pc ویژگی را اعلام کنید. این کد نشان می‌دهد که بازی شما از سخت‌افزار رایانه استفاده می‌کند و حالت ترجمه ورودی را غیرفعال می‌کند. علاوه‌براین، افزودن required="false" کمک می‌کند مطمئن شوید که بازی شما همچنان می‌تواند در تلفن‌ها و رایانه‌های لوحی بدون موشواره نصب شود. برای مثال:

<manifest ...>
  <uses-feature
      android:name="android.hardware.type.pc"
      android:required="false" />
  ...
</manifest>

وقتی بازی راه‌اندازی می‌شود، نسخه تولید «بازی‌های Google Play در رایانه» به حالت صحیح تغییر می‌کند. هنگام اجرا در شبیه‌ساز توسعه‌دهنده، باید روی نماد نوار وظیفه کلیک راست کنید، گزینه‌های توسعه‌دهنده را انتخاب کنید، و سپس حالت رایانه(KiwiMouse) را برای دریافت ورودی خام موشواره انتخاب کنید.

نماگرفت «حالت رایانه(KiwiMouse)» انتخاب‌شده در منو زمینه‌ای

پس‌از انجام این کار، حرکت موشواره با View.onGenericMotionEvent با منبع SOURCE_MOUSE گزارش می‌شود که نشان می‌دهد رویداد موشواره است.

کاتلین

gameView.setOnGenericMotionListener { _, motionEvent ->
    var handled = false
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        handled = true
    }
    handled
}

جاوا

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        return true;
    }
    return false;
});

برای جزئیات مربوط به مدیریت ورودی موشواره، به مستندات ChromeOS مراجعه کنید.

مدیریت حرکت موشواره

برای تشخیص حرکت موشواره، به رویدادهای ACTION_HOVER_ENTER، ACTION_HOVER_EXIT، و ACTION_HOVER_MOVE گوش دهید.

این ویژگی برای تشخیص کاربرانی که روی دکمه‌ها یا اشیای بازی نگه می‌دارند بسیار مفید است و به شما فرصت می‌دهد تا جعبه راهنمایی نمایش دهید یا وضعیت نگه داشتن ماوس را پیاده‌سازی کنید تا آنچه را که بازیکن می‌خواهد انتخاب کند برجسته کنید. برای مثال:

کاتلین

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when(motionEvent.action) {
           MotionEvent.ACTION_HOVER_ENTER -> Log.d("MA", "Mouse entered at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_EXIT -> Log.d("MA", "Mouse exited at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_MOVE -> Log.d("MA", "Mouse hovered at ${motionEvent.x}, ${motionEvent.y}")
       }
       handled = true
   }

   handled
}

جاوا

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_HOVER_ENTER:
                Log.d("MA", "Mouse entered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                Log.d("MA", "Mouse exited at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_MOVE:
                Log.d("MA", "Mouse hovered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

مدیریت دکمه‌های موشواره

رایانه‌های شخصی مدت‌ها است که هم دکمه چپ و هم دکمه راست موشواره را دارند و به عناصر تعاملی کنش‌های اصلی و ثانویه می‌دهند. در بازی، کنش‌هایی مثل تک‌ضرب زدن روی دکمه بهتر است به کلیک چپ تخصیص داده شوند، درحالی‌که کنش‌های لمس و نگه داشتن با کلیک راست طبیعی‌تر هستند. در بازی‌های استراتژی هم‌زمان، ممکن است از کلیک چپ برای انتخاب و کلیک راست برای حرکت دادن استفاده کنید. بازی‌های تیراندازی اول شخص ممکن است آتش اصلی و ثانویه را به کلیک چپ و راست اختصاص دهند. یک بازی دونده بی‌نهایت ممکن است از کلیک چپ برای پریدن و از کلیک راست برای دویدن استفاده کند. از رویداد کلیک وسط پشتیبانی نمی‌کنیم.

برای مدیریت فشار دکمه‌ها، از ACTION_DOWN و ACTION_UP استفاده کنید. سپس از getActionButton برای تعیین اینکه کدام دکمه کنش را راه‌اندازی کرده است یا از getButtonState برای دریافت وضعیت همه دکمه‌ها استفاده کنید.

در این مثال، از یک شمارش برای کمک به نمایش نتیجه getActionButton استفاده شده است:

کاتلین

enum class MouseButton {
   LEFT,
   RIGHT,
   UNKNOWN;
   companion object {
       fun fromMotionEvent(motionEvent: MotionEvent): MouseButton {
           return when (motionEvent.actionButton) {
               MotionEvent.BUTTON_PRIMARY -> LEFT
               MotionEvent.BUTTON_SECONDARY -> RIGHT
               else -> UNKNOWN
           }
       }
   }
}

جاوا

enum MouseButton {
    LEFT,
    RIGHT,
    MIDDLE,
    UNKNOWN;
    static MouseButton fromMotionEvent(MotionEvent motionEvent) {
        switch (motionEvent.getActionButton()) {
            case MotionEvent.BUTTON_PRIMARY:
                return MouseButton.LEFT;
            case MotionEvent.BUTTON_SECONDARY:
                return MouseButton.RIGHT;
            default:
                return MouseButton.UNKNOWN;
        }
    }
}

در این مثال، کنش مشابه رویدادهای نگه داشتن نشانگر مدیریت می‌شود:

کاتلین

// Handle the generic motion event
gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_BUTTON_PRESS -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} pressed at ${motionEvent.x}, ${motionEvent.y}"
           )
           MotionEvent.ACTION_BUTTON_RELEASE -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} released at ${motionEvent.x}, ${motionEvent.y}"
           )
       }
       handled = true
   }

   handled
}

جاوا

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_BUTTON_PRESS:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " pressed at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_BUTTON_RELEASE:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " released at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

پیمایش چرخ موشواره را مدیریت کنید

توصیه می‌کنیم به‌جای اشاره‌های «نیشگون گرفتن برای زوم‌پیش کردن» یا لمس کردن و کشیدن نواحی پیمایش در بازی، از چرخ پیمایش موشواره استفاده کنید.

برای خواندن مقادیر چرخ پیمایش، به رویداد ACTION_SCROLL گوش دهید. دلتا از آخرین قاب را می‌توان بااستفاده از getAxisValue با AXIS_VSCROLL برای جبران عمودی و AXIS_HSCROLL برای جبران افقی بازیابی کرد. برای مثال:

کاتلین

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_SCROLL -> {
               val scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL)
               val scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)
               Log.d("MA", "Mouse scrolled $scrollX, $scrollY")
           }
       }
       handled = true
   }
   handled
}

جاوا

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_SCROLL:
                float scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
                float scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
                Log.d("MA", "Mouse scrolled " + scrollX + ", " + scrollY);
                break;
        }
        return true;
    }
    return false;
});

ضبط ورودی موشواره

برخی‌از بازی‌ها باید کنترل کامل مکان‌نمای موشواره را دراختیار بگیرند، مثل بازی‌های اکشن اول یا سوم شخص که حرکت موشواره را به حرکت دوربین نگاشت می‌کنند. برای به‌دست گرفتن کنترل انحصاری موشواره، View.requestPointerCapture() را فراخوانی کنید.

‫requestPointerCapture() فقط زمانی کار می‌کند که سلسله‌مراتب نمای حاوی نمای شما کانونی باشد. به همین دلیل، نمی‌توانید در onCreate برگشت تماس، اشاره‌گر را به‌دست آورید. باید منتظر تعامل بازیکن برای گرفتن اشاره‌گر موشواره بمانید، مثلاً هنگام تعامل با منو اصلی، یا از onWindowFocusChanged بازخوان استفاده کنید. برای مثال:

کاتلین

override fun onWindowFocusChanged(hasFocus: Boolean) {
   super.onWindowFocusChanged(hasFocus)

   if (hasFocus) {
       gameView.requestPointerCapture()
   }
}

جاوا

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus) {
        View gameView = findViewById(R.id.game_view);
        gameView.requestPointerCapture();
    }
}

رویدادهای ضبط‌شده توسط requestPointerCapture() به نمای کانونی که OnCapturedPointerListener را ثبت کرده است ارسال می‌شود. برای مثال:

کاتلین

gameView.focusable = View.FOCUSABLE
gameView.setOnCapturedPointerListener { _, motionEvent ->
    Log.d("MA", "${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}")
    true
}

جاوا

gameView.setFocusable(true);
gameView.setOnCapturedPointerListener((view, motionEvent) -> {
    Log.d("MA", motionEvent.getX() + ", " + motionEvent.getY() + ", " + motionEvent.getActionButton());
    return true;
});

برای انتشار انحصاری ضبط موشواره، مثلاً برای اینکه به بازیکنان اجازه داده شود با منو توقف موقت تعامل داشته باشند، View.releasePointerCapture() را فراخوانی کنید.