الإدخال بالماوس

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

لقطة شاشة لـ &quot;وضع الكمبيوتر الشخصي(KiwiMouse)&quot; الذي تم اختياره في قائمة السياقات

بعد إجراء ذلك، سيتم الإبلاغ عن حركة الماوس من خلال View.onPublicMotionEvent مع الإشارة إلى أنّ المصدر 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().