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

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

قبل البدء

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

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

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

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

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

مخطّط تسلسل لتنفيذ لعبة تستدعي واجهة برمجة التطبيقات Input SDK API
وتفاعلها مع جهاز 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 واحد مع مفتاح التعديل بدلاً من استخدام InputAction متعدّد يجمع مفتاح التعديل (على سبيل المثال، استخدِم Shift وW وA وS وD بدلاً من Shift + W وShift + A وShift + S وShift + D).InputActions
  • يتم إيقاف إعادة تعيين الإدخال تلقائيًا عندما يكتب المستخدم في حقول النص. اتّبِع أفضل الممارسات لتنفيذ حقول النصوص في Android لضمان أنّه يمكن لنظام Android رصد حقول النصوص في لعبتك ومنع مفاتيح إعادة الربط من التدخل فيها. إذا كانت لعبتك تستخدم حقل setInputContext() غير تقليدي، يمكنك استخدام InputContext يحتوي على قائمة فارغة من InputGroups لإيقاف إعادة الربط يدويًا.
  • إذا كانت لعبتك تتيح إعادة الربط، ننصحك بتعديل عمليات ربط المفاتيح، وهي عملية حساسة يمكن أن تتعارض مع الإصدارات التي حفظها المستخدم. تجنَّب تغيير أرقام تعريف عناصر التحكّم الحالية متى أمكن ذلك.

ميزة إعادة الربط

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

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

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

تُخزِّن ميزة "ألعاب Google Play على الكمبيوتر" عناصر التحكّم التي تمت إعادة تعيينها على الجهاز لكل مستخدم، ما يتيح الاحتفاظ بعناصر التحكّم على مدار جلسات اللعب. يتم تخزين هذه المعلومات على القرص لنظام التشغيل PC فقط، ولا تؤثّر في تجربة استخدام الأجهزة الجوّالة. يتم حذف بيانات التحكّم عند إلغاء تثبيت المستخدم لتطبيق "ألعاب 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 التالي:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

يتغيّر الغلاف كما هو موضّح في الصورة التالية:

العنصر المتراكب مع تفعيل إعادة تعيين المفاتيح

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

ثبِّت حزمة تطوير البرامج (SDK) الخاصة بتطبيق Input وفقًا لمنصّة التطوير.

Java وKotlin

يمكنك الحصول على حزمة SDK Input لـ Java أو Kotlin من خلال إضافة ملف build.gradle تابع لمستوى الوحدة:

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

الوحدة

حزمة Input SDK هي حزمة عادية في Unity تحتوي على عدة عناصر تابعة.

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

تثبيت .unitypackage

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

التثبيت باستخدام أداة UPM

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

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

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

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

أمثلة على الألعاب

للحصول على أمثلة على كيفية الدمج مع حزمة تطوير البرامج (SDK) لنظام الإدخال، يمكنك الاطّلاع على AGDK Tunnel لألعاب 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.

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

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
);

InputAction لمفتاح واحد معروض في الصورة المتراكبة

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

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
);

Mouse InputAction معروض في التراكب

يتم تحديد مجموعات المفاتيح من خلال تمرير رموز مفاتيح متعددة إلى InputAction. في هذا المثال، تم ربط المسافة + Shift بإجراء Turbo، والذي يعمل حتى عندما يكون مفتاح المسافة مرتبطًا بإجراء 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
);

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

تتيح لك حزمة تطوير البرامج (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
);

تركيبة InputAction للمفتاح + الماوس معروضة في التراكب

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

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

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

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

يتم تجميع 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
);

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

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

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

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

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

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

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

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

InputContexts لترتيب المجموعات في التراكب

يتم إجراء هذه التعديلات على التراكب من خلال ضبط 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 على التوالي، يتم تعديل عناصر واجهة المستخدم في اللعبة لعرض المفاتيح التي ضبطها المستخدم.

واجهة المستخدم تستجيب لأحداث إعادة الربط باستخدام دالة الاستدعاء InputRemappingListener

يتم تحقيق هذه الوظيفة من خلال تسجيل 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 Input في لعبتك، يمكنك المتابعة مع استيفاء أي متطلبات متبقية من متطلبات "ألعاب Google Play على الكمبيوتر". لمزيد من المعلومات، يُرجى الاطّلاع على مقالة بدء استخدام تطبيق "ألعاب Google Play" على الكمبيوتر الشخصي.