Questo documento descrive come configurare e visualizzare l'SDK Input nei giochi che supportano Google Play Games su PC. Le attività includono l'aggiunta dell'SDK al tuo gioco e la generazione di una mappa di input, che contiene le assegnazioni delle azioni di gioco all'input dell'utente.
Prima di iniziare
Prima di aggiungere l'SDK Input al gioco, devi supportare l'input da tastiera e mouse utilizzando il sistema di input del motore di gioco.
L'SDK di input fornisce a Google Play Giochi su PC informazioni sui controlli utilizzati dal tuo gioco, in modo che possano essere visualizzati dall'utente. Può anche consentire facoltativamente la rimappatura della tastiera per gli utenti.
Ogni controllo è un InputAction (ad es. "J" per "Salto") e organizzi i tuoi
InputActions in InputGroups. Un InputGroup potrebbe rappresentare una modalità diversa nel gioco, ad esempio "Guida", "A piedi" o "Menu principale". Puoi anche
utilizzare InputContexts per indicare quali gruppi sono attivi in diversi momenti
della partita.
Puoi attivare la rimappatura della tastiera per la gestione automatica, ma se preferisci fornire la tua interfaccia di rimappatura dei controlli, puoi disattivare la rimappatura dell'SDK Input.
Il seguente diagramma di sequenza descrive il funzionamento dell'API dell'SDK Input:

Quando il tuo gioco implementa l'SDK Input, i controlli vengono visualizzati nell'overlay di Google Play Giochi su PC.
La sovrapposizione di Google Play Games su PC
La sovrapposizione di Google Play Games su PC ("la sovrapposizione") mostra i controlli definiti dal tuo gioco. Gli utenti possono accedere alla sovrapposizione in qualsiasi momento premendo Maiusc + Tab.

Best practice per la progettazione delle assegnazioni di tasti
Quando progetti le assegnazioni dei tasti, tieni presente le seguenti best practice:
- Raggruppa i tuoi
InputActionsinInputGroupscorrelati in modo logico per migliorare la navigazione e la visibilità dei controlli durante il gameplay. - Assegna ogni
InputGroupa un massimo di unInputContext. Una granularitàInputMappiù precisa si traduce in un'esperienza migliore per la navigazione dei controlli nella sovrapposizione. - Crea un
InputContextper ogni tipo di scena diverso del tuo gioco. In genere, puoi utilizzare un unicoInputContextper tutte le scene "simili a un menu". UtilizzaInputContextsdiversi per i minigiochi del tuo gioco o per controlli alternativi per una singola scena. - Se due azioni sono progettate per utilizzare la stessa chiave nello stesso
InputContext, utilizza la stringa dell'etichetta, ad esempio "Interagisci / Spara". - Se due tasti sono progettati per essere associati alla stessa
InputAction, utilizza dueInputActionsdiversi che eseguono la stessa azione nel gioco. Puoi utilizzare la stessa stringa di etichetta per entrambi gli attributiInputActions, ma il relativo ID deve essere diverso. - Se un tasto modificatore viene applicato a un insieme di tasti, valuta la possibilità di utilizzare un singolo
InputActioncon il tasto modificatore anziché piùInputActionsche combinano il tasto modificatore (ad esempio, utilizza Maiusc e W, A, S, D anziché Maiusc + W, Maiusc + A, Maiusc + S, Maiusc + D). - La rimappatura dell'input viene disattivata automaticamente quando l'utente scrive nei campi di testo. Segui le best practice per l'implementazione dei campi di testo Android per assicurarti
che Android possa rilevare i campi di testo nel tuo gioco e impedire che i tasti rimappati
interferiscano con loro. Se il tuo gioco deve utilizzare campi di testo non convenzionali, puoi utilizzare
setInputContext()con unInputContextcontenente un elenco vuoto diInputGroupsper disattivare la rimappatura manualmente. - Se il tuo gioco supporta la rimappatura, valuta la possibilità di aggiornare le assegnazioni dei tasti, un'operazione sensibile che può entrare in conflitto con le versioni salvate dall'utente. Evita di modificare gli ID dei controlli esistenti, se possibile.
Funzionalità di rimappatura
Google Play Giochi su PC supporta la rimappatura dei controlli della tastiera in base alle
associazioni di tasti fornite dal gioco utilizzando l'SDK Input. Questa funzionalità è facoltativa e
può essere disattivata completamente. Ad esempio, potresti voler fornire la tua interfaccia di rimappatura della tastiera. Per disattivare la rimappatura per il tuo gioco, devi solo specificare
l'opzione di rimappatura disattivata per il tuo InputMap (vedi
Creare un InputMap per maggiori informazioni).
Per accedere a questa funzionalità, gli utenti devono aprire la sovrapposizione e fare clic sull'azione che vogliono rimappare. Dopo ogni evento di rimappatura, Google Play Games su PC mappa ogni controllo rimappato dall'utente ai controlli predefiniti che il tuo gioco si aspetta di ricevere, quindi il tuo gioco non deve essere a conoscenza della rimappatura del giocatore. Se vuoi, puoi aggiornare gli asset utilizzati per visualizzare i controlli da tastiera nel tuo gioco aggiungendo un callback per rimappare gli eventi.

Google Play Giochi su PC memorizza i controlli rimappati localmente per ogni utente, consentendo la persistenza dei controlli nelle sessioni di gioco. Queste informazioni vengono memorizzate su disco solo per la piattaforma PC e non influiscono sull'esperienza mobile. I dati di controllo vengono eliminati quando l'utente disinstalla o reinstalla Google Play Giochi su PC. Questi dati non sono persistenti su più dispositivi PC.
Per supportare la funzionalità di rimappatura nel tuo gioco, evita le seguenti limitazioni:
Limitazioni della rimappatura
Le funzionalità di rimappatura possono essere disattivate nel gioco se le assegnazioni dei tasti contengono uno dei seguenti casi:
InputActionscon più tasti che non sono composti da un tasto di modifica + un tasto non di modifica. Ad esempio, Maiusc+A è valido, ma A+B, Ctrl+Alt o Maiusc+A+Tab non lo sono.InputMapcontieneInputActions,InputGroupsoInputContextscon ID univoci ripetuti.
Limitazioni della rimappatura
Quando progetti le assegnazioni dei tasti per la rimappatura, considera le seguenti limitazioni:
- La rimappatura alle combinazioni di tasti non è supportata. Ad esempio, gli utenti non possono rimappare Maiusc + A su Ctrl + B o A su Maiusc + A.
- La rimappatura non è supportata per
InputActionscon i pulsanti del mouse. Ad esempio, Maiusc + clic destro non può essere rimappato.
Testare la rimappatura dei tasti nell'emulatore di Google Play Giochi su PC
Puoi attivare la funzionalità di rimappatura in Google Play Games su PC Emulator in qualsiasi momento emettendo il seguente comando adb:
adb shell dumpsys input_mapping_service --set RemappingFlagValue true
La modifica della sovrapposizione come nell'immagine seguente:

Aggiungi l'SDK
Installa l'SDK Input in base alla tua piattaforma di sviluppo.
Java e Kotlin
Per ottenere l'SDK Input per Java o Kotlin, aggiungi una dipendenza al file build.gradle a livello di modulo:
dependencies {
implementation 'com.google.android.libraries.play.games:inputmapping:1.1.1-beta'
...
}
Unity
L'SDK Input è un pacchetto Unity standard con diverse dipendenze.
È necessario installare il pacchetto con tutte le dipendenze. Esistono diversi modi per installare i pacchetti.
Installa .unitypackage
Scarica il file unitypackage dell'SDK Input
con tutte le relative dipendenze. Puoi installare .unitypackage selezionando
Asset > Importa pacchetto > Pacchetto personalizzato e individuando il file che hai scaricato.
Installazione tramite UPM
In alternativa, puoi installare il pacchetto utilizzando
Unity Package Manager scaricando
.tgz e installando le relative dipendenze:
- com.google.external-dependency-manager-1.2.172
- com.google.librarywrapper.java-0.2.0
- com.google.librarywrapper.openjdk8-0.2.0
- com.google.android.libraries.play.games.inputmapping-1.1.1-beta (o selezionando il file tgz da questo archivio)
Installazione tramite OpenUPM
Puoi installare il pacchetto utilizzando OpenUPM.
$ openupm add com.google.android.libraries.play.games.inputmapping
Giochi di esempio
Per esempi di integrazione con l'SDK Input, vedi AGDK Tunnel per i giochi Kotlin o Java e Trivial Kart per i giochi Unity.
Generare le associazioni di tasti
Registra le associazioni di tasti creando un InputMap e restituendolo con un
InputMappingProvider. L'esempio seguente descrive 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
Definisci le azioni di input
La classe InputAction viene utilizzata per mappare un tasto o una combinazione di tasti a un'azione di gioco. InputActions devono avere ID univoci in tutti i InputActions.
Se supporti la rimappatura, puoi definire cosa può essere rimappato.InputActions Se il tuo gioco non supporta la rimappatura, devi disattivare l'opzione di rimappatura
per tutti i tuoi InputActions, ma l'SDK Input è
abbastanza intelligente da disattivare la rimappatura se non la supporti nel tuo
InputMap.
Questo esempio mappa il tasto
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 );

Le azioni possono rappresentare anche gli input del mouse. Questo esempio imposta Clic sinistro sull'azione Sposta:
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 );

Le combinazioni di tasti vengono specificate passando più codici tasto al tuo
InputAction. In questo esempio,
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 );

L'SDK Input consente di combinare i pulsanti del mouse e della tastiera per
una singola azione. Questo esempio indica che
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 ha i seguenti campi:
ActionLabel: la stringa visualizzata nell'interfaccia utente per rappresentare questa azione. La localizzazione non viene eseguita automaticamente, quindi esegui qualsiasi localizzazione in anticipo.InputControls: definisce i controlli di input utilizzati da questa azione. I controlli corrispondono a glifi coerenti nell'overlay.InputActionId: oggettoInputIdentifierche memorizza l'ID numerico e la versione diInputAction(vedi ID chiave di monitoraggio per ulteriori informazioni).InputRemappingOption: uno traInputEnums.REMAP_OPTION_ENABLEDoInputEnums.REMAP_OPTION_DISABLED. Definisce se l'azione è abilitata al remapping. Se il gioco non supporta la rimappatura, puoi saltare questo campo o impostarlo su disattivato.RemappedInputControls: oggettoInputControlsdi sola lettura utilizzato per leggere il set di tasti rimappato dall'utente negli eventi di rimappatura (utilizzato per ricevere notifiche sugli eventi di rimappatura).
InputControls rappresenta gli input associati a un'azione e contiene i seguenti campi:
AndroidKeycodes: è un elenco di numeri interi che rappresentano gli input da tastiera associati a un'azione. Questi sono definiti nella classe KeyEvent o nella classe AndroidKeycode per Unity.MouseActions: è un elenco di valoriMouseActionche rappresentano gli input del mouse associati a questa azione.
Definisci i gruppi di input
InputActions sono raggruppati con azioni correlate logicamente utilizzando InputGroups per
migliorare la navigazione e la visibilità dei controlli nell'overlay. Ogni ID InputGroup deve essere univoco in tutti i InputGroups del gioco.
Organizzando le azioni di input in gruppi, un giocatore può trovare più facilmente l'assegnazione dei tasti corretta per il contesto corrente.
Se supporti la rimappatura, puoi definire cosa può essere rimappato.InputGroups Se il tuo gioco non supporta la rimappatura, devi disattivare l'opzione di rimappatura
per tutti i tuoi InputGroups, ma l'SDK Input è
abbastanza intelligente da disattivare la rimappatura se non la supporti nel tuo
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 );
L'esempio seguente mostra i gruppi di input Controlli stradali e Controlli menu nella sovrapposizione:

InputGroup contiene i seguenti campi:
GroupLabel: una stringa da visualizzare nell'overlay che può essere utilizzata per raggruppare logicamente un insieme di azioni. Questa stringa non viene localizzata automaticamente.InputActions: un elenco di oggettiInputActiondefiniti nel passaggio precedente. Tutte queste azioni vengono visualizzate visivamente sotto l'intestazione del gruppo.InputGroupId: oggettoInputIdentifierche memorizza l'ID numero e la versione diInputGroup. Per saperne di più, consulta ID chiave di monitoraggio.InputRemappingOption: uno traInputEnums.REMAP_OPTION_ENABLEDoInputEnums.REMAP_OPTION_DISABLED. Se disattivata, il remapping di tutti gli oggettiInputActionappartenenti a questo gruppo verrà disattivato anche se specificano che l'opzione di remapping è attivata. Se abilitate, tutte le azioni appartenenti a questo gruppo possono essere rimappate, a meno che non siano specificate come disabilitate dalle singole azioni.
Definisci i contesti di input
InputContexts consente al gioco di utilizzare un diverso set di controlli da tastiera per
diverse scene del gioco. Ad esempio:
- Puoi specificare diversi set di input per navigare nei menu rispetto a quelli per muoverti nel gioco.
- Puoi specificare diversi set di input a seconda della modalità di movimento nel gioco, ad esempio guida e camminata.
- Puoi specificare diversi set di input in base allo stato attuale del gioco, ad esempio navigare in un mondo esterno rispetto a giocare a un livello individuale.
Quando utilizzi InputContexts, l'overlay mostra prima i gruppi del contesto
in uso. Per attivare questo comportamento, chiama setInputContext() per impostare il
contesto ogni volta che il gioco entra in una scena diversa. La seguente immagine
mostra questo comportamento: nella scena "Guida", le azioni dei Controlli stradali
vengono visualizzate nella parte superiore della sovrapposizione. Quando apri il menu "Negozio", le azioni
"Controlli del menu" vengono visualizzate nella parte superiore della sovrapposizione.

Questi aggiornamenti della sovrapposizione vengono ottenuti impostando un InputContext diverso in
diversi punti del gioco. Per farlo:
- Raggruppa i tuoi
InputActionscon azioni correlate in modo logico utilizzandoInputGroups - Assegna questi
InputGroupsa unInputContextper le diverse parti del tuo gioco
InputGroups che appartengono allo stessoInputContextnon possono avere InputActions in conflitto in cui viene utilizzata la stessa chiave. È consigliabile assegnare ogni
InputGroup a un singolo InputContext.
Il seguente codice campione mostra la logica di 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 contiene i seguenti campi:
LocalizedContextLabel: una stringa che descrive i gruppi che appartengono al contesto.InputContextId: oggettoInputIdentifierche memorizza l'ID numerico e la versione diInputContext(vedi ID chiave di monitoraggio per ulteriori informazioni).ActiveGroups: un elenco diInputGroupsda utilizzare e visualizzare nella parte superiore dell'overlay quando questo contesto è attivo.
Creare una mappa di input
Un InputMap è una raccolta di tutti gli oggetti InputGroup disponibili in un gioco e, pertanto, di tutti gli oggetti InputAction che un giocatore può aspettarsi di eseguire.
Quando segnali le assegnazioni dei tasti, crei un InputMap con tutti i
InputGroups utilizzati nel gioco.
Se il gioco non supporta la rimappatura, imposta l'opzione di rimappatura su disattivata e lascia vuote le chiavi riservate.
L'esempio seguente crea un InputMap utilizzato per segnalare una raccolta di
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 contiene i seguenti campi:
InputGroups: gli InputGroups segnalati dal tuo gioco. I gruppi vengono visualizzati in ordine nella sovrapposizione, a meno che non siano specificati i gruppi correnti in uso che chiamanosetInputContext().MouseSettings: l'oggettoMouseSettingsindica che la sensibilità del mouse può essere regolata e che il mouse è invertito sull'asse Y.InputMapId: oggettoInputIdentifierche memorizza l'ID numero e la versione diInputMap(vedi ID chiave di monitoraggio per ulteriori informazioni).InputRemappingOption: uno traInputEnums.REMAP_OPTION_ENABLEDoInputEnums.REMAP_OPTION_DISABLED. Definisce se la funzionalità di rimappatura è abilitata.ReservedControls: un elenco diInputControlsa cui gli utenti non potranno rimappare.
Monitorare gli ID chiave
Gli oggetti InputAction, InputGroup, InputContext e InputMap contengono un oggetto InputIdentifier che memorizza un ID numerico univoco e un ID versione stringa.
Il monitoraggio della versione stringa degli oggetti è facoltativo, ma consigliato per monitorare
le versioni di InputMap. Se la versione della stringa non viene fornita, la
stringa è vuota. Per gli oggetti InputMap è necessaria una versione stringa.
L'esempio seguente assegna una versione stringa 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
Gli ID numero degli oggetti InputAction devono essere univoci in tutti gli InputActions del tuo InputMap. Analogamente, gli ID oggetto InputGroup devono essere univoci in tutti gli InputGroups di un InputMap. Il seguente esempio mostra come utilizzare un
enum per monitorare gli ID univoci dell'oggetto:
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 contiene i seguenti campi:
UniqueId: un ID numerico univoco impostato per identificare chiaramente un determinato insieme di dati di input in modo univoco.VersionString: una stringa di versione leggibile impostata per identificare una versione dei dati di input tra due versioni di modifiche ai dati di input.
Ricevere notifiche sugli eventi di rimappatura (facoltativo)
Ricevi notifiche sugli eventi di rimappatura per essere informato dei tasti utilizzati nel tuo gioco. In questo modo, il gioco può aggiornare gli asset mostrati nella schermata di gioco utilizzati per visualizzare i controlli delle azioni.
L'immagine seguente mostra un esempio di questo comportamento in cui, dopo aver rimappato i tasti

Questa funzionalità viene ottenuta registrando un callback InputRemappingListener. Per implementare questa funzionalità, inizia registrando un'istanza di
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
Il InputRemappingListener viene avvisato al momento dell'avvio dopo il caricamento dei
controlli rimappati salvati dall'utente e ogni volta che l'utente rimappa i tasti.
Inizializzazione
Se utilizzi InputContexts, imposta il contesto per ogni
transizione a una nuova scena, incluso il primo contesto utilizzato per la scena
iniziale. Devi impostare InputContext dopo aver registrato il tuo
InputMap.
Se utilizzi InputRemappingListeners per ricevere notifiche sugli eventi di rimappatura,
registra InputRemappingListener prima di registrare
InputMappingProvider, altrimenti il tuo gioco potrebbe perdere eventi importanti durante
il lancio.
Il seguente esempio mostra come inizializzare l'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 } }
Pulizia
Annulla la registrazione dell'istanza InputMappingProvider e di qualsiasi istanza InputRemappingListener
quando il gioco è chiuso, anche se l'SDK Input è abbastanza intelligente
da evitare perdite di risorse se non lo fai:
Kotlin
override fun onDestroy() { if (isGooglePlayGamesOnPC()) { val inputMappingClient = Input.getInputMappingClient(this) inputMappingClient.clearInputMappingProvider() inputMappingClient.clearRemappingListener() } super.onDestroy() }
Java
@Override protected void onDestroy() { if (isGooglePlayGamesOnPC()) { InputMappingClient inputMappingClient = Input.getInputMappingClient(this); inputMappingClient.clearInputMappingProvider(); inputMappingClient.clearRemappingListener(); } super.onDestroy(); }
C#
public class GameManager : MonoBehaviour { private void OnDestroy() { #if PLAY_GAMES_PC _inputMappingClient.ClearInputMappingProvider(); _inputMappingClient.ClearRemappingListener(); #endif } }
Test
Puoi testare l'implementazione dell'SDK Input aprendo manualmente la sovrapposizione per visualizzare l'esperienza del player o tramite la shell adb per test e verifiche automatizzati.
L'emulatore di Google Play Giochi su PC verifica la correttezza della mappatura degli input
rispetto agli errori comuni. Per scenari come ID unici duplicati, utilizzo di mappe di input diverse o errori nelle regole di rimappatura (se la rimappatura è abilitata), l'overlay mostra un messaggio di errore come di seguito:

Verifica l'implementazione dell'SDK Input utilizzando adb nella riga di comando.
Per ottenere la mappatura di input corrente, utilizza il seguente comando adb shell (sostituisci
MY.PACKAGE.NAME con il nome del tuo gioco):
adb shell dumpsys input_mapping_service --get MY.PACKAGE.NAME
Se hai registrato correttamente il tuo
InputMap, vedrai un output simile a questo:
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
}
Localizzazione
L'SDK Input non utilizza il sistema di localizzazione di Android. Di conseguenza, devi fornire stringhe localizzate quando invii un InputMap. Puoi
anche utilizzare il sistema di localizzazione del tuo motore grafico.
Proguard
Quando utilizzi Proguard per ridurre le dimensioni del gioco, aggiungi le seguenti regole al file di configurazione Proguard per assicurarti che l'SDK non venga rimosso dal pacchetto finale:
-keep class com.google.android.libraries.play.hpe.** { *; }
-keep class com.google.android.libraries.play.games.inputmapping.** { *; }
Passaggi successivi
Dopo aver integrato l'SDK Input nel tuo gioco, puoi continuare con gli altri requisiti di Google Play Giochi su PC. Per saperne di più, consulta la pagina Inizia a utilizzare Google Play Giochi su PC.