يتناول هذا الموضوع كيفية تنفيذ إدخالات الماوس في "ألعاب 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) لتلقّي إدخالات الماوس الأولية.
بعد إجراء ذلك، يتم الإبلاغ عن حركة الماوس من خلال View.onGenericMotionEvent مع المصدر SOURCE_MOUSE
الذي يشير إلى أنّه حدث ماوس.
Kotlin
gameView.setOnGenericMotionListener { _, motionEvent -> var handled = false if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // handle the mouse event here handled = true } handled }
Java
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.
يُستخدَم هذا الإجراء على أفضل وجه لاكتشاف ما إذا كان المستخدم يمرّر مؤشر الماوس فوق الأزرار أو العناصر في إحدى الألعاب، ما يمنحك فرصة عرض مربّع تلميح أو تنفيذ حالة التمرير فوق العنصر لتسليط الضوء على ما سيختاره اللاعب. على سبيل المثال:
Kotlin
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 }
Java
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:
Kotlin
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 } } } }
Java
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; } } }
في هذا المثال، تتم معالجة الإجراء بطريقة مشابهة لأحداث التمرير فوق العنصر:
Kotlin
// 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 }
Java
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 للإزاحة الأفقية. على سبيل المثال:
Kotlin
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 }
Java
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
معاودة الاتصال. على سبيل المثال:
Kotlin
override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus) { gameView.requestPointerCapture() } }
Java
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { View gameView = findViewById(R.id.game_view); gameView.requestPointerCapture(); } }
يتم إرسال الأحداث التي يتم التقاطها من خلال requestPointerCapture()
إلى العرض القابل للتركيز الذي سجّل
OnCapturedPointerListener. على سبيل المثال:
Kotlin
gameView.focusable = View.FOCUSABLE gameView.setOnCapturedPointerListener { _, motionEvent -> Log.d("MA", "${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}") true }
Java
gameView.setFocusable(true); gameView.setOnCapturedPointerListener((view, motionEvent) -> { Log.d("MA", motionEvent.getX() + ", " + motionEvent.getY() + ", " + motionEvent.getActionButton()); return true; });
لإيقاف عملية التقاط الماوس الحصرية، مثلاً للسماح للاعبين بالتفاعل مع قائمة الإيقاف المؤقت، استخدِم View.releasePointerCapture().