Input SDK のスタートガイド

このドキュメントでは、PC 版 Google Play Games をサポートするゲームで Input SDK をセットアップして表示する方法について説明します。このタスクには、ゲームへの SDK の追加と、ゲーム アクションからユーザー入力への割り当てを含む入力マップの生成が含まれます。

開始する前に

Input SDK をゲームに追加する前に、お使いのゲームエンジンの入力システムを使用してキーボードとマウスの入力をサポートする必要があります。

Input SDK からは PC 版 Google Play Games に対して、ゲームでどのコントロールが使用されるかに関する情報が提供されるため、それらのコントロールをユーザーに表示できます。また、ユーザーによるキーボードの割り当て変更も許可できます。

各コントールが InputAction(「Jump」の「J」など)となり、InputActions をまとめたものが InputGroups となります。InputGroup はゲーム内のモード(「運転中」「歩行中」「メインメニュー」など)に相当します。また、InputContexts を使用して、ゲーム内のポイントごとにアクティブなグループを示すこともできます。

キーボードの割り当て変更が自動で処理されるようにすることも可能ですが、コントロールの割り当て変更のための独自のインターフェースを提供する場合は、Input SDK の割り当て変更を無効にできます。

次のシーケンス図は、Input SDK の API の動作を説明するものです。

Input SDK API を呼び出すゲーム実装と、それがどのように Android デバイスとやり取りするかを表すシーケンス図。

Input SDK をゲームに実装すると、PC 版 Google Play Games のオーバーレイにコントロールが表示されます。

PC 版 Google Play Games のオーバーレイ

PC 版 Google Play Games のオーバーレイ(以降、「オーバーレイ」)には、ゲームで定義したコントロールが表示されます。このオーバーレイは、Shift+Tab キーを押すことにより、いつでも表示できます。

PC 版 Google Play Games のオーバーレイ

キー バインディングを設計するにあたってのベスト プラクティス

キー バインディングを設計するにあたっては、次のベスト プラクティスを考慮してください。

  • ゲームプレイ中のナビゲーションを向上させ、コントロールを見つけやすくするために、InputActions を論理的に関連する InputGroups にまとめます。
  • InputGroup を少なくとも 1 つの InputContext に割り当てます。InputMap を細かくすることで、オーバーレイのコントロールのナビゲーションが使いやすくなります。
  • ゲームのシーンの種類ごとに InputContext を作成します。通常は、すべての「メニュー的な」シーンに 1 つの InputContext を使用できます。各ミニゲームや、1 つのシーンの代替コントロールには、別々の InputContexts を使用します。
  • 2 つのアクションに同じ InputContext で同じキーを使用する場合は「操作 / 発射」などのラベル文字列を利用します。
  • 2 つのキーを同じ InputAction にバインドする場合、ゲーム内で同じアクションを実行する 2 つの異なる InputActions を使用します。両方の InputActions に同じラベル文字列を利用しても構いませんが、ID は違っている必要があります。
  • キーのセットに修飾キーを適用する場合、修飾キー付きの複数の InputActions ではなく、修飾キー付きの単独の InputAction とすることを検討してください(例: Shift+W、Shift+A、Shift+S、Shift+D ではなく、Shift+W、A、S、D)。
  • テキスト フィールドに入力する際は、入力の割り当て変更が自動的に無効になります。Android のテキスト フィールドの実装に関するベスト プラクティスのとおり、ゲームのテキスト フィールドが検出されるようにし、割り当て変更されるキーによる干渉が発生しないようにしてください。標準的でないテキスト フィールドを使用する必要がある場合は、InputGroups の空のリストを収めた InputContext を指定して setInputContext() を使用すると、割り当て変更を手動で無効にできます。
  • 割り当て変更をサポートしているゲームの場合、キー バインディングの変更は、ユーザーが保存したバージョンと競合する可能性がある繊細な操作だと考えてください。可能であれば、既存のコントロールの ID は変更しないでください。

割り当て変更機能

PC 版 Google Play Games では、Input SDK の使用によりゲームから提供されるキー バインディングに基づいて、キーボード コントロールの割り当てを変更できます。この機能はオプションであり、完全に無効にできます。たとえば、独自のキーボード割り当て変更インターフェースを提供できます。InputMap の割り当て変更オプションを無効に指定するだけで、割り当て変更を無効にできます(詳しくは、InputMap の作成をご覧ください)。

ユーザーがこの機能を利用するには、オーバーレイを開いてから、割り当て変更したいアクションをクリックする必要があります。割り当て変更イベントが発生するごとに、PC 版 Google Play Games が、ユーザーが割り当て変更を行ったコントロールのそれぞれを、ゲームが受け取るデフォルトのコントロールに割り当てるため、プレーヤーによる割り当て変更をゲーム側で認識する必要はありません。また、割り当て変更イベントに対応するコールバックを追加することにより、キーボード コントロールの表示に使用されるアセットを更新できます。

キーの割り当てを変更しようとしているところ

PC 版 Google Play Games では、割り当てを変更されたコントロールをユーザーごとにローカルで保存し、ゲーム セッション間でコントロールが変わらないようにしています。この情報は、PC プラットフォームの場合にのみディスクに保存され、モバイルでの使用には影響ありません。PC 版 Google Play Games のアンインストールまたは再インストールを行うと、コントロール データが削除されます。このデータが複数の PC デバイスにまたがって存続することはありません。

ゲームの割り当て変更機能をサポートする場合、次の制限事項を避ける必要があります。

割り当て変更の制限事項

キー バインディングに以下のケースが含まれている場合、ゲームでは割り当て変更機能が無効になる可能性があります。

  • 修飾キーと非修飾キーの組み合わせではない、複数キーの InputActions である場合。たとえば、Shift+A は有効ですが、A+BCtrl+AltShift+A+Tab は無効です。
  • InputMap に、一意の ID を重複使用している InputActionsInputGroups、または InputContexts が含まれている場合。

割り当て変更の限界

割り当て変更のキー バインディングを設計するにあたっては、次のような限界があることを考慮してください。

  • 割り当ての変更先をキーの組み合わせにすることはできません。たとえば、Shift+A から Ctrl+B や、A から Shift+A への割り当て変更はできません。
  • マウスボタンを伴う InputActions の割り当て変更はサポートされていません。たとえば、Shift+右クリックの割り当ては変更できません。

PC 版 Google Play Games エミュレータでキーの割り当て変更をテストする

PC 版 Google Play Games エミュレータでは、次の adb コマンドを発行することで、いつでも割り当て変更機能を有効にできます。

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

オーバーレイは、次の画像のように変わります。

キーの割り当て変更が有効になっているオーバーレイ

SDK を追加する

開発プラットフォームに応じて Input SDK をインストールします。

Java と Kotlin

Java または Kotlin 用の Input SDK を取得するには、依存関係をモジュール レベルの build.gradle ファイルに追加します。

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

Unity

Input SDK は、標準の Unity パッケージであり、いくつかの依存関係があります。

パッケージと必要な依存関係をインストールします。インストール方法は、いくつかあります。

.unitypackage をインストールする

すべての依存関係と Input SDK の .unitypackage ファイルをダウンロードします。[Assets] > [Import package] > [Custom Package] を選択し、ダウンロードしたファイルを見つけて、.unitypackage をインストールします。

UPM を使用してインストールする

別の方法として、.tgz をダウンロードし、以下の依存関係をインストールすることで、Unity のパッケージ管理システムを使用してパッケージをインストールすることもできます。

OpenUPM を使用してインストールする

OpenUPM を使用してパッケージをインストールすることもできます。

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

サンプルゲーム

Input SDK と統合する方法の例については、Kotlin または Java ゲーム向けの AGDK Tunnel と Unity ゲーム向けの Trivial Kart をご覧ください。

キー バインディングを生成する

InputMap を作成し、それを InputMappingProvider で返すことにより、キー バインディングを登録します。以下は、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

入力アクションを定義する

InputAction クラスは、キーまたはキーの組み合わせをゲーム アクションに割り当てるために使用します。InputActions の ID は、すべての InputActions で一意である必要があります。

割り当て変更をサポートしている場合、どの InputActions の割り当てを変更できるかを定義できます。割り当て変更をサポートしないゲームでは、すべての InputActions で割り当て変更オプションを無効に設定する必要がありますが、InputMap でサポートしていない場合には Input SDK が自動的に無効にします。

この例では、Space キーが Drive アクションに割り当てられています。

Kotlin

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

Java

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

C#

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

オーバーレイに 1 つのキー InputAction が表示されている

アクションはマウス入力で表すこともできます。この例では、左クリックMove アクションに設定しています。

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 が表示されている

キーの組み合わせは、複数のキーコードを InputAction に渡すと指定できます。この例では、Space+Shift キーが Turbo アクションに割り当てられています。これは、Space キーが Drive に割り当てられている場合でも機能します。

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 が表示されている

Input SDK では、1 つのアクションを実行するためにマウスボタンとキーボタンを混在させることができます。この例は、Shift キーを押しながら右クリックすると、このゲームでウェイポイントが追加されることを示しています。

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 が表示されている

InputAction には以下のフィールドがあります。

  • ActionLabel: このアクションを表し、UI に表示される文字列です。自動ではローカライズされないため、前もってローカライズしてください。
  • InputControls: このアクションで使用される入力コントロールを定義します。オーバーレイでは、これらのコントロールが一貫したグリフに割り当てられます。
  • InputActionId: InputAction の数値 ID とバージョンを保存する InputIdentifier オブジェクトです(詳しくは、キー ID のトラッキングをご覧ください)。
  • InputRemappingOption: InputEnums.REMAP_OPTION_ENABLEDInputEnums.REMAP_OPTION_DISABLED のいずれかです。アクションの割り当て変更が有効になっているかどうかを定義します。割り当て変更をサポートしない場合は、このフィールドをスキップするか、無効に設定します。
  • RemappedInputControls: 割り当て変更イベントでユーザーが設定した割り当て変更対象のキーの読み取りに使用される、読み取り専用の InputControls オブジェクトです(割り当て変更イベントの通知を受け取るために使用されます)。

InputControls は、アクションに関連付けられた入力を表します。以下のフィールドを含みます。

  • AndroidKeycodes: アクションに関連付けられたキーボード入力を表す整数のリストです。これらの整数は、KeyEvent クラス、または Unity の AndroidKeycode クラスで定義されています。
  • MouseActions: このアクションに関連付けられたマウス入力を表す MouseAction 値のリストです。

入力グループを定義する

InputActions は、オーバーレイでのナビゲーションを向上させ、コントロールを発見しやすくするために、InputGroups を使用して、論理的に関連するアクション別にグループ化されます。各 InputGroup ID は、ゲーム内のすべての InputGroups で一意である必要があります。

入力アクションをグループにまとめることで、現在の状況に適したキー バインディングをプレーヤーが容易に見つけられるようになります。

割り当て変更をサポートしている場合、どの InputGroups の割り当てを変更できるかを定義できます。割り当て変更をサポートしないゲームでは、すべての InputGroups で割り当て変更オプションを無効に設定する必要がありますが、InputMap でサポートしていない場合には Input SDK が自動的に無効にします。

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

以下の例では、オーバーレイに Road controls 入力グループと Menu controls 入力グループが表示されています。

オーバーレイに Road controls 入力グループと Menu controls 入力グループを含む InputMap が表示されている。

InputGroup には次のフィールドがあります。

  • GroupLabel: オーバーレイに表示される文字列で、一連のアクションを論理的にグループ化するために使用できます。この文字列は自動ではローカライズされません。
  • InputActions: 前のステップで定義した InputAction オブジェクトのリストです。これらのアクションは、すべてグループ見出しの下に表示されます。
  • InputGroupId: InputGroup の数値 ID とバージョンを保存する InputIdentifier オブジェクトです。詳しくは、キー ID をトラッキングするをご覧ください。
  • InputRemappingOption: InputEnums.REMAP_OPTION_ENABLEDInputEnums.REMAP_OPTION_DISABLED のいずれかです。無効にすると、割り当て変更オプションを有効に指定していても、このグループに属するすべての InputAction オブジェクトの割り当て変更は無効になります。有効にすると、このグループに属するアクションは、個々のアクションで無効と指定しない限り、割り当て変更が可能になります。

入力コンテキストを定義する

InputContexts により、ゲームのシーンごとに異なるキーボード コントロールのセットを使用することが可能になります。次に例を示します。

  • メニューの操作とゲーム内の移動に対して、それぞれ異なる入力セットを指定できます。
  • 運転や歩行など、ゲーム内の移動方法に応じて異なる入力セットを指定できます。
  • ゲーム全体での移動か、個々のレベルでのプレイかなど、ゲームの状況に応じて異なる入力セットを指定できます。

InputContexts を使用している場合、オーバーレイには、まず使用中のコンテキストのグループが表示されます。この動作を有効にするには、ゲームのシーンが変わるときに setInputContext() を呼び出してコンテキストを設定します。次の画像は、この動作(「driving」シーンで、オーバーレイの上部に Road controls のアクションが表示される)を示しています。「store」メニューを開くと、オーバーレイの上部に「Menu controls」のアクションが表示されます。

オーバーレイで InputContexts によりグループが並べ替えられています。

ゲームのポイントごとに異なる InputContext を設定することで、このようなオーバーレイの更新を実現できます。そのためには、次のような処理を行います。

  1. InputGroups を使用して、InputActions を論理的に関連するアクション別にグループ化します。
  2. これらの InputGroups をゲームの異なる部分ごとの InputContext に割り当てます。

同じ InputContext に属する InputGroups で、同じキーが使用され、競合している InputActions を持つことはできません。各 InputGroup を 1 つの InputContext に割り当てることをおすすめします。

以下は、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 には次のフィールドがあります。

  • LocalizedContextLabel: コンテキストに属するグループを説明する文字列です。
  • InputContextId: InputContext の数値 ID とバージョンを保存する InputIdentifier オブジェクトです(詳しくは、キー ID のトラッキングをご覧ください)。
  • ActiveGroups: このコンテキストがアクティブになったときに使用され、オーバーレイの上部に表示される InputGroups のリストです。

入力マップを作成する

InputMap は、ゲームで利用できるすべての InputGroup オブジェクト(つまり、実行されることをユーザーが期待しているすべての InputAction オブジェクト)のコレクションです。

キー バインディングをレポートする場合には、ゲーム内で使用されるすべての InputGroups が入った InputMap を作成します。

割り当て変更をサポートしていない場合、割り当て変更オプションを無効に設定し、予約されているキーを空に設定します。

次の例では、InputGroups のコレクションをレポートするために使用される InputMap を作成しています。

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 には次のフィールドがあります。

  • InputGroups: ゲームからレポートされる InputGroups です。グループは、setInputContext() を呼び出して現在のグループを指定しない限り、オーバーレイに順番に表示されます。
  • MouseSettings: MouseSettings オブジェクトは、マウスの感度を調整できることと、マウスを Y 軸上で反転することを示しています。
  • InputMapId: InputMap の数値 ID とバージョンを保存する InputIdentifier オブジェクトです(詳しくは、キー ID のトラッキングをご覧ください)。
  • InputRemappingOption: InputEnums.REMAP_OPTION_ENABLEDInputEnums.REMAP_OPTION_DISABLED のいずれかです。割り当て変更機能が有効かどうかを定義します。
  • ReservedControls: ユーザーが割り当て変更先にすることを禁止する InputControls のリストです。

キー ID をトラッキングする

InputActionInputGroupInputContextInputMap の各オブジェクトには、一意の数値 ID と文字列バージョン ID を収めた InputIdentifier オブジェクトが含まれます。オブジェクトの文字列バージョンをトラッキングすることは任意ですが、InputMap のバージョンのトラッキングを行う場合はおすすめします。文字列バージョンが指定されていない場合、この文字列は空になります。InputMap オブジェクトには文字列バージョンが必要です。

次の例では、InputActions または 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

InputAction オブジェクトの数値 ID は、InputMap 内のすべての InputActions で一意である必要があります。同様に、InputGroup オブジェクトの ID は、InputMap 内のすべての InputGroups で一意である必要があります。次のサンプルでは、enum を使用してオブジェクトの一意の ID のトラッキングを行う方法を示しています。

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 には次のフィールドがあります。

  • UniqueId: 入力データの特定のセットを一意かつ明確に識別するために設定された一意の数値 ID です。
  • VersionString: 2 つのバージョンの入力データの変化の間で入力データのバージョンを識別するために設定された、人間が読める形のバージョン文字列です。

割り当て変更イベントの通知を受ける(任意)

ゲーム内でキーが使用されていることを知るための割り当て変更イベントの通知を受け取ります。これにより、アクション コントロールの表示に使用され、ゲーム画面に表示されるアセットを更新することが可能になります。

次の画像は、この動作の例です。GPS キーをそれぞれ JXT キーに割り当て変更すると、UI 要素が更新されてユーザーが設定したキーが表示されるようになります。

UI が InputRemappingListener コールバックを使用して割り当て変更イベントに反応している。

この機能は、InputRemappingListener コールバックを登録することで実現します。この機能を実装するために、まず 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 に通知が届きます。

初期化

InputContexts セットを使用している場合、初期シーンに使用される最初のコンテキストを含め、新しいシーンへの遷移のそれぞれにコンテキストを設定します。InputContextInputMap を登録した後で設定する必要があります。

割り当て変更イベントの通知を受け取るために InputRemappingListeners を使用している場合、InputMappingProvider を登録する前に InputRemappingListener を登録します。そうしなかった場合、起動時に重要なイベントを受け取り損なう可能性があります。

次のサンプルは、この 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
    }
}

クリーンアップ

ゲームを閉じる際には、InputMappingProvider インスタンスとすべての InputRemappingListener インスタンスの登録を解除しますが、それを実行しなくても Input SDK により自動的にリソースのリークが回避されます。

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

テスト

Input SDK の実装をテストするには、オーバーレイを手動で開いてプレーヤーから見た動作を確認するか、adb シェルから自動でテストと検証を行います。

PC 版 Google Play Games エミュレータでは、一般的な誤りに照らして入力マップの正しさを確認します。一意の ID の重複、異なる入力マップの使用、割り当て変更ルールの違反(割り当て変更が有効になっている場合)などの状況では、オーバーレイに次のようなエラー メッセージが表示されます。 Input SDK のオーバーレイ

コマンドラインで adb を使用し、Input SDK の実装を検証します。現在の入力マップを取得するには、次の adb shell コマンドを使用します(MY.PACKAGE.NAME は実際のゲーム名に置き換えます)。

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

InputMap が正常に登録されると、次のような出力が表示されます。

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

ローカライズ

Input SDK は、Android のローカライズ システムを使用しません。そのため InputMap を送信するときは、ローカライズした文字列を指定する必要があります。また、ゲームエンジンのローカライズ システムを使用することもできます。

ProGuard

ProGuard を使用してゲームを軽量化する場合、ProGuard の設定ファイルに次のルールを追加して、この SDK が最終的なパッケージから取り除かれないようにします。

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

次のステップ

Input SDK をゲームに統合したら、PC 版 Google Play Games の残りの要件に引き続き取り組むことができます。詳細については、PC 版 Google Play Games のスタートガイドをご覧ください。