Pierwsze kroki z pakietem wejściowego SDK

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

Zanim rozpoczniesz

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

Pakiet Input SDK przekazuje do Gier Google Play na PC informacje o tym, jakich elementów sterujących używa Twoja gra, aby można było wyświetlać je użytkownikowi. Może też opcjonalnie zezwalać użytkownikom na zmianę mapowania klawiatury.

Każdy element sterujący to InputAction (np. „J” w przypadku „Skoku”). InputActions są uporządkowane 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 momentach gry.

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

Ten diagram sekwencji opisuje, jak działa interfejs API pakietu Input SDK:

Diagram sekwencji implementacji gry, która wywołuje interfejs Input SDK API i wchodzi w interakcję z urządzeniem z Androidem.

Gdy gra zaimplementuje pakiet Input SDK, elementy sterujące będą wyświetlane w nakładce Gier Google Play na PC.

Nakładka Gier Google Play na PC

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

Nakładka Gier Google Play na PC.

Sprawdzone metody projektowania powiązań klawiszy

Podczas projektowania przypisań klawiszy pamiętaj o tych sprawdzonych metodach:

  • Grupuj InputActions w logicznie powiązane InputGroups, aby ułatwić poruszanie się po elementach sterujących i ich znajdowanie podczas rozgrywki.
  • Przypisz każdy element InputGroup do co najwyżej jednego elementu InputContext. Drobne ziarnoInputMap zapewnia lepsze wrażenia podczas poruszania się po elementach sterujących w nakładce.
  • Utwórz InputContext dla każdego typu sceny w grze. Zwykle możesz używać jednego tagu InputContext do wszystkich scen przypominających menu. Używaj różnych InputContexts w przypadku minigier w swojej grze lub alternatywnych elementów sterujących w jednej scenie.
  • Jeśli 2 działania mają używać tego samego klawisza w tym samym InputContext, użyj ciągu etykiety, np. „Interakcja / Strzelanie”.
  • Jeśli 2 klucze mają być powiązane z tym samym InputAction, użyj 2 różnych InputActions, które wykonują to samo działanie w grze. Możesz użyć tego samego ciągu znaków etykiety w przypadku obu atrybutów InputActions, ale ich identyfikatory muszą być różne.
  • Jeśli klawisz modyfikujący jest stosowany do zestawu klawiszy, rozważ użycie pojedynczego klawisza InputAction z klawiszem modyfikującym zamiast wielu klawiszy InputActions, które łączą klawisz modyfikujący (np. użyj ShiftW, A, S, D zamiast Shift + W, Shift + A, Shift + S, Shift + D).
  • Remapowanie danych wejściowych jest automatycznie wyłączane, gdy użytkownik wpisuje tekst w polach tekstowych. Postępuj zgodnie ze sprawdzonymi metodami wdrażania pól tekstowych na Androidzie, aby mieć pewność, że Android może wykrywać pola tekstowe w Twojej grze i zapobiegać zakłócaniu ich działania przez ponownie zmapowane klawisze. Jeśli w grze musisz używać nietypowych pól tekstowych, możesz użyć setInputContext()InputContext zawierającym pustą listę InputGroups, aby ręcznie wyłączyć ponowne mapowanie.
  • Jeśli Twoja gra obsługuje zmianę przypisań klawiszy, rozważ zaktualizowanie przypisań klawiszy. Jest to operacja wrażliwa, która może powodować konflikty z wersjami zapisanymi przez użytkownika. W miarę możliwości unikaj zmiany identyfikatorów istniejących elementów sterujących.

Funkcja ponownego mapowania

Gry Google Play na PC obsługują zmianę przypisań klawiszy na podstawie powiązań klawiszy, które Twoja gra udostępnia za pomocą pakietu Input SDK. Jest to opcjonalne i można to całkowicie wyłączyć. Możesz na przykład udostępnić własny interfejs do zmiany przypisań klawiszy. Aby wyłączyć ponowne mapowanie w grze, wystarczy określić, że opcja ponownego mapowania jest wyłączona dla InputMap (więcej informacji znajdziesz w artykule Tworzenie InputMap).

Aby uzyskać dostęp do tej funkcji, użytkownicy muszą otworzyć nakładkę, a następnie kliknąć działanie, które chcą zmienić. Po każdym zdarzeniu ponownego mapowania Gry Google Play na PC mapują każdy ponownie zmapowany przez użytkownika element sterujący na domyślne elementy sterujące, których oczekuje Twoja gra, więc nie musi ona wiedzieć o ponownym mapowaniu przez gracza. Opcjonalnie możesz zaktualizować komponenty używane do wyświetlania elementów sterujących klawiatury w grze, dodając wywołanie zwrotne do ponownego mapowania zdarzeń.

Spróbuj ponownie przypisać klawisz.

Gry Google Play na PC przechowują lokalnie zmienione mapowania elementów sterujących dla każdego użytkownika, dzięki czemu są one zachowywane podczas kolejnych sesji gry. Te informacje są przechowywane na dysku tylko na platformie PC i nie mają wpływu na korzystanie z urządzeń mobilnych. Dane sterujące są usuwane, gdy użytkownik odinstaluje lub ponownie zainstaluje Gry Google Play na PC. Te dane nie są trwałe na wielu komputerach.

Aby obsługiwać funkcję ponownego mapowania w swojej grze, unikaj tych ograniczeń:

Ograniczenia ponownego mapowania

Funkcje zmiany przypisania klawiszy można wyłączyć w grze, jeśli przypisania klawiszy obejmują którykolwiek z tych przypadków:

  • Skróty składające się z wielu klawiszyInputActions, które nie są kombinacją klawisza modyfikującego i klawisza niemodyfikującego. Na przykład Shift + A jest prawidłowe, ale A + B, Ctrl + Alt lub Shift + A + Tab nie.
  • InputMap zawiera InputActions, InputGroups lub InputContexts z powtórzonymi unikalnymi identyfikatorami.

Ograniczenia ponownego mapowania

Podczas projektowania przypisań klawiszy do ponownego mapowania weź pod uwagę te ograniczenia:

  • Zmiana mapowania na kombinacje klawiszy nie jest obsługiwana. Na przykład użytkownicy nie mogą zmienić przypisania Shift + A na Ctrl + B ani A na Shift + A.
  • Zmiana mapowania nie jest obsługiwana w przypadku InputActions z przyciskami myszy. Na przykład Shift + prawy przycisk myszy nie można ponownie przypisać.

Testowanie ponownego mapowania klawiszy w emulatorze Gier Google Play na PC

Funkcję ponownego mapowania możesz włączyć w dowolnym momencie w emulatorze Gier Google Play na PC, wydając to polecenie adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

Nakładka zmieni się tak jak na tym obrazie:

Nakładka z włączonym ponownym mapowaniem klawiszy.

Dodawanie pakietu SDK

Zainstaluj Input SDK zgodnie z platformą programowania.

Java i Kotlin

Pobierz Input SDK na Javę 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 Input SDK to standardowy pakiet Unity z kilkoma zależnościami.

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

Zainstaluj .unitypackage

Pobierz plik unitypackage pakietu Input SDK wraz ze wszystkimi zależnościami. .unitypackage możesz zainstalować, wybierając Assets > Import package > Custom Package i znajdując pobrany plik.

Instalowanie za pomocą UPM

Możesz też zainstalować pakiet za pomocą Menedżera pakietów Unity, pobierając .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 Input SDK znajdziesz w AGDK Tunnel w przypadku gier w Kotlinie lub Javie oraz w Trivial Kart w przypadku gier w Unity.

Generowanie powiązań klawiszy

Zarejestruj powiązania klawiszy, tworząc InputMap i zwracając go za pomocą InputMappingProvider. Poniższy 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

Określ działania wejściowe

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 ponowne mapowanie, możesz określić, co można ponownie mapować.InputActions Jeśli gra nie obsługuje mapowania, wyłącz opcję mapowania dla wszystkich InputActions. Pakiet Input SDK jest jednak wystarczająco inteligentny, aby wyłączyć mapowanie, jeśli nie jest ono obsługiwane w InputMap.

W tym przykładzie klawisz spacji jest przypisany do działania Drive.

Kotlin

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

Java

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

C#

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

Pojedynczy klawisz InputAction wyświetlany w nakładce.

Działania mogą też reprezentować dane wejściowe z myszy. W tym przykładzie kliknięcie lewym przyciskiem jest ustawione na działanie przenoszenia:

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

MouseInputAction wyświetlany w nakładce.

Kombinacje klawiszy są określane przez przekazanie wielu kodów klawiszy do elementu InputAction. W tym przykładzie spacja + shift jest przypisana do działania Turbo, które działa nawet wtedy, gdy spacja jest przypisana do jazdy.

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

W nakładce wyświetlany jest element InputAction z wieloma klawiszami.

Pakiet Input SDK umożliwia łączenie przycisków myszy i klawiatury w jedno działanie. Ten przykład pokazuje, że naciśnięcie jednocześnie klawiszy Shiftprawego przycisku myszy dodaje w tej przykładowej grze punkt pośredni:

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

Obiekt InputAction ma te pola:

  • ActionLabel: ciąg znaków wyświetlany w interfejsie, który reprezentuje to działanie. Lokalizacja nie jest wykonywana automatycznie, więc zrób to z wyprzedzeniem.
  • InputControls: określa elementy sterujące, których używa to działanie. Elementy sterujące są mapowane na spójne symbole w nakładce.
  • InputActionId: obiekt InputIdentifier, który przechowuje identyfikator numeru i wersję InputAction (więcej informacji znajdziesz w artykule Identyfikatory kluczy śledzenia).
  • InputRemappingOption: jedna z wartości InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Określa, czy działanie można ponownie przypisać. Jeśli gra nie obsługuje ponownego mapowania, możesz pominąć to pole lub po prostu ustawić je jako wyłączone.
  • RemappedInputControls: obiekt InputControls tylko do odczytu, który służy do odczytywania zestawu kluczy zmapowanego przez użytkownika podczas zdarzeń ponownego mapowania (używany 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 zdefiniowane w klasie KeyEvent lub w klasie AndroidKeycode w przypadku Unity.
  • MouseActions: to lista wartości MouseAction reprezentujących dane wejściowe myszy powiązane z tym działaniem.

Określ grupy wejściowe

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ć unikalny w ramach wszystkich InputGroups w Twojej grze.

Grupowanie działań wejściowych ułatwia graczowi znalezienie odpowiedniego powiązania klawiszy w bieżącym kontekście.

Jeśli obsługujesz ponowne mapowanie, możesz określić, co można ponownie mapować.InputGroups Jeśli gra nie obsługuje mapowania, wyłącz opcję mapowania dla wszystkich InputGroups. Pakiet Input SDK jest jednak wystarczająco inteligentny, aby wyłączyć mapowanie, jeśli nie jest ono obsługiwane 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 przedstawia grupy wejściowe Elementy sterujące drogąElementy sterujące menu w nakładce:

Nakładka wyświetlająca mapę InputMap zawierającą grupy wejściowe Road controls i Menu controls.

InputGroup zawiera te pola:

  • GroupLabel: ciąg znaków, który ma być wyświetlany w nakładce i 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ą wyświetlane wizualnie pod nagłówkiem grupy.
  • InputGroupId: obiekt InputIdentifier, który przechowuje identyfikator liczbowy i wersję InputGroup. Więcej informacji znajdziesz w artykule Identyfikatory kluczy śledzenia.
  • InputRemappingOption: jedna z wartości InputEnums.REMAP_OPTION_ENABLED lub InputEnums.REMAP_OPTION_DISABLED. Jeśli ta opcja jest wyłączona, wszystkie obiekty należące do tej grupy będą miały wyłączone ponowne mapowanie, nawet jeśli określono, że opcja ponownego mapowania jest włączona.InputAction Jeśli ta opcja jest włączona, wszystkie działania należące do tej grupy można ponownie przypisać, chyba że poszczególne działania mają wyłączoną tę możliwość.

Określanie kontekstów wejściowych

InputContexts umożliwia grze używanie różnych zestawów elementów sterujących klawiatury w różnych scenach. Na przykład:

  • Możesz określić różne zestawy 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 sposobu poruszania się w grze, np. jazdy samochodem lub chodzenia.
  • Możesz określić różne zestawy danych wejściowych w zależności od bieżącego stanu gry, np. podczas poruszania się po mapie świata lub grania na poszczególnych poziomach.

Gdy używasz InputContexts, nakładka wyświetla najpierw grupy kontekstu w użyciu. Aby włączyć to działanie, wywołaj funkcję setInputContext(), aby ustawić kontekst za każdym razem, gdy gra przechodzi do innej sceny. Ilustruje to poniższy obraz: w scenie „prowadzenie pojazdu” u góry nakładki wyświetlają się elementy sterujące drogą. Po otwarciu menu „Sklep” u góry nakładki wyświetlają się działania „Elementy sterujące menu”.

Konteksty wejściowe sortują grupy w nakładce.

Aktualizacje nakładki są realizowane przez ustawienie innego parametru InputContext w różnych momentach gry. Aby to zrobić:

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

InputGroups należące do tego samegoInputContextnie mogą mieć sprzecznychInputActions, w których używany jest ten sam klucz. Dobrze jest przypisać każdy InputGroup do jednego InputContext.

Poniższy przykładowy kod pokazuje 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 identyfikator numeru i wersję InputContext (więcej informacji znajdziesz w artykule 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 wejścia

InputMap to zbiór wszystkich obiektów InputGroup dostępnych w grze, a tym samym wszystkich obiektów InputAction, z którymi gracz może wejść w interakcję.

Podczas raportowania przypisań klawiszy tworzysz InputMap ze wszystkimi InputGroups używanymi w grze.

Jeśli gra nie obsługuje ponownego mapowania, wyłącz tę opcję i usuń zarezerwowane klawisze.

W tym przykładzie tworzymy InputMap, który służy do raportowania kolekcji 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 zgłoszone przez grę. Grupy są wyświetlane w kolejności w nakładce, chyba że określono bieżące grupy w użyciu, wywołując setInputContext().
  • MouseSettings: obiekt MouseSettings wskazuje, że czułość myszy można dostosować i że mysz jest odwrócona na osi Y.
  • InputMapId: InputIdentifier obiekt, który przechowuje identyfikator liczbowy i wersję 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 ponownego mapowania jest włączona.
  • ReservedControls: lista InputControls, których użytkownicy nie będą mogli ponownie przypisać.

Śledzenie identyfikatorów kluczy

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

W przykładzie poniżej przypisujemy wersję ciągu znaków 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

Identyfikatory obiektów InputAction muszą być unikalne we wszystkich InputActions w InputMap. Podobnie identyfikatory obiektów InputGroup muszą być unikalne we wszystkich InputGroups w InputMap. Poniższy przykład pokazuje, jak używać elementu enum do śledzenia unikalnych identyfikatorów 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 ustawiony w celu jednoznacznego zidentyfikowania danego zbioru danych wejściowych.
  • VersionString: ciąg znaków czytelny dla człowieka, który identyfikuje wersję danych wejściowych między 2 wersjami zmian danych wejściowych.

Otrzymywanie powiadomień o zdarzeniach ponownego mapowania (opcjonalnie)

Otrzymuj powiadomienia o zdarzeniach ponownego mapowania, aby być na bieżąco z klawiszami używanymi w Twojej 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.

Ilustracja poniżej pokazuje przykład takiego działania. Po ponownym przypisaniu klawiszy G, PS do klawiszy J, XT elementy interfejsu gry zostają zaktualizowane, aby wyświetlać klawisze ustawione przez użytkownika.

Interfejs reagujący na zdarzenia ponownego mapowania za pomocą wywołania zwrotnego InputRemappingListener.

Można to osiągnąć, rejestrując InputRemappingListenerwywołanie zwrotne. Aby zaimplementować 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 otrzymuje powiadomienie w momencie uruchomienia po wczytaniu zapisanych przez użytkownika zmienionych ustawień sterowania oraz za każdym razem, gdy użytkownik zmieni przypisanie klawiszy.

Inicjowanie

Jeśli używasz InputContexts, ustawiaj kontekst przy każdym przejściu do nowej sceny, w tym pierwszy kontekst używany w początkowej scenie. Musisz ustawić InputContext po zarejestrowaniu InputMap.

Jeśli używasz InputRemappingListeners, aby otrzymywać powiadomienia o zdarzeniach ponownego mapowania, zarejestruj InputRemappingListener przed zarejestrowaniem InputMappingProvider. W przeciwnym razie Twoja gra może przegapić ważne zdarzenia podczas uruchamiania.

Poniższy 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

Po zamknięciu gry wyrejestruj instancję InputMappingProvider i wszystkie instancje InputRemappingListener. Pakiet Input SDK jest jednak wystarczająco inteligentny, aby uniknąć wycieku zasobów, nawet 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

Możesz przetestować implementację pakietu Input SDK, ręcznie otwierając nakładkę, aby zobaczyć, jak wygląda odtwarzacz, lub za pomocą powłoki adb w celu automatycznego testowania i weryfikacji.

Emulator Gier Google Play na PC sprawdza poprawność mapy wejść pod kątem typowych błędów. W przypadku scenariuszy takich jak zduplikowane unikalne identyfikatory, używanie różnych map wejściowych lub niepowodzenie reguł ponownego mapowania (jeśli ponowne mapowanie jest włączone) nakładka wyświetla komunikat o błędzie, jak poniżej: Nakładka Input SDK.

Sprawdź wdrożenie pakietu Input SDK za pomocą polecenia adb w wierszu poleceń. Aby uzyskać bieżącą mapę wejść, 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 rejestracja InputMap zakończy się 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 nie korzysta z systemu lokalizacji Androida. W związku z tym podczas przesyłania InputMap musisz podać zlokalizowane ciągi znaków. Możesz też użyć systemu lokalizacji silnika gry.

Proguard

Jeśli do minifikacji gry używasz Proguarda, dodaj do pliku konfiguracji Proguarda te reguły, aby pakiet SDK nie został usunięty z pakietu końcowego:

-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 przejść do pozostałych wymagań dotyczących Gier Google Play na PC. Więcej informacji znajdziesz w artykule Pierwsze kroki z Grami Google Play na PC.