Cómo comenzar a utilizar el SDK de entrada

En este documento, se describe cómo configurar y mostrar el SDK de entrada en juegos compatibles con Google Play Juegos para PC. Las tareas incluyen agregar el SDK a tu juego y generar un mapa de entrada, que contiene las asignaciones de acciones del juego a entradas del usuario.

Antes de comenzar

Antes de incorporar el SDK de entrada al juego, debes agregar compatibilidad con las entradas de teclado y mouse usando el sistema de entrada de tu motor de juego.

El SDK de entrada brinda información a Google Play Juegos para PC sobre los controles que usa tu juego, de modo que el usuario pueda verlos. También puede, de manera opcional, permitir que los usuarios reasignen teclas del teclado.

Cada control es una InputAction (p. ej., "C" para "Correr"), y tú organizas tus InputActions en InputGroups. Un InputGroup podría representar un modo diferente en el juego, como "Conducir" o "Caminar", o "Menú principal". También puedes usar InputContexts para indicar qué grupos están activos en distintos puntos del juego.

Es posible configurar que la reasignación de teclas se administre automáticamente, pero, si prefieres emplear tu propia interfaz de reasignación de controles, puedes inhabilitar la reasignación del SDK de entrada.

En el siguiente diagrama de secuencias, se describe cómo funciona la API del SDK de entrada:

Diagrama de secuencias de la implementación de un juego que llama a la API del SDK de entrada y su interacción con el dispositivo Android

Cuando tu juego implemente el SDK de entrada, los controles se mostrarán en la superposición de Google Play Juegos para PC.

La superposición de Google Play Juegos para PC

La superposición de Google Play Juegos para PC ("la superposición") muestra los controles que definió tu juego. Los usuarios pueden acceder a ella en cualquier momento con Mayúsculas + Tab.

La superposición de Google Play Juegos para PC

Prácticas recomendadas para diseñar vinculaciones de teclas

Cuando diseñes vinculaciones de teclas, ten en cuenta las siguientes prácticas recomendadas:

  • Agrupa tus InputActions en InputGroups relacionados de forma lógica para mejorar la navegación y la capacidad de detectar los controles durante la partida del juego.
  • Asigna cada InputGroup a, al menos, un InputContext. Un InputMap correcto y detallado genera una mejor experiencia para navegar por tus controles en la superposición.
  • Crea un InputContext para cada tipo de escena distinto de tu juego. Por lo general, puedes usar un solo InputContext para todas las escenas que son similares al menú. Usa InputContexts diferentes para los minijuegos que haya o para los controles alternativos de una escena única.
  • Si hay dos acciones diseñadas para usar la misma tecla bajo el mismo InputContext, usa la cadena de etiqueta; por ejemplo, "Interactuar/disparar".
  • Si hay dos teclas diseñadas para vincularse a la misma InputAction, usa 2 InputActions diferentes que realicen la misma acción en el juego. Puedes utilizar la misma cadena de etiqueta para ambas InputActions, pero su ID debe ser distinto.
  • Si se aplica una tecla modificadora a un conjunto de teclas, considera usar únicamente una sola InputAction con la tecla modificadora, en lugar de varias InputActions que combinen esa tecla (por ejemplo, usa Mayúsculas y W, A, S y D en lugar de Mayúsculas + W, Mayúsculas + A, Mayúsculas + S y Mayúsculas + D).
  • La reasignación de entradas se inhabilita automáticamente cuando el usuario escribe en campos de texto. Sigue las prácticas recomendadas para implementar campos de texto de Android para asegurarte de que Android pueda detectarlos en tu juego y evitar que las teclas reasignadas interfieran con ellos. Si tu juego debe usar campos de texto no convencionales, puedes utilizar setInputContext() con un InputContext que contenga una lista vacía de InputGroups para inhabilitar manualmente la reasignación.
  • Si tu juego admite la reasignación, ten en cuenta que actualizar las vinculaciones de teclas puede ser una operación delicada que podría generar conflictos con las versiones que guardó el usuario. Cuando sea posible, evita cambiar los IDs de los controles existentes.

La función de reasignación

Google Play Juegos para PC admite la reasignación de controles del teclado en función de las vinculaciones de teclas que proporciona tu juego usando el SDK de entrada. Esta función es opcional y se puede inhabilitar por completo. Por ejemplo, tal vez quieras emplear tu propia interfaz de reasignación de teclas. Para inhabilitar la reasignación en tu juego, solo debes especificar que esa opción está inhabilitada para tu InputMap (consulta Cómo crear un mapa de entradas para obtener más información).

Para acceder a esta función, los usuarios deben abrir la superposición y hacer clic en la acción que desean reasignar. Después de cada evento de reasignación, Google Play Juegos para PC asigna cada control modificado por el usuario a los controles predeterminados que espera recibir tu juego, de modo que este no necesite estar al tanto de la reasignación que configuró el usuario. De manera opcional, puedes actualizar los recursos que se usan para mostrar los controles del teclado en el juego agregando una devolución de llamada para los eventos de reasignación.

Intento de reasignar la tecla

Google Play Juegos para PC almacena los controles reasignados de manera local para cada usuario, lo que permite la persistencia de controles en todas las sesiones de juego. Esa información se guarda en el disco solo en el caso de la plataforma de PC y no influye en la experiencia en dispositivos móviles. Los datos de controles se borran si el usuario desinstala o reinstala Google Play Juegos para PC. Esos datos no persisten en diferentes dispositivos de PC.

Para admitir la función de reasignación en tu juego, evita las siguientes restricciones:

Restricciones de la reasignación

Las funciones de reasignación se pueden inhabilitar en tu juego si las vinculaciones de teclas entran en alguno de estos casos:

  • InputActions de varias teclas que no constan de una tecla modificadora + una tecla no modificadora. Por ejemplo, Mayúsculas + A es válida, pero A + B, Ctrl + Alt o Mayúsculas + A + Tab no lo son.
  • InputMap contiene InputActions, InputGroups o InputContexts con IDs únicos repetidos.

Limitaciones de la reasignación

Cuando diseñes las vinculaciones de teclas para la reasignación, ten en cuenta las siguientes limitaciones:

  • No se admite la reasignación a combinaciones de teclas. Por ejemplo, los usuarios no pueden reasignar Mayúsculas + A a Ctrl + B o A a Mayúsculas + A.
  • No se admite la reasignación para InputActions con botones del mouse. Por ejemplo, no se puede reasignar Mayúsculas + clic derecho.

Cómo probar la reasignación de teclas en el emulador de Google Play Juegos para PC

Puedes habilitar la función de reasignación en el emulador de Google Play Juegos para PC en cualquier momento emitiendo el siguiente comando de adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

La superposición cambia como en la siguiente imagen:

Superposición con reasignación de teclas habilitada

Cómo agregar el SDK

Instala el SDK de entrada según tu plataforma de desarrollo.

Java y Kotlin

Para obtener el SDK de entrada para Java o Kotlin, agrega una dependencia al archivo build.gradle del nivel de módulo:

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

Unity

El SDK de entrada es un paquete estándar de Unity con varias dependencias.

Es obligatorio instalar el paquete con todas las dependencias. Hay distintas formas de instalar los paquetes.

Cómo instalar el .unitypackage

Descarga el archivo unitypackage del SDK de entrada con todas sus dependencias. Para instalar el .unitypackage, selecciona Assets > Import package > Custom Package y busca el archivo que descargaste.

Cómo instalar el paquete con UPM

Como alternativa, puedes instalar el paquete con el administrador de paquetes de Unity. Para ello, descarga el .tgz e instala sus dependencias:

Cómo instalar el paquete con OpenUPM

Puedes usar OpenUPM para instalar el paquete.

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

Juegos de muestra

Si quieres ver ejemplos de cómo hacer la integración con el SDK de entrada, consulta AGDK Tunnel para juegos de Kotlin o Java, y Trivial Kart para juegos de Unity.

Cómo generar tus vinculaciones de teclas

Registra tus vinculaciones de teclas creando un InputMap y mostrándolo con un InputMappingProvider. En el siguiente ejemplo, se muestra a grandes rasgos un 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

Cómo definir tus acciones de entrada

La clase InputAction se usa para asignar una tecla o una combinación de teclas a una acción del juego. InputActions debe tener IDs únicos en todas las InputActions.

Si admites la reasignación, puedes definir qué InputActions se pueden reasignar. Si tu juego no admite la reasignación, deberías configurarla como inhabilitada para todas las InputActions, aunque el SDK de entrada es tan inteligente que la desactiva si no la admites en tu InputMap.

En este ejemplo, se asigna la tecla barra espaciadora a la acción Conducir.

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 de una sola tecla en la superposición

Las acciones también pueden representar entradas del mouse. En este ejemplo, se configura el clic izquierdo para la acción Mover.

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 del mouse en la superposición

Las combinaciones de teclas se especifican pasando varios códigos de tecla a InputAction. En este ejemplo, se asigna barra espaciadora + Mayúsculas a la acción Turbo, que funciona incluso cuando la barra espaciadora está asignada a Conducir.

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 de varias teclas en la superposición

El SDK de entrada te permite combinar los botones del mouse y las teclas para una sola acción. En este ejemplo, se indica que Mayúsculas y clic derecho presionados juntos agregan un punto de referencia en este juego de muestra:

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

Combinación de InputAction de tecla + mouse en la superposición

InputAction tiene los siguientes campos:

  • ActionLabel: Es la cadena que se muestra en la IU para representar esta acción. Dado que la localización no se realiza automáticamente, debes hacerla por adelantado.
  • InputControls: Define los controles de entrada que usa esta acción. Los controles se asignan a glifos coherentes en la superposición.
  • InputActionId: Es el objeto InputIdentifier que almacena el ID de número y la versión de la InputAction (consulta Cómo hacer un seguimiento de IDs de teclas para obtener más información).
  • InputRemappingOption: Es InputEnums.REMAP_OPTION_ENABLED o InputEnums.REMAP_OPTION_DISABLED. Define si es posible reasignar la acción. Si tu juego no admite la reasignación, puedes omitir este campo o configurar la opción como inhabilitada.
  • RemappedInputControls: Es el objeto InputControls de solo lectura que se usa para leer la tecla reasignada que configuró el usuario en eventos de reasignación (se usa para recibir notificaciones sobre eventos de reasignación).

InputControls representa las entradas asociadas con una acción y contiene los siguientes campos:

  • AndroidKeycodes: Es una lista de números enteros que representan las entradas del teclado que se asocian con una acción. Se definen en la clase KeyEvent o en la clase AndroidKeycode en Unity.
  • MouseActions es una lista de valores MouseAction que representan entradas del mouse que se asocian con esta acción.

Cómo definir tus grupos de entradas

Las InputActions se agrupan con acciones relacionadas de forma lógica con InputGroups para mejorar la navegación y la capacidad de detectar los controles en la superposición. Cada ID de InputGroup debe ser único en todos los InputGroups en tu juego.

Si organizas tus acciones de entrada en grupos, permites que un jugador encuentre la vinculación correcta de teclas para su contexto actual con mayor facilidad.

Si admites la reasignación, puedes definir qué InputGroups se pueden reasignar. Si tu juego no admite la reasignación, deberías configurarla como inhabilitada para todos los InputGroups, aunque el SDK de entrada es tan inteligente que la desactiva si no la admites en tu 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
);

En el siguiente ejemplo, se muestran los grupos de entradas de los controles de carretera y los controles de menú en la superposición.

Superposición que muestra un InputMap que contiene los grupos de entradas de los controles de carretera y los controles de menú

InputGroup tiene los siguientes campos:

  • GroupLabel: Es una cadena que se muestra en la superposición y que se puede usar para agrupar un conjunto de acciones de forma lógica. Esta cadena no se localiza automáticamente.
  • InputActions: Es una lista de objetos InputAction que defines en el paso anterior. Todas estas acciones se muestran visualmente bajo el encabezado del grupo.
  • InputGroupId: Es un objeto InputIdentifier que almacena el ID de número y la versión del InputGroup. Consulta Cómo hacer un seguimiento de IDs de teclas para obtener más información.
  • InputRemappingOption: Es InputEnums.REMAP_OPTION_ENABLED o InputEnums.REMAP_OPTION_DISABLED. Si se desactiva, se inhabilitará la reasignación en todos los objetos InputAction que pertenezcan a este grupo, incluso si especifican que tienen habilitada esa opción. Si se habilita, todas las acciones que pertenezcan a este grupo se podrán reasignar, a menos que la acción individual especifique que se inhabilitó.

Cómo definir tus contextos de entrada

InputContexts permite que tu juego use un conjunto distinto de controles de teclado para diferentes escenas. Por ejemplo:

  • Puedes especificar conjuntos de entradas para navegar por menús distintos de los que se usan para el movimiento en el juego.
  • Puedes especificar diferentes conjuntos de entradas según el modo de locomoción en el juego (por ejemplo, conducir frente a caminar).
  • Puedes especificar diferentes conjuntos de entradas según el estado actual del juego (por ejemplo, explorar un inframundo frente a recorrer un nivel particular).

Cuando se utilizan InputContexts, la superposición muestra primero los grupos del contexto en uso. Si quieres habilitar este comportamiento, llama a setInputContext() para configurar el contexto cada vez que el juego entre a una escena distinta. En la siguiente imagen, se demuestra este comportamiento: en la escena de conducción, las acciones de los Road controls (Controles de carretera) se muestran en la parte superior de la superposición. Cuando se abre el menú "Store" (Tienda), las acciones de los "Menu controls" (Controles de menú) se muestran en la parte superior de la superposición.

Orden de grupos de InputContexts en la superposición

Para lograr estas actualizaciones de la superposición, se configura un InputContext diferente en distintos puntos del juego. Sigue estos pasos:

  1. Agrupa tus InputActions con acciones relacionadas de forma lógica usando InputGroups.
  2. Asigna estos InputGroups a un InputContext para diferentes partes del juego.

Los InputGroups que pertenecen al mismo InputContext no pueden tener InputActions en conflicto cuando se usa la misma tecla. Una práctica recomendada es asignar cada InputGroup a un solo InputContext.

En el siguiente código de muestra, se demuestra la lógica de 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 tiene los siguientes campos:

  • LocalizedContextLabel: Es una cadena que describe los grupos que pertenecen al contexto.
  • InputContextId: Es el objeto InputIdentifier que almacena el ID de número y la versión del InputContext (consulta Cómo hacer un seguimiento de IDs de teclas para obtener más información).
  • ActiveGroups: Es una lista de InputGroups que se usarán y se mostrarán en la parte superior de la superposición cuando este contexto esté activo.

Cómo crear un mapa de entradas

Un InputMap es una colección de todos los objetos InputGroup disponibles en un juego y, por lo tanto, todos los objetos InputAction que un jugador puede esperar realizar.

Cuando informas tus vinculaciones de teclas, creas un InputMap con todos los InputGroups que se usan en el juego.

Si el juego no admite la reasignación, configúrala como inhabilitada y deja las teclas reservadas vacías.

En el siguiente ejemplo, se crea un InputMap que se usa para informar una colección 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 tiene los siguientes campos:

  • InputGroups: Son los InputGroups que informa tu juego. Los grupos se muestran en orden en la superposición, a menos que se especifique en los grupos actuales en uso llamando a setInputContext().
  • MouseSettings: El objeto MouseSettings indica que la sensibilidad del mouse se puede ajustar y que el mouse se invierte en el eje y.
  • InputMapId: Es el objeto InputIdentifier que almacena el ID de número y la versión del InputMap (consulta Cómo hacer un seguimiento de IDs de teclas para obtener más información).
  • InputRemappingOption: Es InputEnums.REMAP_OPTION_ENABLED o InputEnums.REMAP_OPTION_DISABLED. Define si la función de reasignación está habilitada.
  • ReservedControls: Es una lista de InputControls que los usuarios no podrán reasignar.

Cómo hacer un seguimiento de IDs de teclas

Los objetos InputAction, InputGroup, InputContext y InputMap contienen un objeto InputIdentifier que almacena un ID de número único y un ID de versión de cadena. Hacer un seguimiento de la versión de cadena de tu objeto es opcional, pero se recomienda hacer un seguimiento de las versiones de tu InputMap. Si no se proporciona una versión de cadena, esta estará vacía. Se requiere una versión de cadena para los objetos InputMap.

En el siguiente ejemplo, se asigna una versión de cadena a InputActions o 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

Los IDs de número de los objetos InputAction deben ser únicos en todas las InputActions de tu InputMap. De manera similar, los IDs de objeto de InputGroup deben ser únicos en todos los InputGroups de un InputMap. En el siguiente ejemplo, se demuestra cómo usar una enum para hacer un seguimiento de los IDs únicos de tu 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 tiene los siguientes campos:

  • UniqueId: Es un ID de número único establecido para identificar claramente un conjunto determinado de datos de entrada de manera individual.
  • VersionString: Es una cadena de versión legible por humanos establecida para identificar una versión de datos de entrada entre 2 versiones de cambios en los datos.

Cómo recibir notificaciones sobre eventos de reasignación (opcional)

Recibe notificaciones sobre eventos de reasignación para estar al tanto de las teclas que se usan en tu juego. Esto permite que tu juego actualice los recursos que se muestran en la pantalla del juego y se usan para mostrar los controles de acciones.

En la siguiente imagen, se muestra un ejemplo de este comportamiento: después de que se reasignan las teclas G, P y S a J, X y T, respectivamente, los elementos de la IU del juego se actualizan para mostrar las teclas que configuró el usuario.

Reacción de la IU ante los eventos de reasignación con la devolución de llamada InputRemappingListener

Para lograr esta funcionalidad, se registra una devolución de llamada InputRemappingListener. Para implementar esta función, comienza por registrar una instancia de 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 recibe una notificación al momento de inicio después de cargar los controles reasignados que guardó el usuario y después de cada vez que este reasigna las teclas.

Inicialización

Si usas InputContexts, establece el contexto en cada transición en una nueva escena, incluido el primer contexto que se utilizó para la escena inicial. Debes establecer el InputContext después de haber registrado tu InputMap.

Si usas InputRemappingListeners para recibir notificaciones sobre eventos de reasignación, registra tu InputRemappingListener antes de registrar el InputMappingProvider. De lo contrario, es posible que el juego se pierda eventos importantes durante el tiempo de inicio.

En el siguiente ejemplo, se demuestra cómo inicializar la 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
    }
}

Limpieza

Anula el registro de la instancia de InputMappingProvider y las instancias de InputRemappingListener cuando se cierre el juego, aunque el SDK de entrada es tan inteligente que evitará filtrar recursos si no lo haces.

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

Cómo realizar pruebas

Puedes probar la implementación del SDK de entrada abriendo manualmente la superposición para ver la experiencia del jugador o con el shell adb para pruebas y verificación automatizadas.

El emulador de Google Play Juegos para PC verifica si hay errores comunes en tu mapa de entradas para determinar si es correcto. En casos como IDs únicos duplicados, uso de mapas de entradas diferentes o incumplimiento de las reglas de reasignación (si está habilitada), la superposición muestra un mensaje de error como el siguiente: La superposición del SDK de entrada

Usa adb en la línea de comandos para verificar la implementación del SDK de entrada. Para obtener el mapa de entradas actual, usa el siguiente comando de adb shell (reemplaza MY.PACKAGE.NAME por el nombre de tu juego):

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

Si registraste correctamente tu InputMap, verás un resultado como el siguiente:

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
}

Localización

El SDK de entrada no usa el sistema de localización de Android. Como resultado, debes proporcionar cadenas localizadas cuando envíes un InputMap. También puedes usar el sistema de localización de tu motor de juego.

ProGuard

Cuando uses ProGuard para reducir tu juego, agrega las siguientes reglas al archivo de configuración de ProGuard para asegurarte de que no se quite el SDK del paquete final:

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

Próximos pasos

Después de integrar el SDK de entrada en el juego, puedes continuar con los requisitos restantes de Google Play Juegos para PC. Para obtener más información, consulta Cómo comenzar a usar Google Play Juegos para PC.