يتناول هذا الموضوع كيفية تنفيذ إدخال الماوس في برنامج "ألعاب 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().