بدء استخدام حزمة تطوير البرامج (SDK) للإدخال

يوضِّح هذا المستند طريقة إعداد وعرض حزمة SDK للإدخال في الألعاب المتوافقة مع "ألعاب Google Play على الكمبيوتر". تشمل المهام إضافة حزمة تطوير البرامج (SDK) إلى لعبتك وإنشاء خريطة إدخال تحتوي على مهام game‐actions‐to‐user‐input.

قبل البدء

قبل إضافة حزمة تطوير البرامج (SDK) للإدخال إلى لعبتك، يجب إتاحة إدخال لوحة المفاتيح والماوس باستخدام نظام الإدخال في محرّك اللعبة.

توفّر حِزم تطوير البرامج (SDK) للإدخال معلومات إلى برنامج "ألعاب Google Play على الكمبيوتر" حول عناصر التحكّم في لعبتك ويمكن عرضها للمستخدم. يمكن أن يسمح أيضًا اختياريًا بإعادة تخصيص لوحة المفاتيح للمستخدمين.

كل عنصر تحكّم هو InputAction (على سبيل المثال، "J" للإشارة إلى "Jump") ويمكنك تنظيم InputActions في InputGroups. قد يمثل InputGroup وضعًا مختلفًا في لعبتك، مثل "القيادة" أو "المشي" أو "القائمة الرئيسية". يمكنك أيضًا استخدام InputContexts للإشارة إلى المجموعات النشطة في مراحل مختلفة من اللعبة.

يمكنك تفعيل عملية إعادة تخصيص لوحة المفاتيح ليتمّ ضبطها تلقائيًا، ولكن إذا كنت تفضّل تقديم واجهة إعادة تخصيص التحكّم الخاصة بك، يمكنك عندئذٍ إيقاف عملية إعادة ضبط حزمة تطوير البرامج (SDK).

يوضِّح مخطّط التسلسل التالي طريقة عمل واجهة برمجة التطبيقات لحزمة تطوير البرامج (SDK) للإدخال:

رسم تخطيطي للتسلسل الخاص بتنفيذ لعبة تستدعي واجهة برمجة التطبيقات لنظام الإدخال SDK
وتفاعلها مع جهاز Android.

عندما تنفّذ لعبتك حزمة تطوير البرامج (SDK) للإدخال، يتم عرض عناصر التحكّم ضمن عنصر "ألعاب Google Play على الكمبيوتر".

تراكب "ألعاب Google Play على الكمبيوتر"

تعرض صفحة "ألعاب Google Play على الكمبيوتر" ("التراكب") عناصر التحكّم التي تحدّدها لعبتك. يمكن للمستخدمين الوصول إلى التراكب في أي وقت عن طريق الضغط على Shift + Tab.

العناصر المركّبة على برنامج "ألعاب Google Play على الكمبيوتر".

أفضل الممارسات لتصميم الروابط الرئيسية

عند تصميم الروابط الرئيسية، عليك مراعاة أفضل الممارسات التالية:

  • يمكنك تجميع InputActions في شكل InputGroups مرتبط منطقيًا لتحسين إمكانية التنقّل بين عناصر التحكّم وقابلية اكتشافها أثناء اللعب.
  • يجب تخصيص كل InputGroup إلى InputContext واحد على الأكثر. تؤدي دقة InputMap الحبيبية إلى تجربة أفضل للتنقل بين عناصر التحكم في العناصر المركّبة.
  • أنشئ InputContext لكل نوع مشهد مختلف من اللعبة. ويمكنك في العادة استخدام علامة InputContext واحدة لكل المشاهد "الشبيهة بالقائمة". يمكنك استخدام نوع مختلف من InputContexts لأي ألعاب مصغّرة في لعبتك أو كعناصر تحكم بديلة لمشهد واحد.
  • إذا كان هناك إجراءان مصمّمان لاستخدام المفتاح نفسه ضمن InputContext نفسه، استخدِم سلسلة التصنيف، مثل "التفاعل / الإطلاق".
  • إذا تم تصميم مفتاحَين للربط بمفتاح InputAction نفسه، استخدِم إذنَي InputActions مختلفَين ينفّذان الإجراء نفسه في لعبتك. ويمكنك استخدام سلسلة التصنيف نفسها لكل من InputActions، ولكن يجب أن يكون معرّفها مختلفًا.
  • في حال تطبيق مفتاح تعديل على مجموعة من المفاتيح، ننصحك باستخدام InputAction واحد مع مفتاح التعديل بدلاً من عدة InputActions التي تجمع مفتاح التعديل (مثال: استخدِم Shift وW وA وS وD بدلاً من ذلك Shift + W أو Shift + A أو Shift + S أو Shift + D).
  • يتم إيقاف عملية إعادة تعيين الإدخال تلقائيًا عندما يكتب المستخدم في حقول نصية. يُرجى اتّباع أفضل الممارسات لتطبيق الحقول النصية لنظام التشغيل Android لضمان أن يرصد Android الحقول النصية في لعبتك ومنع المفاتيح التي تمت إعادة تخصيصها من التدخل معها. إذا كان على لعبتك استخدام حقول نصية غير تقليدية، يمكنك استخدام setInputContext() مع InputContext تحتوي على قائمة فارغة من InputGroups لإيقاف عملية إعادة التعيين يدويًا.
  • إذا كانت لعبتك تتيح إعادة التعيين، ننصحك بتعديل روابط المفاتيح وهي عملية حسّاسة قد تتعارض مع النُسخ التي حفظها المستخدم. تجنَّب تغيير معرّفات عناصر التحكّم الحالية متى أمكن.

ميزة إعادة التعيين

يتيح برنامج "ألعاب Google Play على الكمبيوتر" إمكانية إعادة تخصيص عناصر التحكّم في لوحة المفاتيح استنادًا إلى عمليات ربط المفاتيح التي توفّرها لعبتك باستخدام حزمة تطوير البرامج (SDK) للإدخال. هذه الخطوة اختيارية ويمكن إيقافها بالكامل. على سبيل المثال، قد ترغب في تقديم واجهة إعادة تعيين لوحة المفاتيح الخاصة بك. لإيقاف إعادة التعيين للعبتك، يجب فقط تحديد خيار إعادة التعيين الذي تم إيقافه لجهاز InputMap (راجع إنشاء مخطط الإدخال للحصول على مزيد من المعلومات).

للوصول إلى هذه الميزة، يحتاج المستخدمون إلى فتح التراكب ثم النقر على الإجراء الذي يريدون إعادة تعيينه. وبعد كل حدث يُعاد تخصيصه لبرنامج "ألعاب Google Play على الكمبيوتر"، يضبط كل عنصر تحكّم يُعيد المستخدم تخصيصه إلى عناصر التحكّم التلقائية التي من المتوقّع أن تتلقّاها لعبتك، وبذلك لا تحتاج إلى معرفة ما يعدِّله اللاعب. يمكنك اختياريًا تعديل مواد العرض المستخدَمة لعرض عناصر التحكّم في لوحة المفاتيح في لعبتك من خلال إضافة معاودة اتصال لإعادة تخصيص الأحداث.

محاولة إعادة تخصيص المفتاح

يخزّن برنامج "ألعاب Google Play على الكمبيوتر" عناصر التحكّم التي تمت إعادة تخصيصها محليًا لكل مستخدم، ما يتيح مواصلة التحكّم في اللعبة في جميع جلسات الألعاب. ويتم تخزين هذه المعلومات على القرص من أجل نظام تشغيل الكمبيوتر فقط ولا تؤثر على تجربة الهاتف المحمول. يتم حذف بيانات التحكّم عندما يلغي المستخدم تثبيت تطبيق "ألعاب Google Play على الكمبيوتر" أو يعيد تثبيته. وهذه البيانات غير ثابتة على أجهزة الكمبيوتر المتعددة.

لإتاحة ميزة إعادة التعيين في لعبتك، عليك تجنُّب القيود التالية:

القيود المفروضة على إعادة التخصيص

يمكن إيقاف ميزات إعادة التعيين في لعبتك إذا احتوت روابط المفاتيح على أي من الحالات التالية:

  • تشير هذه السمة إلى مفاتيح InputActions المتعددة التي لا تتكون من مفتاح تعديل ومفتاح غير معدِّل. على سبيل المثال، يكون Shift + A صالحًا ولكن A + B أو Ctrl + Alt أو Shift + A + Tab غير صالح.
  • يحتوي InputMap على InputActions أو InputGroups أو InputContexts بمعرّفات فريدة متكرّرة.

قيود إعادة التعيين

عند تصميم الروابط الرئيسية لإعادة التعيين، يجب مراعاة القيود التالية:

  • لا تتوفّر إعادة التخصيص لمجموعات المفاتيح. على سبيل المثال، لا يمكن للمستخدمين إعادة تخصيص Shift + A إلى Ctrl + B أو A إلى Shift + A.
  • لا تتوفّر إعادة التخصيص لـ "InputActions" باستخدام أزرار الماوس. على سبيل المثال، لا يمكن إعادة تخصيص Shift + النقر بزر الماوس الأيمن.

اختبار عملية إعادة تخصيص المفاتيح على محاكي برنامج "ألعاب Google Play على الكمبيوتر"

يمكنك تفعيل ميزة إعادة التعيين في محاكي "ألعاب Google Play على الكمبيوتر" في أيّ وقت من خلال إصدار الأمر التالي:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

يتغير التراكب كما في الصورة التالية:

الإعلان المركّب الذي تم فيه تفعيل ميزة إعادة تخصيص المفاتيح

إضافة حزمة تطوير البرامج (SDK)

ثبِّت حزمة SDK للإدخال وفقًا للنظام الأساسي للتطوير الذي تستخدمه.

Java وKotlin

احصل على حزمة تطوير البرامج (SDK) للإدخال للغة Java أو Kotlin عن طريق إضافة تبعية إلى ملف build.gradle على مستوى الوحدة:

dependencies {
  implementation 'com.google.android.libraries.play.games:inputmapping:1.1.0-beta'
  ...
}

الانسجام

حزمة SDK للإدخال هي حزمة Unity قياسية ذات العديد من التبعيات.

يلزم تثبيت الحزمة مع جميع التبعيات. هناك عدة طرق لتثبيت الحزم.

تثبيت ".unitypackage"

نزِّل ملف unitypackage لحزمة الإدخال SDK مع جميع تبعياته. يمكنك تثبيت .unitypackage عن طريق تحديد الأصول > استيراد الحزمة > الحزمة المخصصة وتحديد موقع الملف الذي نزّلته.

التثبيت باستخدام بروتوكول UPM

بدلاً من ذلك، يمكنك تثبيت الحزمة باستخدام Unity's Package Manager من خلال تنزيل .tgz وتثبيت تبعياته:

التثبيت باستخدام OpenUPM

يمكنك تثبيت الحزمة باستخدام OpenUPM.

$ openupm add com.google.android.libraries.play.games.inputmapping

نماذج الألعاب

للحصول على أمثلة حول كيفية الدمج مع Invation SDK، يُرجى مراجعة نفق AGDK لألعاب لغة البرمجة Kotlin أو ألعاب Java وTrivial Kart لألعاب Unity.

إنشاء روابط المفاتيح

سجِّل روابط المفاتيح من خلال إنشاء InputMap وعرضها مع InputMappingProvider. يوضّح المثال التالي عنصر InputMappingProvider:

Kotlin

class InputSDKProvider : InputMappingProvider {
  override fun onProvideInputMap(): InputMap {
    TODO("Not yet implemented")
  }
}

Java

public class InputSDKProvider implements InputMappingProvider {
    private static final String INPUTMAP_VERSION = "1.0.0";

    @Override
    @NonNull
    public InputMap onProvideInputMap() {
        // TODO: return an InputMap
    }
}

#C

#if PLAY_GAMES_PC
using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;

public class InputSDKProvider : InputMappingProviderCallbackHelper
{
    public static readonly string INPUT_MAP_VERSION = "1.0.0";

    public override InputMap OnProvideInputMap()
    {
        // TODO: return an InputMap
    }
}
#endif

تحديد إجراءات الإدخال

يتم استخدام فئة InputAction لربط مجموعة مفاتيح أو مجموعة مفاتيح بأحد الألعاب. يجب أن يكون لدى "InputActions" معرّفات فريدة في جميع InputActions.

إذا كنت تدعم عملية إعادة التعيين، يمكنك تحديد ما يمكن إعادة تخصيصه من InputActions. إذا كانت لعبتك لا تتيح إعادة التعيين، يجب إيقاف خيار إعادة التعيين لجميع InputActions، ولكن حزمة SDK للإدخال ما يكفي لإيقافها إذا لم تكن متاحة في InputMap.

يربط هذا المثال مفتاح المسافة بإجراء Drive.

Kotlin

companion object {
  private val driveInputAction = InputAction.create(
    "Drive",
    InputActionsIds.DRIVE.ordinal.toLong(),
    InputControls.create(listOf(KeyEvent.KEYCODE_SPACE), emptyList()),
    InputEnums.REMAP_OPTION_ENABLED)
}

Java

private static final InputAction driveInputAction = InputAction.create(
    "Drive",
    InputEventIds.DRIVE.ordinal(),
    InputControls.create(
            Collections.singletonList(KeyEvent.KEYCODE_SPACE),
            Collections.emptyList()),
    InputEnums.REMAP_OPTION_ENABLED
);

#C

private static readonly InputAction driveInputAction = InputAction.Create(
    "Drive",
    (long)InputEventIds.DRIVE,
    InputControls.Create(
        new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE) }.ToJavaList(),
        new ArrayList<Integer>()),
    InputEnums.REMAP_OPTION_ENABLED
);

يتم عرض InAction لمفتاح واحد في التراكب.

ويمكن أن تمثل الإجراءات مدخلات الماوس أيضًا. يحدد هذا المثال النقر الأيسر على إجراء نقل:

Kotlin

companion object {
  private val mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal.toLong(),
    InputControls.create(emptyList(), listOf(InputControls.MOUSE_LEFT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

Java

private static final InputAction mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal(),
    InputControls.create(
            Collections.emptyList(),
            Collections.singletonList(InputControls.MOUSE_LEFT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

#C

private static readonly InputAction mouseInputAction = InputAction.Create(
    "Move",
    (long)InputEventIds.MOUSE_MOVEMENT,
    InputControls.Create(
        new ArrayList<Integer>(),
        new[] { new Integer((int)PlayMouseAction.MouseLeftClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

إجراء الإدخال عن طريق الماوس المعروض في التراكب.

يتم تحديد مجموعات المفاتيح من خلال تمرير رموز مفاتيح متعدّدة إلى InputAction. في هذا المثال، يتم ربط space + shift بإجراء Turbo، الذي يعمل حتى عند ربط Space بتطبيق Drive.

Kotlin

companion object {
  private val turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
      emptyList()),
    InputEnums.REMAP_OPTION_ENABLED)
}

Java

private static final InputAction turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal(),
    InputControls.create(
            Arrays.asList(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
            Collections.emptyList()
    ),
    InputEnums.REMAP_OPTION_ENABLED
);

#C

private static readonly InputAction turboInputAction = InputAction.Create(
    "Turbo",
    (long)InputEventIds.TURBO,
    InputControls.Create(
        new[]
        {
            new Integer(AndroidKeyCode.KEYCODE_SHIFT_LEFT),
            new Integer(AndroidKeyCode.KEYCODE_SPACE)
        }.ToJavaList(),
        new ArrayList<Integer>()),
    InputEnums.REMAP_OPTION_ENABLED
);

يتم عرض enterAction متعدد المفاتيح في التراكب.

تتيح لك حزمة أدوات تطوير البرامج (SDK) للإدخال مزج الماوس وأزرار المفاتيح معًا في إجراء واحد. يشير هذا المثال إلى أنّ الضغط على Shift والنقر بزر الماوس الأيمن يضيفان نقطة وسيطة في نموذج اللعبة هذا:

Kotlin

companion object {
  private val addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KeyEvent.KEYCODE_TAB),
      listOf(InputControls.MOUSE_RIGHT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

Java

private static final InputAction addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal(),
    InputControls.create(
            Collections.singletonList(KeyEvent.KEYCODE_TAB),
            Collections.singletonList(InputControls.MOUSE_RIGHT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

#C

private static readonly InputAction addWaypointInputAction = InputAction.Create(
    "Add waypoint",
    (long)InputEventIds.ADD_WAYPOINT,
    InputControls.Create(
        new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE) }.ToJavaList(),
        new[] { new Integer((int)PlayMouseAction.MouseRightClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

يتم عرض نسخة من مفتاح الإدخال والماوس في التراكب.

يحتوي InAction على الحقول التالية:

  • ActionLabel: السلسلة المعروضة في واجهة المستخدم لتمثيل هذا الإجراء. لا تتم عملية الأقلمة بشكل تلقائي، لذا يجب تنفيذ أي أقلمة بشكل مسبق.
  • InputControls: يحدّد عناصر التحكّم في الإدخال التي يستخدمها هذا الإجراء. يتم تعيين عناصر التحكم إلى رموز رسومية متسقة في التراكب.
  • InputActionId: كائن InputIdentifier يخزِّن رقم تعريف الرقم وإصدار InputAction (يُرجى الاطّلاع على معرّفات مفاتيح التتبّع للحصول على مزيد من المعلومات).
  • InputRemappingOption: إما InputEnums.REMAP_OPTION_ENABLED أو InputEnums.REMAP_OPTION_DISABLED. ويحدد ما إذا كان الإجراء ممكنًا لإعادة التعيين. إذا كانت لعبتك لا تتيح إعادة التعيين، يمكنك تخطي هذا الحقل أو إيقافه ببساطة.
  • RemappedInputControls: يمكن استخدام كائن InputControls للقراءة فقط لقراءة المفتاح الذي أعاد تعيينه للمستخدم عند إعادة تخصيص الأحداث (يُستخدَم في تلقّي إشعارات بشأن إعادة تخصيص الأحداث).

تمثّل السمة InputControls المدخلات المرتبطة بإجراء ما وتحتوي على الحقول التالية:

  • AndroidKeycodes: هو قائمة من الأعداد الصحيحة التي تمثّل مدخلات لوحة المفاتيح المرتبطة بأحد الإجراءات. ويتم تحديدها في فئة KeyEvent أو فئة AndroidKeycode لـ Unity.
  • MouseActions: هي قائمة من قيم MouseAction التي تمثّل إدخالات الماوس المرتبطة بهذا الإجراء.

تحديد مجموعات الإدخال

تم تجميع InputActions مع الإجراءات ذات الصلة المنطقية باستخدام InputGroups لتحسين التنقل وعناصر التحكم في قابلية الاكتشاف في التراكب. ويجب أن يكون كل معرّف InputGroup فريدًا على مستوى جميع InputGroups في لعبتك.

فمن خلال تنظيم إجراءات الإدخال في مجموعات، يصبح من السهل على اللاعب العثور على الربط الرئيسي المناسب للسياق الحالي.

إذا كنت تدعم عملية إعادة التعيين، يمكنك تحديد ما يمكن إعادة تخصيصه من InputGroups. إذا كانت لعبتك لا تتيح إعادة التعيين، يجب إيقاف خيار إعادة التعيين لجميع InputGroups، ولكن حزمة SDK للإدخال ما يكفي لإيقافها إذا لم تكن متاحة في InputMap.

Kotlin

companion object {
  private val menuInputGroup = InputGroup.create(
    "Menu keys",
    listOf(
      navigateUpInputAction,
      navigateLeftInputAction,
      navigateDownInputAction,
      navigateRightInputAction,
      openMenuInputAction,
      returnMenuInputAction),
    InputGroupsIds.MENU_ACTION_KEYS.ordinal.toLong(),
    InputEnums.REMAP_OPTION_ENABLED
  )
}

Java

private static final InputGroup menuInputGroup = InputGroup.create(
    "Menu keys",
    Arrays.asList(
           navigateUpInputAction,
           navigateLeftInputAction,
           navigateDownInputAction,
           navigateRightInputAction,
           openMenuInputAction,
           returnMenuInputAction),
    InputGroupsIds.MENU_ACTION_KEYS.ordinal(),
    REMAP_OPTION_ENABLED
);

#C

private static readonly InputGroup menuInputGroup = InputGroup.Create(
    "Menu keys",
    new[]
    {
        navigateUpInputAction,
        navigateLeftInputAction,
        navigateDownInputAction,
        navigateRightInputAction,
        openMenuInputAction,
        returnMenuInputAction,
    }.ToJavaList(),
    (long)InputGroupsIds.MENU_ACTION_KEYS,
    InputEnums.REMAP_OPTION_ENABLED
);

يعرض المثال التالي مجموعات الإدخال عناصر التحكّم في الطريق وعناصر التحكّم في القائمة في العرض المركّب:

يعرض التراكب خريطة الإدخال التي تحتوي على عناصر التحكم في الطريق
والقائمة تسيطر على مجموعات الإدخال.

يتضمّن InputGroup الحقول التالية:

  • GroupLabel: سلسلة يتم عرضها في التراكب ويمكن استخدامها لتجميع مجموعة من الإجراءات بشكل منطقي. لا تتم ترجمة هذه السلسلة تلقائيًا.
  • InputActions: قائمة بكائنات InputAction تم تحديدها في الخطوة السابقة يتم عرض كل هذه الإجراءات بشكل مرئي أسفل عنوان المجموعة.
  • InputGroupId: كائن InputIdentifier يخزِّن رقم تعريف الرقم وإصدار InputGroup. اطّلع على أرقام تعريف مفاتيح التتبّع للحصول على مزيد من المعلومات.
  • InputRemappingOption: إما InputEnums.REMAP_OPTION_ENABLED أو InputEnums.REMAP_OPTION_DISABLED. أمّا في حال إيقافها، فسيتم إيقاف عملية إعادة التخصيص على جميع كائنات InputAction التي تنتمي إلى هذه المجموعة حتى إذا تم تفعيل خيار إعادة التعيين. إذا تم تفعيل هذه الميزة، يمكن إعادة تفعيل جميع الإجراءات التي تنتمي إلى هذه المجموعة ما لم يتم إيقافها من خلال الإجراءات الفردية.

تحديد سياقات الإدخال

يسمح InputContexts للعبتك باستخدام مجموعة مختلفة من عناصر تحكم لوحة المفاتيح لعرض مشاهد مختلفة من لعبتك. مثلاً:

  • يمكنك تحديد مجموعات مختلفة من المدخلات للتنقل في القوائم مقابل التنقل في اللعبة.
  • ويمكنك تحديد مجموعات مختلفة من الإدخالات اعتمادًا على وضع الحركة في لعبتك، مثل القيادة مقابل المشي.
  • يمكنك تحديد مجموعات مختلفة من الإدخالات بناءً على الحالة الراهنة للعبتك، مثل التنقّل في عالم آخر أو لعب مستوى فردي.

عند استخدام InputContexts، يعرض التراكب أولاً مجموعات السياق قيد الاستخدام. لتفعيل هذا السلوك، عليك استدعاء setInputContext() لضبط السياق كلما انتقلت اللعبة إلى مشهد مختلف. توضح الصورة التالية هذا السلوك: في مشهد "القيادة"، تظهر إجراءات عناصر التحكم في الطريق في أعلى التراكب. عند فتح قائمة "المتجر"، يتم عرض إجراءات "عناصر التحكم في القائمة" أعلى التراكب.

EnterContexts فرز المجموعات في التراكب.

ويتم تطبيق هذه التعديلات على العناصر المركّبة من خلال ضبط سمة InputContext مختلفة في مراحل مختلفة من لعبتك. ولإجراء ذلك:

  1. يمكنك تجميع InputActions مع الإجراءات ذات الصلة المنطقية باستخدام InputGroups.
  2. يمكنك تعيين InputGroups هذه إلى InputContext للأجزاء المختلفة من لعبتك.

InputGroups التي تنتمي إلى الجهاز نفسهInputContextلا يمكن أن يكون لها تعارضInputActions حيث يتم استخدام المفتاح نفسه. ومن الممارسات الجيدة تحديد كل InputGroup لعنصر InputContext واحد.

يوضّح الرمز النموذجي التالي منطق InputContext:

Kotlin

companion object {
  val menuSceneInputContext = InputContext.create(
    "Menu",
    InputIdentifier.create(
      INPUTMAP_VERSION,
      InputContextIds.MENU_SCENE.ordinal.toLong()),
    listOf(basicMenuNavigationInputGroup, menuActionsInputGroup))

  val gameSceneInputContext = InputContext.create(
    "Game",
    InputIdentifier.create(
      INPUTMAP_VERSION,
      InputContextIds.GAME_SCENE.ordinal.toLong()),
    listOf(
      movementInputGroup,
      mouseActionsInputGroup,
      emojisInputGroup,
      gameActionsInputGroup))
}

Java

public static final InputContext menuSceneInputContext = InputContext.create(
        "Menu",
        InputIdentifier.create(
                INPUTMAP_VERSION,
                InputContextIds.MENU_SCENE.ordinal()),
        Arrays.asList(
                basicMenuNavigationInputGroup,
                menuActionsInputGroup
        )
);

public static final InputContext gameSceneInputContext = InputContext.create(
        "Game",
        InputIdentifier.create(
                INPUTMAP_VERSION,
                InputContextIds.GAME_SCENE.ordinal()),
        Arrays.asList(
                movementInputGroup,
                mouseActionsInputGroup,
                emojisInputGroup,
                gameActionsInputGroup
        )
);

#C

public static readonly InputContext menuSceneInputContext = InputContext.Create(
    "Menu",
    InputIdentifier.Create(
        INPUT_MAP_VERSION,
        (long)InputContextsIds.MENU_SCENE),
    new[]
    {
        basicMenuNavigationInputGroup,
        menuActionsInputGroup
    }.ToJavaList()
);

public static readonly InputContext gameSceneInputContext = InputContext.Create(
    "Game",
    InputIdentifier.Create(
        INPUT_MAP_VERSION,
        (long)InputContextsIds.GAME_SCENE),
    new[]
    {
        movementInputGroup,
        mouseActionsInputGroup,
        emojisInputGroup,
        gameActionsInputGroup
    }.ToJavaList()
);

يتضمّن InputContext الحقول التالية:

  • LocalizedContextLabel: سلسلة تصف المجموعات التي تنتمي إلى السياق.
  • InputContextId: كائن InputIdentifier يخزِّن رقم تعريف الرقم وإصدار InputContext (يُرجى الاطّلاع على معرّفات مفاتيح التتبّع للحصول على مزيد من المعلومات).
  • ActiveGroups: قائمة بـ InputGroups سيتم استخدامها وعرضها أعلى التراكب عندما يكون هذا السياق نشطًا.

إنشاء خريطة إدخال

والعنصر InputMap هو مجموعة من كل عناصر InputGroup المتوفّرة في اللعبة، وبالتالي جميع عناصر InputAction التي يمكن أن يتوقّع اللاعب تنفيذها.

عند الإبلاغ عن روابط المفاتيح، تنشئ InputMap تتضمّن كل InputGroups المستخدَمة في لعبتك.

إذا كانت لعبتك لا تتيح إعادة التعيين، فاضبط خيار إعادة التعيين على إيقاف والمفاتيح المحجوزة فارغة.

ينشئ المثال التالي InputMap تُستخدم للإبلاغ عن مجموعة من InputGroups.

Kotlin

companion object {
  val gameInputMap = InputMap.create(
    listOf(
      basicMenuNavigationInputGroup,
      menuActionKeysInputGroup,
      movementInputGroup,
      mouseMovementInputGroup,
      pauseMenuInputGroup),
    MouseSettings.create(true, false),
    InputIdentifier.create(INPUTMAP_VERSION, INPUT_MAP_ID.toLong()),
    InputEnums.REMAP_OPTION_ENABLED,
    // Use ESCAPE as reserved remapping key
    listof(InputControls.create(listOf(KeyEvent.KEYCODE_ESCAPE), emptyList()))
  )
}

Java

public static final InputMap gameInputMap = InputMap.create(
        Arrays.asList(
                basicMenuNavigationInputGroup,
                menuActionKeysInputGroup,
                movementInputGroup,
                mouseMovementInputGroup,
                pauseMenuInputGroup),
        MouseSettings.create(true, false),
        InputIdentifier.create(INPUTMAP_VERSION, INPUT_MAP_ID),
        REMAP_OPTION_ENABLED,
        // Use ESCAPE as reserved remapping key
        Arrays.asList(
                InputControls.create(
                        Collections.singletonList(KeyEvent.KEYCODE_ESCAPE),
                        Collections.emptyList()
                )
        )
);

#C

public static readonly InputMap gameInputMap = InputMap.Create(
    new[]
    {
        basicMenuNavigationInputGroup,
        menuActionKeysInputGroup,
        movementInputGroup,
        mouseMovementInputGroup,
        pauseMenuInputGroup,
    }.ToJavaList(),
    MouseSettings.Create(true, false),
    InputIdentifier.Create(INPUT_MAP_VERSION, INPUT_MAP_ID),
    InputEnums.REMAP_OPTION_ENABLED,
    // Use ESCAPE as reserved remapping key
    new[]
    {
        InputControls.Create(
            New[] {
            new Integer(AndroidKeyCode.KEYCODE_ESCAPE)
        }.ToJavaList(),
        new ArrayList<Integer>())
    }.ToJavaList()
);

يتضمّن InputMap الحقول التالية:

  • InputGroups: مجموعات الإدخال التي تم الإبلاغ عنها من خلال لعبتك يتم عرض المجموعات بالترتيب في التراكب ما لم يتم تحديد المجموعات الحالية التي يتم استخدامها استدعاء setInputContext().
  • MouseSettings: يشير الكائن MouseSettings إلى إمكانية ضبط حساسية الماوس وعكس اتجاه الماوس على المحور y.
  • InputMapId: كائن InputIdentifier يخزِّن رقم تعريف الرقم وإصدار InputMap (يُرجى الاطّلاع على معرّفات مفاتيح التتبّع للحصول على مزيد من المعلومات).
  • InputRemappingOption: إما InputEnums.REMAP_OPTION_ENABLED أو InputEnums.REMAP_OPTION_DISABLED. وتحدد ما إذا كانت ميزة إعادة التعيين مفعّلة.
  • ReservedControls: قائمة تتضمّن InputControls لن يُسمح للمستخدمين بإعادة التخصيص إليها.

أرقام تعريف مفاتيح التتبُّع

تحتوي الكائنات InputAction وInputGroup وInputContext وInputMap على كائن InputIdentifier الذي يخزِّن رقم تعريف رقم فريدًا ومعرّف إصدار السلسلة. إنّ تتبُّع إصدار السلسلة للكائنات أمر اختياري ولكن يُنصح به لتتبُّع إصدارات InputMap. إذا لم يتم توفير إصدار السلسلة، فهذا يعني أن السلسلة فارغة. يجب إصدار سلسلة للكائنات InputMap.

يعيّن المثال التالي إصدار سلسلة إلى InputActions أو InputGroups:

Kotlin

class InputSDKProviderKotlin : InputMappingProvider {
  companion object {
    const val INPUTMAP_VERSION = "1.0.0"
    private val enterMenuInputAction = InputAction.create(
      "Enter menu",
      InputControls.create(listOf(KeyEvent.KEYCODE_ENTER), emptyList()),
      InputIdentifier.create(
        INPUTMAP_VERSION, InputActionsIds.ENTER_MENU.ordinal.toLong()),
      InputEnums.REMAP_OPTION_ENABLED
    )

    private val movementInputGroup  = InputGroup.create(
      "Basic movement",
      listOf(
        moveUpInputAction,
        moveLeftInputAction,
        moveDownInputAction,
        mouseGameInputAction),
      InputIdentifier.create(
        INPUTMAP_VERSION, InputGroupsIds.BASIC_MOVEMENT.ordinal.toLong()),
      InputEnums.REMAP_OPTION_ENABLED)
  }
}

Java

public class InputSDKProvider implements InputMappingProvider {
    public static final String INPUTMAP_VERSION = "1.0.0";

    private static final InputAction enterMenuInputAction = InputAction.create(
            "Enter menu",
            InputControls.create(
                    Collections.singletonList(KeyEvent.KEYCODE_ENTER),
                    Collections.emptyList()),
            InputIdentifier.create(
                    INPUTMAP_VERSION, InputActionsIds.ENTER_MENU.ordinal()),
            InputEnums.REMAP_OPTION_ENABLED
    );

    private static final InputGroup movementInputGroup = InputGroup.create(
            "Basic movement",
            Arrays.asList(
                    moveUpInputAction,
                    moveLeftInputAction,
                    moveDownInputAction,
                    moveRightInputAction,
                    mouseGameInputAction
            ),
            InputIdentifier.create(
                    INPUTMAP_VERSION, InputGroupsIds.BASIC_MOVEMENT.ordinal()),
            InputEnums.REMAP_OPTION_ENABLED
    );
}

#C


#if PLAY_GAMES_PC

using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;

public class InputSDKMappingProvider : InputMappingProviderCallbackHelper
{
    public static readonly string INPUT_MAP_VERSION = "1.0.0";

    private static readonly InputAction enterMenuInputAction =
        InputAction.Create(
            "Enter menu",
            InputControls.Create(
                new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE)}.ToJavaList(),
                new ArrayList<Integer>()),
            InputIdentifier.Create(
                INPUT_MAP_VERSION,
                (long)InputEventIds.ENTER_MENU),
            InputEnums.REMAP_OPTION_ENABLED
        );

    private static readonly InputGroup movementInputGroup = InputGroup.Create(
        "Basic movement",
        new[]
        {
            moveUpInputAction,
            moveLeftInputAction,
            moveDownInputAction,
            moveRightInputAction,
            mouseGameInputAction
        }.ToJavaList(),
        InputIdentifier.Create(
            INPUT_MAP_VERSION,
            (long)InputGroupsIds.BASIC_MOVEMENT),
        InputEnums.REMAP_OPTION_ENABLED
    );
}
#endif

يجب أن تكون أرقام تعريف أرقام كائنات InputAction فريدة عبر جميع InputActions في InputMap. وبالمثل، يجب أن تكون معرّفات عناصر InputGroup فريدة في جميع InputGroups في InputMap. يوضّح النموذج التالي كيفية استخدام enum لتتبُّع المعرّفات الفريدة للعنصر:

Kotlin

enum class InputActionsIds {
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

enum class InputGroupsIds {
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

enum class InputContextIds {
    MENU_SCENE, // Basic menu navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

const val INPUT_MAP_ID = 0

Java

public enum InputActionsIds {
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

public enum InputGroupsIds {
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

public enum InputContextIds {
    MENU_SCENE, // Basic navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

public static final long INPUT_MAP_ID = 0;

#C

public enum InputActionsIds
{
    NAVIGATE_UP,
    NAVIGATE_DOWN,
    ENTER_MENU,
    EXIT_MENU,
    // ...
    JUMP,
    RUN,
    EMOJI_1,
    EMOJI_2,
    // ...
}

public enum InputGroupsIds
{
    // Main menu scene
    BASIC_NAVIGATION, // WASD, Enter, Backspace
    MENU_ACTIONS, // C: chat, Space: quick game, S: store
    // Gameplay scene
    BASIC_MOVEMENT, // WASD, space: jump, Shift: run
    MOUSE_ACTIONS, // Left click: shoot, Right click: aim
    EMOJIS, // Emojis with keys 1,2,3,4 and 5
    GAME_ACTIONS, // M: map, P: pause, R: reload
}

public enum InputContextIds
{
    MENU_SCENE, // Basic navigation, menu actions
    GAME_SCENE, // Basic movement, mouse actions, emojis, game actions
}

public static readonly long INPUT_MAP_ID = 0;

يتضمّن InputIdentifier الحقول التالية:

  • UniqueId: معرّف رقم فريد تم ضبطه لتحديد مجموعة معيّنة من بيانات الإدخال بشكل فريد.
  • VersionString: سلسلة إصدار قابلة للقراءة البشرية لتحديد نسخة من بيانات الإدخال بين نسختين من تغييرات بيانات الإدخال.

تلقّي إشعارات بشأن إعادة تخصيص الأحداث (اختياري)

يمكنك تلقّي إشعارات حول أحداث إعادة التعيين للحصول على معلومات حول المفاتيح التي يتم استخدامها في لعبتك. يتيح هذا الإجراء للعبتك تعديل مواد العرض المعروضة على شاشة اللعبة المستخدَمة لعرض عناصر التحكّم في الإجراءات.

تُظهر الصورة التالية مثالاً لهذا السلوك، حيث يتم تعديل عناصر واجهة المستخدم الخاصة باللعبة بعد إعادة ضبط المفاتيح G وP وS إلى J وX وT على التوالي لعرض المفاتيح التي ضبطها المستخدم.

واجهة مستخدم تتفاعل مع أحداث إعادة تخصيص الأحداث باستخدام معاودة الاتصال enterRemappingListener

ويمكن تحقيق هذه الوظيفة من خلال تسجيل استدعاء InputRemappingListener. لتنفيذ هذه الطريقة، ابدأ بتسجيل مثيل InputRemappingListener:

Kotlin

class InputSDKRemappingListener : InputRemappingListener {
  override fun onInputMapChanged(inputMap: InputMap) {
    Log.i(TAG, "Received update on input map changed.")
    if (inputMap.inputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED) {
      return
    }
    for (inputGroup in inputMap.inputGroups()) {
      if (inputGroup.inputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED) {
        continue
      }
      for (inputAction in inputGroup.inputActions()) {
        if (inputAction.inputRemappingOption() != InputEnums.REMAP_OPTION_DISABLED) {
          // Found InputAction remapped by user
          processRemappedAction(inputAction)
        }
      }
    }
  }

  private fun processRemappedAction(remappedInputAction: InputAction) {
    // Get remapped action info
    val remappedControls = remappedInputAction.remappedInputControls()
    val remappedKeyCodes = remappedControls.keycodes()
    val mouseActions = remappedControls.mouseActions()
    val version = remappedInputAction.inputActionId().versionString()
    val remappedActionId = remappedInputAction.inputActionId().uniqueId()
    val currentInputAction: Optional<InputAction>
    currentInputAction = if (version == null || version.isEmpty()
      || version == InputSDKProvider.INPUTMAP_VERSION
    ) {
      getCurrentVersionInputAction(remappedActionId)
    } else {
      Log.i(TAG,
            "Detected version of user-saved input action defers from current version")
      getCurrentVersionInputActionFromPreviousVersion(
        remappedActionId, version)
    }
    if (!currentInputAction.isPresent) {
      Log.e(TAG, String.format(
        "can't find remapped input action with id %d and version %s",
        remappedActionId, if (version == null || version.isEmpty()) "UNKNOWN" else version))
      return
    }
    val originalControls = currentInputAction.get().inputControls()
    val originalKeyCodes = originalControls.keycodes()
    Log.i(TAG, String.format(
      "Found input action with id %d remapped from key %s to key %s",
      remappedActionId,
      keyCodesToString(originalKeyCodes),
      keyCodesToString(remappedKeyCodes)))

    // TODO: make display changes to match controls used by the user
  }

  private fun getCurrentVersionInputAction(inputActionId: Long): Optional<InputAction> {
    for (inputGroup in InputSDKProvider.gameInputMap.inputGroups()) {
      for (inputAction in inputGroup.inputActions()) {
        if (inputAction.inputActionId().uniqueId() == inputActionId) {
          return Optional.of(inputAction)
        }
      }
    }
    return Optional.empty()
  }

  private fun getCurrentVersionInputActionFromPreviousVersion(
    inputActionId: Long, previousVersion: String
  ): Optional<InputAction7gt; {
    // TODO: add logic to this method considering the diff between the current and previous
    //  InputMap.
    return Optional.empty()
  }

  private fun keyCodesToString(keyCodes: List<Int>): String {
    val builder = StringBuilder()
    for (keyCode in keyCodes) {
      if (!builder.toString().isEmpty()) {
        builder.append(" + ")
      }
      builder.append(keyCode)
    }
    return String.format("(%s)", builder)
  }

  companion object {
    private const val TAG = "InputSDKRemappingListener"
  }
}

Java

public class InputSDKRemappingListener implements InputRemappingListener {

    private static final String TAG = "InputSDKRemappingListener";

    @Override
    public void onInputMapChanged(InputMap inputMap) {
        Log.i(TAG, "Received update on input map changed.");
        if (inputMap.inputRemappingOption() ==
                InputEnums.REMAP_OPTION_DISABLED) {
            return;
        }
        for (InputGroup inputGroup : inputMap.inputGroups()) {
            if (inputGroup.inputRemappingOption() ==
                    InputEnums.REMAP_OPTION_DISABLED) {
                continue;
            }
            for (InputAction inputAction : inputGroup.inputActions()) {
                if (inputAction.inputRemappingOption() !=
                        InputEnums.REMAP_OPTION_DISABLED) {
                    // Found InputAction remapped by user
                    processRemappedAction(inputAction);
                }
            }
        }
    }

    private void processRemappedAction(InputAction remappedInputAction) {
        // Get remapped action info
        InputControls remappedControls =
            remappedInputAction.remappedInputControls();
        List<Integer> remappedKeyCodes = remappedControls.keycodes();
        List<Integer> mouseActions = remappedControls.mouseActions();
        String version = remappedInputAction.inputActionId().versionString();
        long remappedActionId = remappedInputAction.inputActionId().uniqueId();
        Optional<InputAction> currentInputAction;
        if (version == null || version.isEmpty()
                    || version.equals(InputSDKProvider.INPUTMAP_VERSION)) {
            currentInputAction = getCurrentVersionInputAction(remappedActionId);
        } else {
            Log.i(TAG, "Detected version of user-saved input action defers " +
                    "from current version");
            currentInputAction =
                    getCurrentVersionInputActionFromPreviousVersion(
                            remappedActionId, version);
        }
        if (!currentInputAction.isPresent()) {
            Log.e(TAG, String.format(
                    "input action with id %d and version %s not found",
                    remappedActionId, version == null || version.isEmpty() ?
                            "UNKNOWN" : version));
            return;
        }
        InputControls originalControls =
                currentInputAction.get().inputControls();
        List<Integer> originalKeyCodes = originalControls.keycodes();

        Log.i(TAG, String.format(
                "Found input action with id %d remapped from key %s to key %s",
                remappedActionId,
                keyCodesToString(originalKeyCodes),
                keyCodesToString(remappedKeyCodes)));

        // TODO: make display changes to match controls used by the user
    }

    private Optional<InputAction> getCurrentVersionInputAction(
            long inputActionId) {
        for (InputGroup inputGroup :
                    InputSDKProvider.gameInputMap.inputGroups()) {
            for (InputAction inputAction : inputGroup.inputActions()) {
                if (inputAction.inputActionId().uniqueId() == inputActionId) {
                    return Optional.of(inputAction);
                }
            }
        }
        return Optional.empty();
    }

    private Optional<InputAction>
            getCurrentVersionInputActionFromPreviousVersion(
                    long inputActionId, String previousVersion) {
        // TODO: add logic to this method considering the diff between your
        // current and previous InputMap.
        return Optional.empty();
    }

    private String keyCodesToString(List<Integer> keyCodes) {
        StringBuilder builder = new StringBuilder();
        for (Integer keyCode : keyCodes) {
            if (!builder.toString().isEmpty()) {
                builder.append(" + ");
            }
            builder.append(keyCode);
        }
        return String.format("(%s)", builder);
    }
}

#C

#if PLAY_GAMES_PC

using System.Text;
using Java.Lang;
using Java.Util;
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.Inputmapping.Datamodel;
using UnityEngine;

public class InputSDKRemappingListener : InputRemappingListenerCallbackHelper
{
    public override void OnInputMapChanged(InputMap inputMap)
    {
        Debug.Log("Received update on remapped controls.");
        if (inputMap.InputRemappingOption() == InputEnums.REMAP_OPTION_DISABLED)
        {
            return;
        }
        List<InputGroup> inputGroups = inputMap.InputGroups();
        for (int i = 0; i < inputGroups.Size(); i ++)
        {
            InputGroup inputGroup = inputGroups.Get(i);
            if (inputGroup.InputRemappingOption()
                    == InputEnums.REMAP_OPTION_DISABLED)
            {
                continue;
            }
            List<InputAction> inputActions = inputGroup.InputActions();
            for (int j = 0; j < inputActions.Size(); j ++)
            {
                InputAction inputAction = inputActions.Get(j);
                if (inputAction.InputRemappingOption()
                        != InputEnums.REMAP_OPTION_DISABLED)
                {
                    // Found action remapped by user
                    ProcessRemappedAction(inputAction);
                }
            }
        }
    }

    private void ProcessRemappedAction(InputAction remappedInputAction)
    {
        InputControls remappedInputControls =
                remappedInputAction.RemappedInputControls();
        List<Integer> remappedKeycodes = remappedInputControls.Keycodes();
        List<Integer> mouseActions = remappedInputControls.MouseActions();
        string version = remappedInputAction.InputActionId().VersionString();
        long remappedActionId = remappedInputAction.InputActionId().UniqueId();
        InputAction currentInputAction;
        if (string.IsNullOrEmpty(version)
                || string.Equals(
                version, InputSDKMappingProvider.INPUT_MAP_VERSION))
        {
            currentInputAction = GetCurrentVersionInputAction(remappedActionId);
        }
        else
        {
            Debug.Log("Detected version of used-saved input action defers" +
                " from current version");
            currentInputAction =
                GetCurrentVersionInputActionFromPreviousVersion(
                    remappedActionId, version);
        }
        if (currentInputAction == null)
        {
            Debug.LogError(string.Format(
                "Input Action with id {0} and version {1} not found",
                remappedActionId,
                string.IsNullOrEmpty(version) ? "UNKNOWN" : version));
            return;
        }
        InputControls originalControls = currentInputAction.InputControls();
        List<Integer> originalKeycodes = originalControls.Keycodes();

        Debug.Log(string.Format(
            "Found Input Action with id {0} remapped from key {1} to key {2}",
            remappedActionId,
            KeyCodesToString(originalKeycodes),
            KeyCodesToString(remappedKeycodes)));
        // TODO: update HUD according to the controls of the user
    }

    private InputAction GetCurrentVersionInputAction(
            long inputActionId)
    {
        List<InputGroup> inputGroups =
            InputSDKMappingProvider.gameInputMap.InputGroups();
        for (int i = 0; i < inputGroups.Size(); i++)
        {
            InputGroup inputGroup = inputGroups.Get(i);
            List<InputAction> inputActions = inputGroup.InputActions();
            for (int j = 0; j < inputActions.Size(); j++)
            {
                InputAction inputAction = inputActions.Get(j);
                if (inputAction.InputActionId().UniqueId() == inputActionId)
                {
                    return inputAction;
                }
            }
        }
        return null;
    }

    private InputAction GetCurrentVersionInputActionFromPreviousVersion(
            long inputActionId, string version)
    {
        // TODO: add logic to this method considering the diff between your
        // current and previous InputMap.
        return null;
    }

    private string KeyCodesToString(List<Integer> keycodes)
    {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < keycodes.Size(); i ++)
        {
            Integer keycode = keycodes.Get(i);
            if (builder.Length > 0)
            {
                builder.Append(" + ");
            }
            builder.Append(keycode.IntValue());
        }
        return string.Format("({0})", builder.ToString());
    }
}
#endif

يتم إرسال إشعار إلى "InputRemappingListener" في وقت الإطلاق بعد تحميل عناصر التحكّم التي أعاد ضبطها المستخدم، وبعد كل مرّة يُعيد فيها المستخدِم تحديد مفاتيحه.

الإعداد

إذا كنت تستخدم InputContexts، اضبط السياق في كل عملية انتقال إلى مشهد جديد، بما في ذلك السياق الأول المستخدم في المشهد الأول. يجب ضبط InputContext بعد تسجيل InputMap.

إذا كنت تستخدم "InputRemappingListeners" لتلقّي إشعارات بشأن أحداث إعادة الخريطة عليك تسجيل InputRemappingListener قبل تسجيل InputMappingProvider، وإلا قد تفوتك أحداث مهمة خلال وقت الإطلاق.

يعرض النموذج التالي كيفية إعداد واجهة برمجة التطبيقات:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    if (isGooglePlayGamesOnPC()) {
        val inputMappingClient = Input.getInputMappingClient(this)
        // Register listener before registering the provider
        inputMappingClient.registerRemappingListener(InputSDKRemappingListener())
        inputMappingClient.setInputMappingProvider(
                InputSDKProvider())
        // Set the context after you have registered the provider.
        inputMappingClient.setInputContext(InputSDKProvider.menuSceneInputContext)
    }
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (isGooglePlayGamesOnPC()) {
        InputMappingClient inputMappingClient =
                Input.getInputMappingClient(this);
        // Register listener before registering the provider
        inputMappingClient.registerRemappingListener(
                new InputSDKRemappingListener());
        inputMappingClient.setInputMappingProvider(
                new InputSDKProvider());
        // Set the context after you have registered the provider
        inputMappingClient.setInputContext(InputSDKProvider.menuSceneInputContext);
    }
}

#C

#if PLAY_GAMES_PC
using Google.Android.Libraries.Play.Games.Inputmapping;
using Google.Android.Libraries.Play.Games.InputMapping.ExternalType.Android.Content;
using Google.LibraryWrapper.Java;
#endif

public class GameManager : MonoBehaviour
{
#if PLAY_GAMES_PC
    private InputSDKMappingProvider _inputMapProvider =
        new InputSDKMappingProvider();
    private InputMappingClient _inputMappingClient;
#endif

    public void Awake()
    {
#if PLAY_GAMES_PC
        Context context = (Context)Utils.GetUnityActivity().GetRawObject();
        _inputMappingClient = Google.Android.Libraries.Play.Games.Inputmapping
            .Input.GetInputMappingClient(context);
        // Register listener before registering the provider.
        _inputMappingClient.RegisterRemappingListener(
            new InputSDKRemappingListener());
        _inputMappingClient.SetInputMappingProvider(_inputMapProvider);
        // Register context after you have registered the provider.
       _inputMappingClient.SetInputContext(
           InputSDKMappingProvider.menuSceneInputContext);
#endif
    }
}

تَنظيم

يمكنك إلغاء تسجيل مثيل InputMappingProvider وأي مثيلات InputRemappingListener عند إغلاق اللعبة، على الرغم من أنّ حزمة تطوير البرامج (SDK) للإدخال ذكية بما يكفي لتجنُّب تسرُّب الموارد في حال عدم إجراء ما يلي:

Kotlin

override fun onDestroy() {
    if (isGooglePlayGamesOnPC()) {
        val inputMappingClient = Input.getInputMappingClient(this)
        inputMappingClient.clearInputMappingProvider()
        inputMappingClient.clearRemappingListener()
    }

    super.onDestroy()
}

Java

@Override
protected void onDestroy() {
    if (isGooglePlayGamesOnPC()) {
        InputMappingClient inputMappingClient =
                Input.getInputMappingClient(this);
        inputMappingClient.clearInputMappingProvider();
        inputMappingClient.clearRemappingListener();
    }

    super.onDestroy();
}

#C

public class GameManager : MonoBehaviour
{
    private void OnDestroy()
    {
#if PLAY_GAMES_PC
        _inputMappingClient.ClearInputMappingProvider();
        _inputMappingClient.ClearRemappingListener();
#endif
    }
}

الاختبار

يمكنك اختبار تنفيذ حزمة تطوير البرامج (SDK) للإدخال عن طريق فتح التراكب يدويًا لعرض تجربة اللاعب، أو عبر واجهة adb Shell للاختبار والتحقق الآلي.

يتحقّق محاكي برنامج "ألعاب Google Play على الكمبيوتر" من صحة خريطة الإدخال من خلال رصد الأخطاء الشائعة. بالنسبة إلى سيناريوهات مثل تكرار المعرّفات الفريدة أو استخدام خرائط إدخال مختلفة أو الإخفاق في قواعد إعادة التعيين (في حال تفعيل إعادة التعيين)، يعرض العنصر الذي يظهر على سطح الفيديو رسالة خطأ على النحو التالي: تراكب حزمة تطوير البرامج (SDK) للإدخال

تحقَّق من تنفيذ حزمة تطوير البرامج (SDK) للإدخال باستخدام adb في سطر الأوامر. للحصول على الخريطة الحالية التي تم إدخالها، استخدِم الأمر adb shell التالي (استبدِل MY.PACKAGE.NAME باسم لعبتك):

adb shell dumpsys input_mapping_service --get MY.PACKAGE.NAME

ستظهر لك نتائج مشابهة إذا تم تسجيل InputMap بنجاح:

Getting input map for com.example.inputsample...
Successfully received the following inputmap:
# com.google.android.libraries.play.games.InputMap@d73526e1
input_groups {
  group_label: "Basic Movement"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
    }
    unique_id: 0
  }
  input_actions {
    action_label: "Left"
    input_controls {
      keycodes: 29
      keycodes: 21
    }
    unique_id: 1
  }
  input_actions {
    action_label: "Right"
    input_controls {
      keycodes: 32
      keycodes: 22
    }
    unique_id: 2
  }
  input_actions {
    action_label: "Use"
    input_controls {
      keycodes: 33
      keycodes: 66
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 3
  }
}
input_groups {
  group_label: "Special Input"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
      keycodes: 62
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 4
  }
  input_actions {
    action_label: "Duck"
    input_controls {
      keycodes: 47
      keycodes: 20
      keycodes: 113
      mouse_actions: MOUSE_RIGHT_CLICK
      mouse_actions_value: 1
    }
    unique_id: 5
  }
}
mouse_settings {
  allow_mouse_sensitivity_adjustment: true
  invert_mouse_movement: true
}

ترجمة

لا تستخدم حِزم تطوير البرامج (SDK) للإدخال نظام الأقلمة في Android. نتيجةً لذلك، يجب توفير سلاسل مترجَمة عند إرسال InputMap. يمكنك أيضًا استخدام نظام أقلمة محرك اللعبة.

ProGuard

عند استخدام Proguard لتصغير لعبتك، أضف القواعد التالية إلى ملف إعداد Proguard لضمان عدم إزالة حزمة SDK من الحزمة النهائية:

-keep class com.google.android.libraries.play.hpe.** { *; }
-keep class com.google.android.libraries.play.games.inputmapping.** { *; }

الخطوات التالية

بعد دمج حزمة SDK للإدخال في لعبتك، يمكنك مواصلة استيفاء أي متطلبات متبقّية ضمن برنامج "ألعاب Google Play على الكمبيوتر". لمعرفة مزيد من المعلومات، يُرجى الاطّلاع على بدء استخدام برنامج "ألعاب Google Play على الكمبيوتر".