תחילת העבודה עם קלט SDK

במסמך הזה מוסבר איך להגדיר ולהציג את ה-SDK לקלט משחקים שתומכים ב-Google Play Games במחשב. המשימות כוללות את הוספת ה-SDK למשחק שלך וליצור מפת קלט, שמכילה את הקצאות של game-actions-to-user-input.

לפני שמתחילים

לפני שמוסיפים את ה-SDK לקלט למשחק, צריך לתמוך קלט מקלדת ועכבר באמצעות מנוע המשחק מערכת הקלט.

ה-SDK של קלט מספק ל-Google Play Games במחשב מידע על אילו גורמים שולטים בשימוש במשחק שלך, כדי שהם יוכלו להופיע בפני המשתמש. אפשר גם לאפשר למשתמשים מיפוי מחדש של המקלדת.

כל פקד הוא InputAction (למשל J עבור 'Jump') ואתם מארגנים את InputActions בתוך InputGroups. InputGroup יכול לייצג במצב המשחק, כמו 'נהיגה' או "הליכה" או 'תפריט ראשי'. אפשר גם יש להשתמש ב-InputContexts כדי לציין אילו קבוצות פעילות בנקודות שונות של את המשחק.

אתם יכולים להפעיל את המיפוי מחדש של המקלדת כדי שיטופל בשבילכם באופן אוטומטי, אבל אם אתם מעדיפים לספק ממשק משלכם למיפוי מחדש. אז אתם יכולים להשבית מיפוי מחדש של ה-SDK.

בתרשים הרצף הבא מוסבר איך פועל ה-API של ה-SDK לקליטת נתונים:

תרשים רצף של הטמעת משחק שקוראת ל-קלט SDK API
והאינטראקציה שלו עם מכשיר Android,
במכשיר.

כשמטמיעים במשחק את ה-SDK לקלט, הפקדים מוצגים בשכבת-העל של Google Play Games במחשב.

שכבת-על של Google Play Games במחשב

בשכבת-העל של Google Play Games במחשב ("שכבת-העל") מוצגים הפקדים מוגדר במשחק. המשתמשים ניגשים לשכבת-העל בכל עת על ידי מקישים על Shift + Tab.

שכבת-העל של Google Play Games במחשב.

שיטות מומלצות לעיצוב קישורי מפתחות

כשמתכננים את קישורי המפתחות, כדאי ליישם את השיטות המומלצות הבאות:

  • כדי להשתפר, צריך לקבץ את InputActions ל-InputGroups עם קשר לוגי ניווט ויכולת גילוי של הפקדים במהלך המשחק.
  • אפשר להקצות כל InputGroup ל-InputContext אחד לכל היותר. גרגרים דקים בזכות InputMap, חוויית הניווט באמצעי הבקרה שלך טובה יותר את שכבת-העל.
  • יוצרים InputContext לכל סוג סצנה במשחק. בדרך כלל אפשר להשתמש ב-InputContext אחד לכל הפריטים מהסוגים "כמו תפריט" סצנות. אפשר להשתמש בהגדרות InputContexts שונות לכל מיני-משחקים במשחק או עבור פקדים חלופיים לסצנה אחת.
  • אם שתי פעולות מתוכננות להשתמש באותו מפתח InputContext, אפשר להשתמש במחרוזת התווית כמו "Interact / Fire" (אינטראקציה / אש).
  • אם שני מפתחות נועדו להיות מקושרים לאותו InputAction, צריך להשתמש ב-2 InputActions שונות שמבצעות את אותה פעולה במשחק. אפשר להשתמש באותה מחרוזת תווית ב-InputActions, אבל המזהה חייב להיות אחרת.
  • אם מקש צירוף מופעל על קבוצה של מפתחות, כדאי להשתמש במקש צירוף InputAction מחליפים באמצעות מקש הצירוף במקום כמה פעמים InputActions לשלב את מקש הצירוף (לדוגמה, להשתמש במקום זאת ב-Shift וב-W, A, S, D Shift + W, Shift + A, Shift + S, Shift + D).
  • מיפוי מחדש של קלט מושבת באופן אוטומטי כשהמשתמש כותב בטקסט . כדי לוודא שהטמעת שדות טקסט ב-Android, יש לפעול לפי השיטות המומלצות שמערכת Android יכולה לזהות שדות טקסט במשחק ולמנוע מקשים שמופו מחדש להפריע להם. אם צריך להשתמש בטקסט לא קונבנציונלי במשחק שדות שבהם אפשר להשתמש ב-setInputContext() עם InputContext שמכיל רשימה ריקה של InputGroups כדי להשבית מיפוי מחדש באופן ידני.
  • אם המשחק שלך תומך במיפוי מחדש, כדאי לעדכן את קישורי המפתחות פעולה רגישה שעלולה להתנגש עם הגרסאות השמורות של המשתמש. בלי שינוי המזהים של אמצעי הבקרה הקיימים, כשהדבר אפשרי.

תכונת המיפוי מחדש

Google Play Games במחשב תומך במיפוי מחדש של השליטה במקלדת על סמך המקש הקישורים שהמשחק שלך מספק באמצעות קלט SDK. הפעולה הזו היא אופציונלית ניתן להשבית לגמרי. לדוגמה, ייתכן שתרצו לספק מקלדת משלכם. למיפוי מחדש. כדי להשבית מיפוי מחדש של המשחק, צריך רק לציין אפשרות המיפוי מחדש מושבתת ל-InputMap (יש לעיין כדי ליצור מפת קלט לקבלת מידע נוסף).

כדי לגשת לתכונה הזו, המשתמשים צריכים לפתוח את שכבת-העל וללחוץ על הפעולה שהם רוצים למפות מחדש. אחרי כל אירוע של מיפוי מחדש, Google Play Games במפות Google במחשב כל פקד שמופה מחדש על ידי המשתמש לפקדי ברירת המחדל שהמשחק מצפה לקבל מקבלים, כך שהמשחק לא צריך להיות מודע למיפוי מחדש של השחקן. שלך יכול לעדכן את הנכסים שמשמשים להצגת לחצני המקלדת את המשחק על ידי הוספת קריאה חוזרת (callback) למיפוי מחדש של אירועים.

מנסים למפות מחדש את המפתח

ב-Google Play Games במחשב מאוחסנים אמצעי הבקרה שמופו מחדש באופן מקומי לכל משתמש, שמאפשרת שליטה באופן עקבי בכל סשנים של גיימינג. המידע הזה נשמר בדיסק רק לפלטפורמת ה-PC, ולא משפיע על החוויה בנייד. נתוני הבקרה נמחקים אחרי שהמשתמש מסיר את ההתקנה של Google Play Games במחשב או מתקין אותו מחדש. הנתונים האלה לא קבועים בכמה מכשירי מחשב.

כדי לתמוך בתכונת המיפוי מחדש במשחק שלך, עליך להימנע מההגבלות הבאות:

ההגבלות של מיפוי מחדש

אפשר להשבית תכונות של מיפוי מחדש במשחק אם קישורי המקשים כוללים במקרים הבאים:

  • כמה מקשים InputActions שלא נכתבו על ידי מקש צירוף + a מקש ללא משנה. לדוגמה, Shift + A חוקי, אבל A + B, Ctrl + Alt או Shift + A + Tab לא מתאימים.
  • השדה InputMap מכיל את הערך InputActions, InputGroups או InputContexts עם מזהים ייחודיים שחוזרים על עצמם.

המגבלות של המיפוי מחדש

כשמתכננים את קישורי המפתחות למיפוי מחדש, כדאי לשקול את הדברים הבאים מגבלות:

  • אין תמיכה במיפוי מחדש לשילובי מקשים. לדוגמה, משתמשים לא יכולים ממפים מחדש את Shift + A ל-Ctrl + B או A ל-Shift + A.
  • אין תמיכה במיפוי מחדש עבור InputActions באמצעות לחצני העכבר. עבור לדוגמה, אי אפשר למפות מחדש את Shift + לחיצה ימנית.

בדיקת מיפוי מחדש של מפתחות באמולטור Google Play Games במחשב

אפשר להפעיל את תכונת המיפוי מחדש באמולטור Google Play Games במחשב בכל שלב על ידי ביצוע פקודת adb הבאה:

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

לחלופין אפשר להתקין את החבילה באמצעות Package Manager של Unity מאת מורידים את .tgz ומתקינים את יחסי התלות שלו:

התקנה באמצעות OpenUPM

אפשר להתקין את החבילה באמצעות OpenUPM.

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

משחקים לדוגמה

דוגמאות לאופן השילוב עם קלט 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.

בדוגמה הזו מתבצע מיפוי של מקש space לפעולה 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
);

קלט פעולה של מקש יחיד מוצג בשכבת-העל.

פעולות יכולות לייצג גם קלט של עכבר. בדוגמה הזו, לחיצה שמאלית היא הפעולה Move:

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 בדוגמה הזו, המקשים רווח + 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
);

קלט פעולה מרובה מפתחות שמוצג בשכבת-העל.

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

שילוב של מקש + עכבר קלטAction שמוצג בשכבת-העל.

קלטAction כולל את השדות הבאים:

  • ActionLabel: המחרוזת שמוצגת בממשק המשתמש כדי לייצג את הפעולה הזו. ההתאמה לשוק המקומי לא מתבצעת באופן אוטומטי, לכן צריך לבצע את ההתאמה לשוק המקומי הצד הקדמי.
  • InputControls: מגדיר את פקדי הקלט שבהם הפעולה הזו משתמשת. ממפים לגליפים עקביים בשכבת-העל.
  • InputActionId: אובייקט InputIdentifier שמאחסן את המזהה והגרסה של המספר של InputAction (למידע נוסף, יש לעיין במזהים של מפתחות למעקב מידע).
  • InputRemappingOption: אחד מתוך InputEnums.REMAP_OPTION_ENABLED או InputEnums.REMAP_OPTION_DISABLED. קביעה אם הפעולה מופעלת למפות מחדש. אם המשחק שלך לא תומך במיפוי מחדש, אפשר לדלג על השדה הזה או פשוט משביתים אותה.
  • RemappedInputControls: אובייקט InputControls לקריאה בלבד המשמש לקריאת מפתחות שמופו מחדש על ידי המשתמש במיפוי מחדש של אירועים (משמשים למטרות לקבל התראות לגבי מיפוי מחדש של אירועים).

InputControls מייצג את הקלטים שמשויכים לפעולה ומכיל את השדות הבאים:

  • AndroidKeycodes: הוא רשימה של מספרים שלמים שמייצגים את הקלט מהמקלדת שמשויכים לפעולה מסוימת. הם מוגדרים אירוע מרכזי או מחלקה של 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() כדי להגדיר ההקשר בכל פעם שהמשחק נכנס לסצנה אחרת. התמונה הבאה ממחישה את ההתנהגות הזאת: של הסצנה, אמצעי הבקרה בכבישים פעולות מוצגות בחלק העליון של שכבת-העל. כשפותחים את ה'חנות' תפריט, 'פקדי תפריט' פעולות מוצגות בחלק העליון של שכבת-העל.

קלטהקשר של קבוצות למיון בשכבת-העל.

העדכונים האלה בשכבת-העל מתבצעים על ידי הגדרת 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 ArrayListI<nteger(>)),
            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 בהתאמה, רכיבי ממשק המשתמש של המשחק מעודכנים הצגת המקשים שהוגדרו על ידי המשתמש.

ממשק משתמש שמגיב למיפוי מחדש של אירועים באמצעות קריאה חוזרת של קלטRemappingListener.

אפשר לעשות את זה באמצעות רישום של 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, אחרת המשחק עלול לפספס אירועים חשובים במהלך שעת ההשקה.

הדוגמה הבאה ממחישה איך לאתחל את ה-API:

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 לצורך בדיקה ואימות אוטומטיים.

האמולטור 'Google Play Games במחשב' בודק את הנכונות של מפת הקלט שלך נגד שגיאות נפוצות. בתרחישים כמו מזהים ייחודיים כפולים, שימוש להזין מפות או להיכשל בכללי המיפוי מחדש (אם המיפוי מחדש מופעל), שכבת-על מציגה הודעת שגיאה כמו: שכבת-העל של ה-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 כדי לוודא שה-SDK לא יוסר החבילה הסופית:

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

השלב הבא

אחרי שמשלבים את קלט SDK במשחק, אפשר להמשיך עם שאר הדרישות של Google Play Games במחשב. לקבלת מידע נוסף, כדאי לקרוא את המאמר איך מתחילים לעבוד עם Google Play Games במחשב.