Pierwsze kroki z pakietem wejściowego SDK

Z tego dokumentu dowiesz się, jak skonfigurować i wyświetlać pakiet SDK Input w grach, które obsługują Gry Google Play na PC. Do zadań tych należy dodanie pakietu SDK do gry i wygenerowanie mapy danych wejściowych, która zawiera przypisania działań w grze do danych wejściowych użytkownika.

Zanim rozpoczniesz

Zanim dodasz pakiet SDK dotyczący danych wejściowych do gry, musisz obsługiwać dane wejściowe z klawiatury i myszy za pomocą systemu danych wejściowych silnika gry.

Pakiet SDK do obsługi wejścia przekazuje Google Play Games na PC informacje o sterowaniu w grze, aby można je wyświetlić użytkownikowi. Możesz też opcjonalnie zezwolić użytkownikom na zmianę mapowania klawiatury.

Każda kontrola to InputAction (np. „J” oznacza „Jump” – „skok”), a Twoje InputActions są zorganizowane w InputGroups. InputGroup może reprezentować inny tryb w grze, np. „Jazda”, „Chodzenie” lub „Menu główne”. Możesz też użyć InputContexts, aby wskazać, które grupy są aktywne w różnych punktach gry.

Możesz włączyć automatyczne mapowanie klawiatury, ale jeśli wolisz mieć własny interfejs mapowania elementów sterujących, możesz wyłączyć mapowanie pakietu SDK wejścia.

Na poniższym diagramie sekwencji opisano, jak działa interfejs API pakietu Input SDK:

Diagram sekwencji implementacji gry, która wywołuje interfejs API pakietu SDK do obsługi wejścia, oraz jej interakcje z urządzeniem z Androidem.

Gdy gra wdraża pakiet SDK dotyczący wprowadzania danych, elementy sterujące są wyświetlane w nakładce Gier Google Play na PC.

Warstwy Gier Google Play na PC

Nakładka Gier Google Play na PC („nakładka”) zawiera elementy sterujące zdefiniowane przez grę. Użytkownicy mogą w dowolnym momencie otworzyć nakładkę, naciskając Shift + Tab.

Przestroga w Gry Google Play na PC

Sprawdzone metody projektowania przycisków skrótów

Podczas projektowania przycisków klawiatury należy wziąć pod uwagę te sprawdzone metody:

  • Pogrupuj InputActions w logicznie powiązane InputGroups, aby ulepszyć nawigację i wykrywalność elementów sterujących podczas rozgrywki.
  • Każdy krok InputGroup może być przypisany do maksymalnie jednego kroku InputContext. Dokładne InputMap zapewnia lepszą nawigację po elementach sterujących nakładki.
  • Utwórz InputContext dla każdego typu sceny w grze. Zwykle możesz użyć jednego tagu InputContext do wszystkich scen „podobnych do menu”. Używaj różnych InputContexts w przypadku minigier w grze lub alternatywnych sterowań w pojedynczej scenie.
  • Jeśli 2 działania mają używać tego samego klucza w ramach tego samego InputContext, użyj ciągu etykiety, np. „Interakcja / Wywołanie”.
  • Jeśli 2 klucze mają być przypisane do tego samego InputAction, użyj 2 różnych InputActions, które wykonują to samo działanie w grze. Możesz użyć tego samego ciągu etykiety dla obu InputActions, ale ich identyfikatory muszą być różne.
  • Jeśli modyfikator jest stosowany do zestawu klawiszy, rozważ użycie pojedynczego klawisza InputAction z modyfikatorem zamiast wielu klawiszy InputAction, które łączą modyfikator (np. użyj ShiftW, A, S, D zamiast Shift + W, Shift + A, Shift + S, Shift + D).InputActions
  • Przemapowanie danych wejściowych jest automatycznie wyłączane, gdy użytkownik zacznie pisać w polach tekstowych. Postępuj zgodnie ze sprawdzonymi metodami implementowania pól tekstowych na Androidzie, aby zapewnić wykrywanie pól tekstowych w grze przez Androida i uniemożliwić zakłócanie im przez przypisane na nowo klawisze. Jeśli Twoja gra musi używać niekonwencjonalnych pól tekstowych, możesz użyć setInputContext()InputContext zawierającym pustą listę InputGroups, aby ręcznie wyłączyć ponowne mapowanie.
  • Jeśli gra obsługuje przemapowanie, rozważ zaktualizowanie mapowania klawiszy. W miarę możliwości unikaj zmiany identyfikatorów istniejących elementów sterujących.

Funkcja przemapowania

Gry Google Play na PC obsługują przemapowanie sterowania za pomocą klawiatury na podstawie ustawień klawiszy udostępnianych przez grę za pomocą pakietu Input SDK. Jest to opcjonalne i można je całkowicie wyłączyć. Możesz na przykład udostępnić własny interfejs do ponownego mapowania klawiatury. Aby wyłączyć przemapowanie w grze, wystarczy określić opcję przemapowania jako wyłączoną dla InputMap (więcej informacji znajdziesz w artykule Tworzenie mapy danych wejściowych).

Aby uzyskać dostęp do tej funkcji, użytkownicy muszą otworzyć nakładkę, a następnie kliknąć działanie, które chcą przemapować. Po każdym takim zdarzeniu Gry Google Play na PC mapują każde przemapowane przez użytkownika sterowanie na domyślne sterowanie, którego oczekuje gra. Dzięki temu gra nie musi wiedzieć o przemapowaniu przez gracza. Opcjonalnie możesz zaktualizować zasoby używane do wyświetlania elementów sterujących klawiaturą w grze, dodając wywołanie zwrotne do ponownego mapowania zdarzeń.

Spróbuj przypisać klawisz

Gry Google Play na PC przechowują przypisane ponownie kontrolki lokalnie dla każdego użytkownika, co umożliwia zachowanie ustawień podczas kolejnych sesji gry. Te informacje są przechowywane na dysku tylko w przypadku platformy PC i nie mają wpływu na działanie aplikacji na urządzeniach mobilnych. Dane sterujące są usuwane, gdy użytkownik odinstaluje lub ponownie zainstaluje Gry Google Play na komputerze. Te dane nie są trwałe na wielu komputerach.

Aby umożliwić korzystanie z funkcji ponownego mapowania w grze, unikaj tych ograniczeń:

Ograniczenia dotyczące remapowania

Funkcja przypisywania klawiszy może zostać wyłączona w grze, jeśli przypisania klawiszy zawierają któryś z tych przypadków:

  • Klucze wielokluczowe InputActions, które nie są złożone z klawisza modyfikującego i klawisza niemodyfikującego. Na przykład Shift + A jest prawidłową kombinacją, ale A + B, Ctrl + Alt czy Shift + A + Tab nie są prawidłowe.
  • Tag InputMap zawiera tagi InputActions, InputGroups lub InputContexts z powtarzającymi się unikalnym identyfikatorami.

Ograniczenia mapowania

Podczas projektowania przycisków do ponownego mapowania weź pod uwagę te ograniczenia:

  • Przypisywanie nowych funkcji do kombinacji klawiszy nie jest obsługiwane. Użytkownicy nie mogą na przykład zmienić skrótu Shift + A na Ctrl + B ani A na Shift + A.
  • Przypisanie nowych funkcji do przycisków InputActions myszy nie jest obsługiwane. Na przykład skrót Shift + prawy przycisk myszy nie może zostać zmieniony.

Testowanie zmiany mapowania klawiszy w emulatorze Gier Google Play na PC

Funkcję przemapowania w Gry Google Play na emulatorze PC można włączyć w dowolnym momencie, podając ten sam komendę adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

Zmiana nakładki jak na poniższym obrazie:

Nakładka z włączonym przemapowaniem klawiszy

Dodawanie pakietu SDK

Zainstaluj pakiet Input SDK na odpowiedniej platformie programistycznej.

Java i Kotlin

Pobierz pakiet SDK do obsługi danych wejściowych na potrzeby języka Java lub Kotlin, dodając zależność do pliku build.gradle na poziomie modułu:

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

Unity

Pakiet SDK do obsługi danych wejściowych to standardowy pakiet Unity z kilkoma zależnościami.

Wymagane jest zainstalowanie pakietu ze wszystkimi zależnościami. Pakiety można instalować na kilka sposobów.

Zainstaluj aplikację .unitypackage.

Pobierz plik pakietu SDK do wprowadzania danych w formacie unitypackage ze wszystkimi zależnościami. Aby zainstalować .unitypackage, wybierz Zasoby > Importuj pakiet > Własny pakiet i wskaż pobrany plik.

Instalowanie za pomocą UPM

Możesz też zainstalować pakiet za pomocą menedżera pakietów Unity, pobierając pakiet .tgz i instalując jego zależności:

Instalowanie za pomocą OpenUPM

Pakiet możesz zainstalować za pomocą OpenUPM.

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

Przykładowe gry

Przykłady integracji z pakietem SDK do obsługi danych wejściowych znajdziesz w przypadku gier w językach Kotlin i Java (AGDK Tunnel) oraz gier w Unity (Trivial Kart).

Generowanie ustawień klawiszy

Zarejestruj przypisania klawiszy, tworząc InputMap i zwracając je za pomocą funkcji InputMappingProvider. Ten przykład przedstawia: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

Definiowanie działań wejściowych

Klasa InputAction służy do mapowania klawisza lub kombinacji klawiszy na działanie w grze. InputActions muszą mieć unikalne identyfikatory we wszystkich InputActions.

Jeśli obsługujesz mapowanie, możesz określić, jakie InputActions można przemapować. Jeśli gra nie obsługuje przemapowania, opcję przemapowania należy wyłączyć we wszystkich InputActions, ale Input SDK jest na tyle inteligentny, że wyłączy przemapowanie, jeśli nie obsługuje go InputMap.

W tym przykładzie klawisz spacja jest mapowany na działanie Nawiguj.

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 z jednym klawiszem wyświetlane w nakładce.

Działania mogą też obejmować dane wejściowe z myszy. W tym przykładzie kliknięcie lewym przyciskiem myszy jest przypisane do działania Przenieś:

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

Aktywne pole wprowadzania myszy wyświetlane w nakładce.

Kombinacje klawiszy są określane przez przekazanie wielu kodów klawiszy do funkcji InputAction. W tym przykładzie spacja + Shift jest mapowana na działanie Turbo, które działa nawet wtedy, gdy spacja jest mapowana na Dysk.

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 z wieloma klawiszami wyświetlane w nakładce.

Pakiet SDK wejścia umożliwia łączenie przycisków myszy i klawiszy w jednym działaniu. Ten przykład pokazuje, że naciśnięcie Shiftkliknięcie prawym przyciskiem myszy w tym przykładowym programie powoduje dodanie punktu kontrolnego:

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

Kombinacja klawisza + mysz InputAction wyświetlana w nakładce.

Pole InputAction zawiera te pola:

  • ActionLabel: ciąg tekstowy wyświetlany w interfejsie, który reprezentuje to działanie. Lokalizacja nie jest wykonywana automatycznie, dlatego należy ją przeprowadzić na wczesnym etapie.
  • InputControls: definiuje elementy sterujące, których używa to działanie. Kontroluje mapę, aby uzyskać spójne glify na nakładce.
  • InputActionId: obiekt InputIdentifier, który przechowuje numer identyfikacyjny i wersję InputAction (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • InputRemappingOption: należy do InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Określa, czy działanie jest włączone do przemapowania. Jeśli gra nie obsługuje przemapowania, możesz pominąć to pole lub po prostu je wyłączyć.
  • RemappedInputControls: obiekt InputControls tylko do odczytu, który służy do odczytywania przemapowanego klucza ustawionego przez użytkownika w przypadku zdarzeń przemapowania (używany do otrzymywania powiadomień o zdarzeniach przemapowania).

InputControls reprezentuje dane wejściowe powiązane z działaniem i zawiera te pola:

  • AndroidKeycodes: lista liczb całkowitych reprezentujących naciśnięcia klawiszy powiązane z działaniem. Są one zdefiniowane w klasie KeyEvent lub w klasie AndroidKeycode w Unity.
  • MouseActions: lista wartości MouseAction reprezentujących sygnały myszy powiązane z tym działaniem.

Definiowanie grup danych wejściowych

InputActions są grupowane z powiązanymi logicznie działaniami za pomocą InputGroups, aby ułatwić nawigację i odkrywanie elementów sterujących w nakładce. Każdy identyfikator InputGroup musi być niepowtarzalny w przypadku wszystkich InputGroups w grze.

Dzięki grupowaniu działań związanych z wprowadzaniem danych ułatwiasz graczom znajdowanie odpowiedniego przypisania klawiszy w bieżącym kontekście.

Jeśli obsługujesz mapowanie, możesz określić, jakie InputGroups można przemapować. Jeśli gra nie obsługuje przemapowania, opcję przemapowania należy wyłączyć we wszystkich InputGroups, ale Input SDK jest na tyle inteligentny, że wyłączy przemapowanie, jeśli nie obsługuje go 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
);

W tym przykładzie na nakładce widać grupy elementów sterujących DrogaMenu:

Nakładka z mapą klawiszy, która zawiera elementy sterujące dotyczące drogi i grupy elementów sterujących menu.

InputGroup zawiera te pola:

  • GroupLabel: ciąg znaków wyświetlany w nakładce, który może służyć do logicznego grupowania zestawu działań. Ten ciąg nie jest automatycznie lokalizowany.
  • InputActions: lista obiektów InputAction zdefiniowanych w poprzednim kroku. Wszystkie te działania są wizualnie wyświetlane pod nagłówkiem grupy.
  • InputGroupId: obiekt InputIdentifier, który przechowuje identyfikator numeru i wersję InputGroup. Więcej informacji znajdziesz w artykule Identyfikatory kluczy śledzenia.
  • InputRemappingOption: InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Jeśli ta opcja jest wyłączona, przemapowanie jest wyłączone dla wszystkich obiektów InputAction należących do tej grupy, nawet jeśli opcja przemapowania jest włączona. Jeśli ta opcja jest włączona, wszystkie działania należące do tej grupy mogą być przemapowane, chyba że zostały wyłączone w przypadku poszczególnych działań.

Definiowanie kontekstów danych wejściowych

InputContexts pozwala grze używać innego zestawu elementów sterujących na klawiaturze w różnych scenach. Przykład:

  • Możesz używać różnych zestawów danych wejściowych do poruszania się po menu i do poruszania się w grze.
  • Możesz określić różne zestawy danych wejściowych w zależności od trybu poruszania się w grze, np. jazdy lub chodzenia.
  • Możesz określić różne zestawy danych wejściowych na podstawie bieżącego stanu gry, np. nawigacji po świecie gry lub rozgrywki na poszczególnych poziomach.

Gdy używasz InputContexts, nakładka pokazuje najpierw grupy kontekstu, Aby włączyć to zachowanie, wywołaj funkcję setInputContext(), aby ustawić kontekst za każdym razem, gdy gra przechodzi do innej sceny. Na poniższym obrazie widać, jak to działa: w scenie „prowadzenie pojazdu” u góry nakładki wyświetlane są działania sterowania ruchem drogowym. Po otwarciu menu „sklep” u góry nakładki wyświetlają się działania „elementów sterujących menu”.

Grupy sortowania InputContexts w nakładce.

Te aktualizacje nakładki są osiągane przez ustawienie innego InputContext w różnych punktach gry. Aby to zrobić:

  1. Grupuj InputActions z logicznie powiązanymi działaniami za pomocą InputGroups
  2. Przypisz te InputGroups do InputContext w różnych częściach gry.

InputGroups należące do tego samegoInputContext nie mogą mieć sprzecznychInputActions, w których używany jest ten sam klucz. Dobrą praktyką jest przypisywanie każdego elementu InputGroup do jednego elementu InputContext.

Poniższy przykładowy kod demonstruje logikę 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 zawiera te pola:

  • LocalizedContextLabel: ciąg znaków opisujący grupy należące do kontekstu.
  • InputContextId: obiekt InputIdentifier, który przechowuje numer identyfikacyjny i wersję InputContext (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • ActiveGroups: lista InputGroups, które mają być używane i wyświetlane u góry nakładki, gdy ten kontekst jest aktywny.

Tworzenie mapy danych wejściowych

InputMap to zbiór wszystkich obiektów InputGroup dostępnych w grze, a zatem wszystkich obiektów InputAction, których działania może oczekiwać gracz.

Gdy zgłaszasz przypisania klawiszy, tworzysz InputMap ze wszystkimi InputGroupsużywanymi w grze.

Jeśli gra nie obsługuje przypisywania, wyłącz opcję przypisywania, a zarezerwowane klawisze pozostaw puste.

W tym przykładzie tworzymy element InputMap, który służy do raportowania zbioru elementów 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 zawiera te pola:

  • InputGroups: grupy danych InputGroups z Twojej gry. Grupy są wyświetlane w kolejności w nakładce, chyba że określisz aktualne grupy w używanych wywołaniach setInputContext().
  • MouseSettings: obiekt MouseSettings wskazuje, że czułość myszy można dostosować, a mysz jest odwrócona na osi y.
  • InputMapId: obiekt InputIdentifier, który przechowuje numer identyfikacyjny i wersję InputMap (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • InputRemappingOption: InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Określa, czy funkcja przemapowania jest włączona.
  • ReservedControls: lista InputControls, których użytkownicy nie będą mogli przemapować.

Śledź identyfikatory kluczy

Obiekty InputAction, InputGroup, InputContextInputMap zawierają obiekt InputIdentifier, który przechowuje unikalny identyfikator liczbowy i identyfikator wersji w postaci ciągu znaków. Śledzenie wersji ciągu znaków w obiektach jest opcjonalne, ale zalecane, aby śledzić wersje InputMap. Jeśli nie podasz wersji ciągu znaków, będzie on pusty. W przypadku obiektów InputMap wymagana jest wersja ciągu znaków.

W tym przykładzie wersja ciągu znaków jest przypisywana do InputActions lub 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

Numery identyfikacyjne obiektów InputAction muszą być unikalne wśród wszystkich InputActions na Twoim koncie InputMap. Podobnie identyfikatory obiektów InputGroup muszą być unikalne w przypadku wszystkich InputGroupsInputMap. Ten przykład pokazuje, jak za pomocą tagu enum śledzić unikalne identyfikatory obiektów:

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 zawiera te pola:

  • UniqueId: unikalny identyfikator liczbowy, który jednoznacznie identyfikuje dany zestaw danych wejściowych.
  • VersionString: czytelny dla człowieka ciąg znaków określający wersję danych wejściowych między 2 wersjami zmian danych wejściowych.

otrzymywać powiadomienia o przemapowaniu zdarzeń (opcjonalnie),

otrzymywać powiadomienia o przypisanych wydarzeniach, aby wiedzieć, które klawisze są używane w grze; Dzięki temu gra może aktualizować zasoby wyświetlane na ekranie gry, które służą do wyświetlania elementów sterujących.

Na poniższym obrazku widać przykład tego zachowania: po przemapowaniu klawiszy G, PS odpowiednio na J, XT elementy interfejsu gry zostały zaktualizowane, aby wyświetlać klawisze ustawione przez użytkownika.

Interfejs użytkownika reagujący na zdarzenia przemapowania za pomocą podręcznego wywołania InputRemappingListener.

Ta funkcja jest realizowana przez zarejestrowanie wywołania zwrotnego InputRemappingListener. Aby wdrożyć tę funkcję, zacznij od zarejestrowania wystąpienia 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 jest powiadamiany w momencie uruchomienia po załadowaniu zapisanych przez użytkownika mapowanych elementów sterujących oraz za każdym razem, gdy użytkownik zmapuje przyciski.

Inicjowanie

Jeśli używasz InputContexts, ustaw kontekst dla każdej zmiany sceny, w tym pierwszy kontekst użyty w początkowej scenie. Ustawienie InputContext musisz wykonać po zarejestrowaniu InputMap.

Jeśli używasz usługi InputRemappingListeners, aby otrzymywać powiadomienia o przemapowaniu zdarzeń, zarejestruj InputRemappingListener przed zarejestrowaniem InputMappingProvider. W przeciwnym razie gra może przeoczyć ważne zdarzenia w czasie uruchomienia.

Ten przykład pokazuje, jak zainicjować interfejs 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
    }
}

Uporządkuj

zarejestruj instancję InputMappingProvider i wszystkie instancje InputRemappingListener, gdy gra jest zamknięta, chociaż pakiet SDK do wprowadzania danych jest na tyle inteligentny, że zapobiega wyciekom zasobów, jeśli tego nie zrobisz:

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
    }
}

Test

Implementację pakietu Input SDK możesz przetestować, ręcznie otwierając nakładkę, aby wyświetlić interfejs odtwarzacza, lub za pomocą powłoki adb na potrzeby automatycznego testowania i weryfikacji.

Emulator Gier Google Play na PC sprawdza poprawność mapy danych pod kątem typowych błędów. W przypadku takich sytuacji jak zduplikowane unikalne identyfikatory, używanie różnych map danych wejściowych lub niespełnienie reguł przemapowania (jeśli przemapowanie jest włączone) nakładka wyświetla komunikat o błędzie:Warstwy w pakiecie Input SDK

Sprawdź implementację pakietu SDK do wprowadzania danych za pomocą polecenia adb w wierszu poleceń. Aby uzyskać bieżącą mapę wejściową, użyj polecenia adb shell (zastąp MY.PACKAGE.NAME nazwą gry):

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

Jeśli rejestracja InputMap przebiegnie pomyślnie, zobaczysz dane wyjściowe podobne do tych:

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
}

Lokalizacja

Pakiet SDK do wprowadzania danych nie korzysta z systemu lokalizacji Androida. W rezultacie podczas przesyłania InputMap musisz podać zlokalizowane ciągi znaków. Możesz też użyć systemu lokalizacji silnika gry.

Proguard

Jeśli używasz narzędzia Proguard do kompresji gry, dodaj do pliku konfiguracyjnego proguarda te reguły, aby mieć pewność, że pakiet SDK nie zostanie usunięty z ostatecznego pakietu:

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

Co dalej

Po zintegrowaniu pakietu SDK do obsługi danych wejściowych z grą możesz kontynuować spełnianie pozostałych wymagań Gier Google Play na PC. Więcej informacji znajdziesz w artykule Pierwsze kroki z Grami Google Play na PC.