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

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