Pierwsze kroki z pakietem wejściowego SDK

Z tego dokumentu dowiesz się, jak skonfigurować i wyświetlać pakiet wejściowego pakietu SDK w grach obsługujących Gry Google Play na PC. Obejmuje to dodanie do gry pakietu SDK i wygenerowanie mapy wejściowej zawierającej przypisania akcji w grze do danych wejściowych użytkownika.

Zanim rozpoczniesz

Zanim dodasz do gry pakiet SDK do wprowadzania danych, musisz zapewnić obsługę klawiatury i myszy przy użyciu systemu wprowadzania tekstu z silnika gry.

Pakiet wprowadzania danych dostarcza Grom Google Play na PC informacje o tym, jakich elementów sterujących używa w grze, aby można je było wyświetlić użytkownikowi. Może też opcjonalnie umożliwić użytkownikom mapowanie klawiatury.

Każdy element sterujący to InputAction (np. „J” w przypadku „Jump”), a Twój InputActions jest zorganizowany w InputGroups. InputGroup może reprezentować inny tryb w grze, np. „Samochodem”, „Pieszo” lub „Menu główne”. Możesz też użyć InputContexts, aby wskazać, które grupy są aktywne na różnych etapach gry.

Możesz włączyć automatyczne mapowanie klawiatury, ale jeśli wolisz udostępnić własny interfejs do zmiany mapowania, możesz wyłączyć mapowanie wejściowego pakietu SDK.

Ten diagram sekwencji opisuje, jak działa interfejs API pakietu wejściowego SDK:

Schemat sekwencji implementacji gry, która wywołuje interfejs API pakietu wejściowego SDK i jej interakcję z urządzeniem z Androidem.

Gdy gra korzysta z pakietu SDK wejściowego, elementy sterujące są wyświetlane w nakładce Gry Google Play na PC.

Nakładka z Grami Google Play na PC

Nakładka z Gier Google Play na PC („nakładka”) wyświetla elementy sterujące zdefiniowane przez grę. Użytkownicy mogą w każdej chwili otworzyć nakładkę, naciskając Shift + Tab.

Nakładka z Grami Google Play na PC.

Sprawdzone metody projektowania powiązań kluczy

Podczas projektowania powiązań kluczy weź pod uwagę te sprawdzone metody:

  • Pogrupuj urządzenie InputActions w powiązane logicznie elementy typu InputGroups, aby poprawić nawigację i łatwiej znaleźć elementy sterujące podczas rozgrywki.
  • Każdą pozycję InputGroup przypisz maksymalnie 1 użytkownikowi InputContext. Precyzyjny InputMap ułatwia poruszanie się po elementach sterujących w nakładce.
  • Utwórz po InputContext dla poszczególnych typów scen w grze. Zwykle wystarczy użyć 1 elementu InputContext dla wszystkich scen, które przypominają menu. Używaj różnych InputContexts w przypadku minigier w grze lub jako alternatywnych elementów sterujących w pojedynczej scenie.
  • Jeśli 2 działania mają używać tego samego klucza w ramach tego samego klucza InputContext, użyj ciągu etykiety, np. „Interact / Fire”.
  • Jeśli 2 klucze są powiązane z tym samym obiektem InputAction, użyj 2 różnych kluczy InputActions, które wykonują to samo działanie w grze. W obu elementach InputActions możesz użyć tego samego ciągu etykiety, ale jego identyfikator musi być inny.
  • Jeśli klawisz modyfikujący jest stosowany do zestawu klawiszy, rozważ utworzenie pojedynczego InputAction z tym klawiszem zamiast wielu InputActions, które łączą ten klawisz (np. użyj klawiszy Shift i W, A, S, D zamiast Shift + W, Shift + A, Shift + S, Shift + D).
  • Ponowne mapowanie danych wejściowych wyłącza się automatycznie, gdy użytkownik wpisuje tekst w polach tekstowych. Postępuj zgodnie ze sprawdzonymi metodami implementacji pól tekstowych na Androidzie, aby mieć pewność, że Android będzie mógł wykrywać w grze pola tekstowe i zapobiegać zakłócaniu działania zmapowanych klawiszy. Jeśli Twoja gra musi używać niekonwencjonalnych pól tekstowych, możesz użyć pola setInputContext() z elementem InputContext zawierającym pustą listę InputGroups, aby wyłączyć ręczne mapowanie.
  • Jeśli Twoja gra obsługuje ponowne mapowanie, rozważ zaktualizowanie powiązań klawiszy w poufnej operacji, która może powodować konflikt z wersjami zapisanymi przez użytkownika. W miarę możliwości nie zmieniaj identyfikatorów dotychczasowych ustawień.

Funkcja mapowania

Gry Google Play na PC obsługują zmianę mapowania klawiszy na podstawie powiązań klawiszy w grze za pomocą pakietu wejściowego SDK. Ten krok jest opcjonalny i można go całkowicie wyłączyć. Możesz na przykład udostępnić własny interfejs do mapowania klawiatury. Aby wyłączyć ponowne mapowanie w grze, musisz po prostu wyłączyć opcję mapowania w grze InputMap (więcej informacji znajdziesz w sekcji Tworzenie mapy wejściowej).

Aby uzyskać dostęp do tej funkcji, użytkownicy muszą otworzyć nakładkę, a potem kliknąć działanie, które chcą zmienić. Po każdym zmapowaniu w Grach Google Play na PC każdy element sterujący w grze zostanie zmieniony na domyślne elementy sterujące, które powinna otrzymać Twoja gra, dzięki czemu gra nie musi wiedzieć, że użytkownik zmieni mapowanie. Opcjonalnie możesz zaktualizować zasoby używane do wyświetlania elementów sterujących klawiaturą w grze, dodając wywołanie zwrotne na potrzeby ponownego mapowania zdarzeń.

Spróbuj ponownie zmapować klucz

Gry Google Play na PC przechowują lokalnie zmapowane elementy sterujące dla każdego użytkownika, co zapewnia ich trwałość podczas wszystkich sesji gier. Te informacje są przechowywane na dysku tylko na potrzeby platformy PC i nie mają wpływu na działanie urządzeń mobilnych. Dane kontrolne są usuwane, gdy użytkownik odinstaluje lub ponownie zainstaluje Gry Google Play na PC. Te dane nie są trwałe na wielu urządzeniach PC.

Aby korzystać z funkcji ponownego mapowania w grze, unikaj tych ograniczeń:

Ograniczenia mapowania

Funkcje ponownego mapowania możesz wyłączyć w grze, jeśli powiązania klawiszy zawierają którykolwiek z tych przypadków:

  • Wieloklawiszowy InputActions, który nie składa się z klawisza modyfikującego + klawisza bez modyfikatora. Na przykład możesz użyć kombinacji klawiszy Shift + A, ale nie A + B, Ctrl + Alt i Shift + A + Tab.
  • InputMap zawiera elementy InputActions, InputGroups lub InputContexts z powtórzonymi unikalnymi identyfikatorami.

Ograniczenia mapowania

Projektując powiązania klawiszy na potrzeby ponownego mapowania, weź pod uwagę te ograniczenia:

  • Mapowanie na kombinacje klawiszy nie jest obsługiwane. Użytkownicy nie mogą na przykład zamieniać Shift + A na Ctrl + B ani A na Shift + A.
  • Mapowanie nie jest obsługiwane w przypadku urządzenia InputActions z użyciem przycisków myszy. Na przykład nie można przypisać przypisania Shift + kliknięcie prawym przyciskiem myszy.

Testowanie zmiany mapowania klawiszy w Emulatorze Gier Google Play na PC

Możesz w każdej chwili włączyć funkcję ponownego mapowania w emulatorze Gier Google Play na PC, uruchamiając to polecenie adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

Nakładka zmienia się jak poniżej:

Nakładka z włączonym mapowaniem klawiszy.

Dodaj pakiet SDK

Zainstaluj pakiet wejściowego pakietu SDK odpowiednio do używanej przez Ciebie platformy programisty.

Java i Kotlin

Pobierz pakiet wejściowego SDK dla Javy 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.0-beta'
  ...
}

Jedność

Wejściowy pakiet SDK to standardowy pakiet Unity z kilkoma zależnościami.

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

Zainstaluj aplikację .unitypackage

Pobierz plik wejściowych pakietu SDK ze wszystkimi zależnościami. Aby zainstalować .unitypackage, wybierz Zasoby > Importuj pakiet > Pakiet niestandardowy i odszukaj pobrany plik.

Instalowanie za pomocą UPM

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

Zainstaluj za pomocą OpenUPM

Możesz zainstalować pakiet za pomocą OpenUPM.

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

Przykładowe gry

Przykłady integracji z pakietem wejściowego SDK znajdziesz w artykułach AGDK Tunnel dla gier Kotlin lub Java oraz Trivial Kart na Unity.

Wygeneruj powiązania kluczy

Zarejestruj powiązania kluczy, tworząc InputMap i zwracając go za pomocą InputMappingProvider. W tym przykładzie określono 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

Zdefiniuj działania wejściowe

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

Jeśli obsługujesz mapowanie, możesz określić, które elementy InputActions można zmapować. Jeśli Twoja gra nie obsługuje mapowania, wyłącz opcję mapowania na wszystkich urządzeniach InputActions. Pamiętaj jednak, że pakiet SDK do wprowadzania danych jest na tyle inteligentny, aby wyłączyć mapowanie, jeśli nie obsługujesz go w InputMap.

W tym przykładzie klawisz spacji jest mapowany na działanie Dysk.

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

Pojedynczy klawisz FeedAction wyświetlany w nakładce.

Działania mogą też być reprezentowane przez myszy. W tym przykładzie kliknięcie lewym przyciskiem zostanie ustawione jako 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
);

Wskaźnik Mouse FeedAction wyświetlany w nakładce.

Kombinacje kluczy są określane przez przekazanie kilku kodów do klucza InputAction. W tym przykładzie spacja + shift jest zmapowana 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
);

Wieloklawiszowe działanie wejściowe wyświetlane w nakładce.

Pakiet SDK do wprowadzania danych umożliwia jednoczesne korzystanie z przycisków myszy i klawiszy w ramach jednego działania. Ten przykład pokazuje, że naciśnięcie jednocześnie Shift i kliknięcie prawym przyciskiem myszy dodaje punkt pośredni w tej przykładowej grze:

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 klawiszy wejściowego działania myszy i klawisza myszy wyświetlana w nakładce.

ParametrinputAction zawiera te pola:

  • ActionLabel: ciąg znaków wyświetlany w interfejsie, który reprezentuje to działanie. Lokalizacja nie odbywa się automatycznie, dlatego musisz od razu ją przetłumaczyć.
  • InputControls: określa dane wejściowe używane przez to działanie. Elementy sterujące mapują się na spójne glify w nakładce.
  • InputActionId: obiekt InputIdentifier, który przechowuje identyfikator numeru i wersję InputAction (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • InputRemappingOption: jedna z wartości InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Określa, czy działanie ma być mapowane. Jeśli Twoja gra nie obsługuje mapowania, możesz pominąć to pole lub wyłączyć je.
  • RemappedInputControls: obiekt InputControls tylko do odczytu używany do odczytu ponownie zmapowanego klucza ustawionego przez użytkownika przy zdarzeniach mapowania (służy do otrzymywania powiadomień o zdarzeniach ponownego mapowania).

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

  • AndroidKeycodes: to lista liczb całkowitych reprezentujących dane wejściowe z klawiatury powiązane z działaniem. Są one definiowane w klasie KeyEvent lub w klasie AndroidKeycode dla Unity.
  • MouseActions: zawiera listę wartości MouseAction reprezentujących dane wejściowe myszą powiązane z tym działaniem.

Zdefiniuj grupy danych wejściowych

Elementy InputActions są pogrupowane w powiązane logicznie działania z użyciem elementu InputGroups, które usprawnia nawigację i ułatwia wykrywalność elementów sterujących w nakładce. Każdy identyfikator InputGroup musi być unikalny we wszystkich elementach InputGroups w Twojej grze.

Organizując działania wejściowe w grupach, ułatwiasz graczowi znalezienie właściwego powiązania klawiszy w bieżącym kontekście.

Jeśli obsługujesz mapowanie, możesz określić, które elementy InputGroups można zmapować. Jeśli Twoja gra nie obsługuje mapowania, wyłącz opcję mapowania na wszystkich urządzeniach InputGroups. Pamiętaj jednak, że pakiet SDK do wprowadzania danych jest na tyle inteligentny, aby wyłączyć mapowanie, jeśli nie obsługujesz go w 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
);

Poniższy przykład pokazuje grupy danych wejściowych Elementy sterujące drogi i elementy sterujące menu w nakładce:

Nakładka zawierająca mapę wejściową, która zawiera elementy sterujące drogi i grupy wprowadzania elementów sterujących Menu.

InputGroup zawiera te pola:

  • GroupLabel: ciąg znaków wyświetlany w nakładce, którego można użyć do logicznego zgrupowania zestawu działań. Ten ciąg nie jest lokalizowany automatycznie.
  • 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 sekcji Identyfikatory kluczy śledzenia.
  • InputRemappingOption: jedna z wartości InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Jeśli ta opcja jest wyłączona, ponowne mapowanie wszystkich obiektów InputAction należących do tej grupy będzie wyłączone, nawet jeśli określono w nich włączoną opcję ponownego mapowania. Jeśli ta opcja jest włączona, wszystkie działania należące do tej grupy mogą być powielane, chyba że zostaną one wyłączone w poszczególnych działaniach.

Zdefiniuj konteksty danych wejściowych

InputContexts umożliwia użycie w grze innego zestawu elementów sterujących na klawiaturze w poszczególnych scenach. Na przykład:

  • Możesz określić różne zestawy danych wejściowych dla menu nawigacyjnego i poruszania się w grze.
  • Możesz określić różne zestawy danych wejściowych w zależności od trybu przemieszczania się w grze, np. jazda samochodem lub chodzenie.
  • Możesz podać różne zestawy danych wejściowych w zależności od bieżącego stanu gry, np. nawigacja po świecie lub granie na poziomie indywidualnym.

Gdy używasz elementu InputContexts, nakładka pokazuje najpierw używane grupy kontekstu. Aby umożliwić to zachowanie, wywołaj setInputContext(), aby określić kontekst za każdym razem, gdy gra wchodzi w inną scenę. Ten obraz przedstawia to zachowanie: w scenie „samochodowej” u góry nakładki widoczne są Elementy sterujące drogi. Po otwarciu menu „Sklep” u góry nakładki znajdują się działania „Elementy sterujące menu”.

Grupy sortowania danych wejściowego kontekstu w nakładce.

Aktualizacje te uzyskuje się przez ustawienie różnych wartości InputContext w różnych momentach gry. Aby wykonać te działania:

  1. Pogrupuj elementy typu InputActions za pomocą powiązanych logicznie działań za pomocą funkcji InputGroups
  2. Przypisz te funkcje InputGroups do elementów InputContext w grze

InputGroups, które należą do tego samego elementu,InputContextnie mogą mieć konfliktu InputActions, gdy używany jest ten sam klucz. Najlepiej jest przypisać każdy element InputGroup do jednego obiektu InputContext.

Ten przykładowy kod ilustruje 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 tekstowy opisujący grupy należące do kontekstu.
  • InputContextId: obiekt InputIdentifier, który przechowuje identyfikator numeru i wersję InputContext (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • ActiveGroups: lista elementów InputGroups, która jest używana i wyświetlana u góry nakładki, gdy ten kontekst jest aktywny.

Utwórz mapę wejściową

InputMap to zbiór wszystkich obiektów InputGroup dostępnych w grze, a tym samym wszystkie obiekty InputAction, które gracz może wykonać.

Raportując powiązania klawiszy, tworzysz InputMap ze wszystkimi InputGroups użytymi w grze.

Jeśli Twoja gra nie obsługuje mapowania, wyłącz tę opcję, a zarezerwowane klucze są puste.

Z poniższego przykładu dowiesz się, jak utworzyć InputMap służący do zgłaszania kolekcji zawierającej 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 wejściowe raportowane przez Twoją grę. Grupy są wyświetlane w nakładce w kolejności, chyba że określono bieżące grupy w wywołaniu setInputContext().
  • MouseSettings: obiekt MouseSettings wskazuje, że można regulować czułość myszy oraz że jest ona odwrócona do osi Y.
  • InputMapId: obiekt InputIdentifier, który przechowuje identyfikator numeru i wersję elementu InputMap (więcej informacji znajdziesz w sekcji Identyfikatory kluczy śledzenia).
  • InputRemappingOption: jedna z wartości InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Określa, czy funkcja mapowania jest włączona.
  • ReservedControls: lista obiektów InputControls, na które użytkownicy nie będą mogli mapować się ponownie.

Śledzenie identyfikatorów kluczy

Obiekty InputAction, InputGroup, InputContext i InputMap zawierają obiekt InputIdentifier, który przechowuje unikalny identyfikator numeru oraz identyfikator wersji ciągu znaków. Śledzenie wersji obiektów w postaci ciągów znaków jest opcjonalne, ale zalecane do śledzenia wersji obiektu InputMap. Jeśli wersja ciągu znaków nie jest podana, ciąg znaków jest pusty. Wersja ciągu znaków jest wymagana w przypadku obiektów InputMap.

Ten przykład przypisuje wersję ciągu znaków do funkcji 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

Identyfikatory numerów obiektów InputAction muszą być unikalne w obrębie wszystkich elementów InputActions w InputMap. Podobnie identyfikatory obiektów InputGroup muszą być unikalne wśród wszystkich elementów InputGroups w obiekcie InputMap. Ten przykład pokazuje, jak za pomocą enum śledzić unikalne identyfikatory obiektu:

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 służy do jednoznacznego identyfikowania danego zbioru danych wejściowych.
  • VersionString: zrozumiały dla człowieka ciąg znaków wersji, który identyfikuje wersję danych wejściowych między 2 wersjami danych wejściowych.

Otrzymuj powiadomienia o ponownym zmapowaniu zdarzeń (opcjonalnie)

Otrzymuj powiadomienia o zmianach mapowania, aby wiedzieć, jakich kluczy używasz w grze. Dzięki temu gra może aktualizować zasoby widoczne na jej ekranie, aby wyświetlać elementy sterujące akcją.

Na ilustracji poniżej widać przykład tego działania. Po ponownym zmapowaniu klawiszy G, P i S na odpowiednio J, X i T elementy interfejsu gry są aktualizowane tak, aby wyświetlać klucze ustawione przez użytkownika.

Interfejs użytkownika reagujący na remapowanie zdarzeń za pomocą wywołania zwrotnego ConversionRemappingListener.

Jest to możliwe dzięki zarejestrowaniu wywołania zwrotnego InputRemappingListener. Aby wdrożyć to działanie, zacznij od zarejestrowania instancji 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 przy uruchomieniu po wczytaniu zapisanych przez użytkownika zmienionych elementów sterujących i za każdym razem, gdy użytkownik ponownie zmapuje klucze.

Inicjacja

Jeśli używasz InputContexts, ustaw kontekst przy każdym przejściu do nowej sceny, w tym pierwszy kontekst użyty w scenie początkowej. Musisz skonfigurować InputContext po zarejestrowaniu urządzenia InputMap.

Jeśli używasz pakietu InputRemappingListeners do otrzymywania powiadomień o zmianie mapowania zdarzeń, zarejestruj element InputRemappingListener przed zarejestrowaniem zasobu InputMappingProvider. W przeciwnym razie w trakcie wprowadzania gry na rynek może pominąć ważne wydarzenia.

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

Wyrejestruj instancję InputMappingProvider i wszystkie wystąpienia InputRemappingListener, gdy gra jest zamknięta, chociaż pakiet SDK do wprowadzania danych jest na tyle sprytny, aby uniknąć wycieku 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
    }
}

Testowanie

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

Emulator Gier Google Play na PC sprawdza poprawność mapy wejściowej względem typowych błędów. W przypadkach takich jak duplikowanie unikalnych identyfikatorów, używanie różnych map danych lub niezgodność reguł mapowania (jeśli ponowne mapowanie jest włączone), nakładka wyświetla komunikat o błędzie podobny do tego: Nakładka z wejściem pakietu SDK.

Sprawdź implementację wejściowego pakietu SDK za pomocą funkcji adb w wierszu poleceń. Aby pobrać aktualną mapę wejściową, użyj tego polecenia adb shell (zastąp MY.PACKAGE.NAME nazwą gry):

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

Jeśli udało Ci się zarejestrować InputMap, 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
}

Tłumaczenie

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

ProGuard

Jeśli używasz ProGuard do minifikacji gry, dodaj te reguły do pliku konfiguracyjnego zabezpieczenia, aby pakiet SDK nie został 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 wejściowego z grą możesz kontynuować spełnianie pozostałych wymagań Gier Google Play na PC. Więcej informacji znajdziesz w artykule o pierwszych krokach z Grami Google Play na PC.