Começar a usar o SDK de entrada

Este documento descreve como configurar e mostrar o SDK de entrada em jogos com suporte ao Google Play Games no PC. As tarefas incluem adicionar o SDK ao jogo e gerar um mapa de entrada, que contém as atribuições de ação do jogo para entrada do usuário.

Antes de começar

Antes de adicionar o SDK de entrada ao jogo, é preciso oferecer suporte para entrada de teclado e mouse usando o sistema de entrada do mecanismo de jogo.

O SDK de entrada fornece informações ao Google Play Games no PC sobre os controles que o jogo usa, para que sejam mostrados ao usuário. Também é possível permitir o remapeamento do teclado para usuários.

Cada controle é uma InputAction (por exemplo, "J" para "Jump") e você organiza suas InputActions em InputGroups. Um InputGroup pode representar um modo diferente no jogo, como "Dirigir", "Caminhar" ou "Menu principal". Você também pode usar InputContexts para indicar os grupos que estão ativos em pontos diferentes do jogo.

É possível ativar o remapeamento do teclado automaticamente, mas se você preferir fornecer sua própria interface de remapeamento de controle, pode desativar o remapeamento do SDK de entrada.

O diagrama de sequência abaixo descreve como funciona a API do SDK de entrada:

Diagrama de sequência de uma implementação de jogo que chama a API de entrada do SDK
e a interação dela com o dispositivo
Android.

Quando o jogo implementa o SDK de entrada, seus controles são mostrados na sobreposição do Google Play Games no PC.

Sobreposição do Google Play Games no PC

A sobreposição do Google Play Games no PC ("a sobreposição") mostra os controles definidos pelo jogo. Os usuários podem acessar a sobreposição a qualquer momento pressionando Shift + Tab.

Sobreposição do Google Play Games no PC.

Práticas recomendadas para o design de vinculações de teclas

Ao desenvolver o design das vinculações de teclas, considere as seguintes práticas recomendadas:

  • Agrupe as InputActions relacionadas de maneira lógica em InputGroups para melhorar a navegação e a facilidade de descoberta dos controles na jogabilidade.
  • Atribua cada InputGroup a pelo menos um InputContext. Um InputMap bem granulado resulta em uma melhor experiência de navegação dos controles na sobreposição.
  • Crie um InputContext para cada tipo de caso diferente do jogo. Normalmente, você pode usar um único InputContext para todos os casos de menus. Use diferentes InputContexts para minijogos no seu app ou para controles alternativos para um único cenário.
  • Se duas ações usarem a mesma tecla no mesmo InputContext, use uma string de rótulo, como "Interagir / Atirar".
  • Se duas teclas estiverem vinculadas à mesma InputAction, use duas InputActions diferentes que executam a mesma ação no jogo. Você pode usar a mesma string de rótulo para as duas InputActions, mas o ID precisa ser diferente.
  • Se uma tecla modificadora for aplicada a um conjunto de teclas, considere ter uma única InputAction com a tecla modificadora, em vez de várias InputActions combinadas com a tecla modificadora. Por exemplo: use Shift e W, A, S, D em vez de Shift + W, Shift + A, Shift + S, Shift + D.
  • O remapeamento de entrada fica desativado automaticamente quando o usuário escreve nos campos de texto. Siga as práticas recomendadas para implementar os campos de texto do Android e garantir que ele possa detectá-los no seu jogo e evitar a interferência de teclas remapeadas. Caso seu jogo tenha que usar campos de texto não convencionais, você pode usar setInputContext() com um InputContext contendo uma lista vazia de InputGroups para desativar manualmente o remapeamento.
  • Se o jogo oferecer suporte ao remapeamento, considere que a atualização das vinculações de teclas é uma operação sensível que pode entrar em conflito com as versões salvas pelo usuário. Evite trocar IDs de controles existentes quando possível.

Recurso de remapeamento

O Google Play Games no PC oferece suporte ao remapeamento de controles de teclado com base nas vinculações que seu jogo oferece usando o SDK de entrada. Isso é opcional e pode ser totalmente desativado. Por exemplo, talvez você queira fornecer sua própria interface de remapeamento de teclado. Para desativar esse recurso no seu jogo, basta especificar a opção de remapeamento desativada para o InputMap. Consulte Criar um InputMap para mais informações.

Para acessar esse recurso, os usuários precisam abrir a sobreposição e clicar na ação que querem remapear. Depois de cada evento de remapeamento, o Google Play Games no PC remapeia cada controle mapeado pelo usuário para os controles padrão que seu jogo espera receber, de maneira que ele não precise estar ciente do remapeamento do jogador. Você tem a opção de atualizar os recursos usados para mostrar os controles de teclado no seu jogo adicionando um callback para eventos de remapeamento.

Tentativa de remapear o teclado

O Google Play Games no PC armazena controles remapeados localmente para cada usuário, para que eles persistam entre as sessões de jogo. Essas informações são armazenadas em disco apenas para a plataforma do PC e não afetam a experiência em dispositivos móveis. Os dados de controle são excluídos quando o usuário desinstala ou reinstala o Google Play Games no PC. Esses dados não são persistentes entre vários dispositivos de PC diferentes.

Para oferecer suporte ao recurso de remapeamento no jogo, evite as seguintes restrições:

Restrições de remapeamento

Os recursos de remapeamento podem ser desativados no jogo se as vinculações de teclas contiverem algum dos seguintes casos:

  • InputActions de várias teclas que não são compostas por uma tecla modificadora + uma tecla não modificadora. Por exemplo, a combinação Shift + A é válida, mas A + B, Ctrl + Alt ou Shift + A + Tab não são.
  • O InputMap contém InputActions, InputGroups ou InputContexts com IDs exclusivos repetidos.

Limitações de remapeamento

Ao desenvolver o design das vinculações de teclas para remapeamento, considere as seguintes limitações:

  • Não há suporte para o remapeamento de combinações de teclas. Por exemplo, os usuários não podem remapear Shift + A para Ctrl + B ou A para Shift + A.
  • O remapeamento não tem suporte para InputActions com botões do mouse. Por exemplo, Shift + clicar com o botão direito do mouse não pode ser remapeado.

Testar o remapeamento de teclas no emulador do Google Play Games no PC

Você pode ativar o recurso de remapeamento no emulador do Google Play Games no PC quando quiser com este comando adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

A sobreposição muda conforme a imagem abaixo:

A sobreposição com remapeamento de teclas ativado.

Adicionar o SDK

Instale o SDK de entrada de acordo com a plataforma de desenvolvimento.

Java e Kotlin

Faça o download do SDK de entrada para Java ou Kotlin adicionando uma dependência ao arquivo build.gradle de nível de módulo:

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

Unity

O SDK de entrada é um pacote padrão do Unity com várias dependências.

É necessário instalar o pacote com todas as dependências. Há várias maneiras de instalar os pacotes.

Instalar o .unitypackage

Faça o download do arquivo do SDK de entrada para Unity com todas as dependências. Você pode instalar o .unitypackage selecionando Assets > Import package > Custom Package e localizando o arquivo que você salvou.

Instalar usando UPM

Como alternativa, você pode instalar o pacote usando o Gerenciador de pacotes do Unity (link em inglês) fazendo o download de .tgz e instalando as dependências:

Instalar usando OpenUPM

Você pode instalar o pacote usando OpenUPM (link em inglês).

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

Exemplos de jogos

Para aprender a fazer a integração com o SDK de entrada, consulte o exemplo AGDK Tunnel para jogos Kotlin ou Java e o Trivial Kart para jogos Unity (links em inglês).

Gerar suas vinculações de teclas

Registre as vinculações de teclas criando um InputMap e retornando com um InputMappingProvider. O exemplo abaixo define um 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

Definir suas ações de entrada

A classe InputAction é usada para mapear uma tecla ou uma combinação de teclas para uma ação do jogo. InputActions precisam ter IDs exclusivos em todas as InputActions.

Se você oferece suporte a remapeamento, pode definir quais InputActions podem ser remapeadas. Se o jogo não oferecer suporte ao remapeamento, defina essa opção como desativada para todas as InputActions. Porém, o SDK de entrada é inteligente o suficiente para desativar o remapeamento se você não oferecer suporte no seu InputMap.

Este exemplo mapeia a tecla de espaço para a ação de dirigir.

Kotlin

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

Java

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

C#

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

InputAction com uma única tecla mostrada na sobreposição.

As ações também podem representar entradas de mouse. Este exemplo define o botão esquerdo do mouse para a ação de avançar:

Kotlin

companion object {
  private val mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal.toLong(),
    InputControls.create(emptyList(), listOf(InputControls.MOUSE_LEFT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

Java

private static final InputAction mouseInputAction = InputAction.create(
    "Move",
    InputActionsIds.MOUSE_MOVEMENT.ordinal(),
    InputControls.create(
            Collections.emptyList(),
            Collections.singletonList(InputControls.MOUSE_LEFT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

C#

private static readonly InputAction mouseInputAction = InputAction.Create(
    "Move",
    (long)InputEventIds.MOUSE_MOVEMENT,
    InputControls.Create(
        new ArrayList<Integer>(),
        new[] { new Integer((int)PlayMouseAction.MouseLeftClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

InputAction do mouse mostrada na sobreposição.

As combinações de teclas são especificadas transmitindo vários códigos de teclas para a InputAction. Neste exemplo, espaço + shift é mapeado para a ação Turbo, que funciona mesmo quando a tecla de espaço é mapeada para dirigir.

Kotlin

companion object {
  private val turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
      emptyList()),
    InputEnums.REMAP_OPTION_ENABLED)
}

Java

private static final InputAction turboInputAction = InputAction.create(
    "Turbo",
    InputActionsIds.TURBO.ordinal(),
    InputControls.create(
            Arrays.asList(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SPACE),
            Collections.emptyList()
    ),
    InputEnums.REMAP_OPTION_ENABLED
);

C#

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

InputAction com várias teclas mostrada na sobreposição.

O SDK de entrada permite combinar botões do mouse e do teclado para uma única ação. Este exemplo indica que shift e clique com o botão direito do mouse pressionados adicionam um waypoint neste jogo:

Kotlin

companion object {
  private val addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal.toLong(),
    InputControls.create(
      listOf(KeyEvent.KeyEvent.KEYCODE_TAB),
      listOf(InputControls.MOUSE_RIGHT_CLICK)),
    InputEnums.REMAP_OPTION_DISABLED)
}

Java

private static final InputAction addWaypointInputAction = InputAction.create(
    "Add waypoint",
    InputActionsIds.ADD_WAYPOINT.ordinal(),
    InputControls.create(
            Collections.singletonList(KeyEvent.KEYCODE_TAB),
            Collections.singletonList(InputControls.MOUSE_RIGHT_CLICK)
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

C#

private static readonly InputAction addWaypointInputAction = InputAction.Create(
    "Add waypoint",
    (long)InputEventIds.ADD_WAYPOINT,
    InputControls.Create(
        new[] { new Integer(AndroidKeyCode.KEYCODE_SPACE) }.ToJavaList(),
        new[] { new Integer((int)PlayMouseAction.MouseRightClick) }.ToJavaList()
    ),
    InputEnums.REMAP_OPTION_DISABLED
);

InputAction combinando tecla + mouse mostrada na sobreposição.

A InputAction tem os campos abaixo:

  • ActionLabel: a string mostrada na interface para representar essa ação. A localização não é automática, portanto, faça isso antecipadamente.
  • InputControls: define os controles de entrada que essa ação usa. Os controles mapeiam para glifos consistentes na sobreposição.
  • InputActionId: o objeto InputIdentifier que armazena o ID de número e versão da InputAction. Consulte Como rastrear IDs de tecla para mais informações.
  • InputRemappingOption: um de InputEnums.REMAP_OPTION_ENABLED ou InputEnums.REMAP_OPTION_DISABLED. Define se a ação para remapear está ativada. Se o jogo não oferecer suporte ao remapeamento, ignore esse campo ou simplesmente configure como desativado.
  • RemappedInputControls: objeto somente leitura InputControls usado para a leitura da tecla remapeada definida pelo usuário nos eventos de remapeamento. É usada para receber notificação de eventos de remapeamento.

InputControls representa as entradas associadas a uma ação e contém os campos abaixo:

  • AndroidKeycodes: é uma lista de números inteiros que representam entradas de teclado associadas a uma ação. Eles são definidos na classe KeyEvent ou AndroidKeycode do Unity.
  • MouseActions: é uma lista de valores de MouseAction que representam entradas de mouse associadas a essa ação.

Definir seus grupos de entrada

InputActions são agrupadas com ações relacionadas logicamente usando InputGroups para melhorar a navegação e a facilidade de descoberta de controles na sobreposição. Cada ID de InputGroup precisa ser exclusivo em todos os InputGroups do jogo.

Se você dividir as ações de entrada em grupos, o jogador vai poder encontrar as teclas de atalho corretas para o contexto atual com mais facilidade.

Se você oferecer suporte a remapeamento, poderá definir quais InputGroups podem ser remapeados. Se o jogo não oferecer suporte ao remapeamento, defina essa opção como desativada para todas as InputGroups. Porém, o SDK de entrada é inteligente o suficiente para desativar o remapeamento se você não oferecer suporte no seu 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
);

O exemplo abaixo mostra os grupos de entrada dos controles de estrada e dos controles de menu na sobreposição:

A sobreposição mostra um InputMap que contém os controles de estrada e os
grupos de entrada de controles de menu.

InputGroup tem os campos abaixo:

  • GroupLabel: é uma string que será mostrada na interface e pode ser usada para agrupar um conjunto de ações de maneira lógica. Essa string não é localizada automaticamente.
  • InputActions: uma lista de objetos InputAction que você define na etapa anterior. Todas essas ações são mostradas visualmente sob o título do grupo.
  • InputGroupId: o objeto InputIdentifier que armazena o ID de número e versão do InputGroup. Consulte Como rastrear IDs de tecla para mais informações.
  • InputRemappingOption: um de InputEnums.REMAP_OPTION_ENABLED ou InputEnums.REMAP_OPTION_DISABLED. Se desativada, todos os objetos InputAction pertencentes a esse grupo terão o remapeamento desativado, mesmo que eles tenham a essa opção ativada. Se ativada, todas as ações pertencentes a esse grupo poderão ser remapeadas, a menos que sejam especificadas como desativadas pelas ações.

Definir seus contextos de entrada

InputContexts permite que o jogo use um conjunto diferente de controles de teclado para diferentes cenários do jogo. Por exemplo:

  • Você pode especificar diferentes conjuntos de entradas para navegar em menus ou avançar no jogo.
  • Você pode especificar diferentes conjuntos de entradas, dependendo do modo de locomoção no jogo, como dirigir ou caminhar.
  • Você pode especificar diferentes conjuntos de entradas com base no estado atual do jogo, como navegar em um mapa do mundo ou percorrer um nível individual.

Ao usar InputContexts, a sobreposição mostra primeiro os grupos do contexto em uso. Para ativar esse comportamento, chame setInputContext() para definir o contexto sempre que o jogo entrar em um cenário diferente. A imagem abaixo demonstra esse comportamento: no cenário de "dirigir". As ações dos controles de estrada são mostradas na parte de cima da sobreposição. Ao abrir o menu "loja", as ações dos "controles de Menu" são mostradas na parte de cima da sobreposição.

InputContexts ordenando grupos na sobreposição.

Essas atualizações de sobreposição são feitas configurando um InputContext diferente em pontos diferentes do jogo. Siga estas etapas:

  1. Agrupe as InputActions com ações relacionadas de maneira lógica usando InputGroups.
  2. Atribua esses InputGroups a um InputContext para as diferentes partes do jogo.

InputGroups que pertencem ao mesmo InputContext não podem entrar em conflito com InputActions em que a mesma tecla é usada. Recomendamos atribuir cada InputGroup a um único InputContext.

O código do exemplo abaixo demonstra a lógica do 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 tem os campos abaixo:

  • LocalizedContextLabel: uma string descrevendo os grupos que pertencem ao contexto.
  • InputContextId: o objeto InputIdentifier que armazena o ID de número e versão da InputContext. Consulte Como rastrear IDs de tecla para mais informações.
  • ActiveGroups: uma lista de InputGroups a ser usada e mostrada na parte de cima da sobreposição quando esse contexto está ativo.

Criar um mapa de entrada

Um InputMap é uma coleção de todos os objetos InputGroup disponíveis em um jogo e, portanto, todos os objetos InputAction que um jogador pode esperar realizar.

Ao informar as vinculações de teclas, você cria um InputMap com todos os InputGroups usados no jogo.

Se o jogo não oferecer suporte ao remapeamento, defina essa opção como desativada e mantenha as teclas reservadas vazias.

O exemplo abaixo cria um InputMap usado para informar uma coleção de 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 tem os campos abaixo:

  • InputGroups: os InputGroups informados pelo jogo. Os grupos são mostrados em ordem na sobreposição, a menos que sejam especificados os grupos atuais em uso chamando setInputContext().
  • MouseSettings: os objetos MouseSettings indicam que a sensibilidade do mouse pode ser ajustada e que o mouse é invertido no eixo y.
  • InputMapId: o objeto InputIdentifier que armazena o ID de número e versão da InputMap. Consulte Como rastrear IDs de tecla para mais informações.
  • InputRemappingOption: um de InputEnums.REMAP_OPTION_ENABLED ou InputEnums.REMAP_OPTION_DISABLED. Define se o recurso de remapeamento está ativado.
  • ReservedControls: uma lista de InputControls que os usuários não vão ter permissão para remapear.

Rastrear IDs de tecla

Os objetos InputAction, InputGroup, InputContext e InputMap contêm um objeto InputIdentifier que armazena um ID de número exclusivo e um ID de versão da string. Rastrear a versão de string dos seus objetos é opcional, mas recomendamos rastrear as versões dos objetos InputMap. Se a versão da string não for fornecida, ela vai estar vazia. É necessária uma versão da string para objetos InputMap.

O exemplo abaixo atribui uma versão da string para InputActions ou 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

Os IDs dos número de objetos InputAction precisam ser exclusivos em todas as InputActions no seu InputMap. Da mesma forma, IDs de objetos InputGroup precisam ser exclusivos para todos os InputGroups em um InputMap. O exemplo abaixo demonstra como usar uma enum para rastrear os IDs exclusivos do objeto:

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 tem os campos abaixo:

  • UniqueId: um ID de número exclusivo definido para identificar claramente um determinado conjunto de dados de entrada de maneira única.
  • VersionString: uma string de versão legível por humanos definida para identificar uma versão dos dados de entrada entre duas versões de mudanças nos dados de entrada.

Receber notificações sobre eventos de remapeamento (opcional)

Receba notificações sobre eventos de remapeamento para saber sobre as teclas que estão sendo usadas no seu jogo. Isso permite que o jogo atualize os recursos mostrados na tela do jogo usada para exibir os controles de ação.

A imagem abaixo mostra um exemplo desse comportamento em que, após remapear as teclas G, P e S para J, X e T respectivamente, os elementos de interface do jogo são atualizados para mostrar as teclas definidas pelo usuário.

A interface do usuário reagindo a eventos de remapeamento usando o callback InputRemappingListener.

Essa funcionalidade precisa do registro de um callback InputRemappingListener. Para implementar esse recurso, comece registrando uma instância 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

O InputRemappingListener é notificado no momento da inicialização após o carregamento dos controles remapeados salvos pelo usuário e sempre que o usuário remapeia as teclas.

Inicialização

Se você estiver usando InputContexts, defina o contexto em cada transição para um novo cenário, incluindo o primeiro contexto usado para o cenário inicial. É preciso definir o InputContext depois de registrar o InputMap.

Se você estiver usando InputRemappingListeners para receber notificações sobre eventos de remapeamento, registre o InputRemappingListener antes de registrar o InputMappingProvider. Caso contrário o jogo pode perder eventos importantes durante a inicialização.

O exemplo abaixo demonstra como inicializar a 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
    }
}

Limpeza

Cancele a inscrição da instância InputMappingProvider e de qualquer instância InputRemappingListener quando o jogo for fechado, embora o SDK de entrada seja inteligente o suficiente para evitar vazamento de recursos:

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

Testar

É possível testar a implementação do SDK de entrada abrindo manualmente a sobreposição para visualizar a experiência do jogador, ou pelo shell do adb para testes e verificação automatizada.

O emulador do Google Play Games no PC verifica a exatidão do mapa de entrada em relação a erros comuns. Para cenários como IDs exclusivos duplicados, uso de diferentes mapas de entrada ou falha nas regras de remapeamento (se estiver ativado), a sobreposição mostra uma mensagem de erro, conforme abaixo: Sobreposição do SDK de entrada.

Verifique a implementação do SDK de entrada usando adb na linha de comando. Para conferir o mapa de entrada atual, use o comando adb shell abaixo (substitua MY.PACKAGE.NAME pelo nome do jogo):

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

Você vai encontrar uma saída semelhante a esta se tiver registrado o InputMap:

Getting input map for com.example.inputsample...
Successfully received the following inputmap:
# com.google.android.libraries.play.games.InputMap@d73526e1
input_groups {
  group_label: "Basic Movement"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
    }
    unique_id: 0
  }
  input_actions {
    action_label: "Left"
    input_controls {
      keycodes: 29
      keycodes: 21
    }
    unique_id: 1
  }
  input_actions {
    action_label: "Right"
    input_controls {
      keycodes: 32
      keycodes: 22
    }
    unique_id: 2
  }
  input_actions {
    action_label: "Use"
    input_controls {
      keycodes: 33
      keycodes: 66
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 3
  }
}
input_groups {
  group_label: "Special Input"
  input_actions {
    action_label: "Jump"
    input_controls {
      keycodes: 51
      keycodes: 19
      keycodes: 62
      mouse_actions: MOUSE_LEFT_CLICK
      mouse_actions_value: 0
    }
    unique_id: 4
  }
  input_actions {
    action_label: "Duck"
    input_controls {
      keycodes: 47
      keycodes: 20
      keycodes: 113
      mouse_actions: MOUSE_RIGHT_CLICK
      mouse_actions_value: 1
    }
    unique_id: 5
  }
}
mouse_settings {
  allow_mouse_sensitivity_adjustment: true
  invert_mouse_movement: true
}

Localização

O SDK de entrada não usa o sistema de localização do Android. Como resultado, é necessário fornecer strings localizadas ao enviar um InputMap. Isso também permite usar o sistema de localização do mecanismo do jogo.

ProGuard

Ao usar o ProGuard para reduzir o jogo, adicione as regras abaixo ao arquivo de configuração para garantir que o SDK não seja removido do pacote final:

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

A seguir

Depois de integrar o SDK de entrada ao jogo, é possível continuar com todos os requisitos restantes do Google Play Games no PC. Para mais informações, consulte Começar a usar o Google Play Games no PC.