Начало работы с Input SDK

В этом документе описывается, как настроить и отобразить Input SDK в играх, поддерживающих Google Play Games на ПК. Задачи включают добавление SDK в вашу игру и генерацию карты ввода, которая содержит назначения действий игры пользовательскому вводу.

Прежде чем начать

Прежде чем добавлять Input SDK в свою игру, необходимо обеспечить поддержку ввода с клавиатуры и мыши с использованием системы ввода вашего игрового движка .

Input SDK предоставляет Google Play Games на ПК информацию о том, какие элементы управления используются в вашей игре, чтобы они могли отображаться пользователю. Он также может опционально разрешать переназначение клавиш для пользователей.

Каждый элемент управления представляет собой InputAction (например, "J" для "Прыжка"), и вы организуете свои InputActions в InputGroups . InputGroup может представлять собой различный режим в вашей игре, например, "Вождение", "Ходьба" или "Главное меню". Вы также можете использовать InputContexts , чтобы указать, какие группы активны в разные моменты игры.

Вы можете включить автоматическую перенастройку клавиш, но если вы предпочитаете использовать собственный интерфейс переназначения элементов управления, вы можете отключить переназначение клавиш в Input SDK.

Следующая диаграмма последовательности описывает принцип работы API Input SDK:

Последовательность действий в реализации игры, которая вызывает API Input SDK, и её взаимодействие с устройством Android.

Если в вашей игре используется Input SDK, элементы управления отображаются в оверлее Google Play Games на ПК.

Наложение Google Play Games на ПК

Вкладка Google Play Games на ПК («вкладка») отображает элементы управления, определенные вашей игрой. Пользователи могут получить доступ к вкладке в любое время, нажав Shift + Tab .

Наложение Google Play Games на ПК.

Рекомендации по разработке сочетаний клавиш

При разработке сочетаний клавиш учитывайте следующие рекомендации:

  • Сгруппируйте ваши InputActions в логически связанные InputGroups , чтобы улучшить навигацию и удобство использования элементов управления во время игры.
  • Назначьте каждому InputGroup не более одного InputContext . Более детальная настройка InputMap обеспечит более удобную навигацию по элементам управления в оверлее.
  • Создайте InputContext для каждого типа сцен в вашей игре. Как правило, для всех сцен, напоминающих меню, можно использовать один InputContext . Используйте разные InputContexts для мини-игр в вашей игре или для альтернативных элементов управления в одной сцене.
  • Если два действия предназначены для использования одной и той же клавиши в рамках одного и того же InputContext , используйте строковую метку, например, "Взаимодействовать / Выстрелить".
  • Если две клавиши предназначены для привязки к одному и тому же InputAction используйте в игре два разных InputActions , выполняющих одно и то же действие. Вы можете использовать одну и ту же строку метки для обоих InputActions , но их ID должен быть разным.
  • Если клавиша-модификатор применяется к набору клавиш, рекомендуется использовать один InputAction с этой клавишей-модификатором вместо нескольких InputActions , объединяющих эти клавиши (например, использовать Shift и W, A, S, D вместо Shift + W, Shift + A, Shift + S, Shift + D ).
  • Переназначение клавиш автоматически отключается, когда пользователь вводит текст в текстовые поля. Следуйте рекомендациям по реализации текстовых полей Android, чтобы гарантировать, что Android сможет распознавать текстовые поля в вашей игре и предотвращать конфликт с переназначенными клавишами. Если в вашей игре используются нестандартные текстовые поля, вы можете использовать setInputContext() с InputContext содержащим пустой список InputGroups , чтобы вручную отключить переназначение клавиш.
  • Если ваша игра поддерживает переназначение клавиш, обновите их настройки — это деликатная операция, которая может конфликтовать с сохраненными пользователем версиями. По возможности избегайте изменения идентификаторов существующих элементов управления.

Функция переназначения

Google Play Games на ПК поддерживает переназначение клавиш управления на основе настроек клавиш, предоставляемых вашей игрой с помощью Input SDK. Эта функция является необязательной и может быть полностью отключена. Например, вы можете захотеть предоставить собственный интерфейс переназначения клавиш. Чтобы отключить переназначение для вашей игры, вам просто нужно указать параметр переназначения disabled для вашего InputMap (см. раздел «Создание InputMap» для получения дополнительной информации).

Для доступа к этой функции пользователям необходимо открыть оверлей, а затем щелкнуть действие, которое они хотят переназначить. После каждого события переназначения Google Play Games на ПК сопоставляет каждое переназначенное пользователем управление с управлением по умолчанию, которое ожидает ваша игра, поэтому вашей игре не нужно знать о переназначении игрока. При желании вы можете обновить ресурсы, используемые для отображения управления с клавиатуры в вашей игре, добавив функцию обратного вызова для событий переназначения.

Попробуйте переназначить клавишу.

Google Play Games на ПК сохраняет переназначенные элементы управления локально для каждого пользователя, обеспечивая сохранение управления между игровыми сессиями. Эта информация хранится на диске только для платформы ПК и не влияет на работу на мобильных устройствах. Данные управления удаляются при удалении или повторной установке Google Play Games на ПК. Эти данные не сохраняются на нескольких устройствах ПК.

Для поддержки функции переназначения клавиш в вашей игре избегайте следующих ограничений:

Ограничения переназначения

Функции переназначения клавиш можно отключить в игре, если в настройках сочетаний клавиш присутствует любой из следующих случаев:

  • Многоклавишные InputActions не являющиеся комбинацией клавиши-модификатора и клавиши без модификатора. Например, комбинация Shift + A допустима, а A + B , Ctrl + Alt или Shift + A + Tab — нет.
  • InputMap содержит InputActions , InputGroups или InputContexts с повторяющимися уникальными идентификаторами.

Ограничения переназначения

При разработке сочетаний клавиш для переназначения учитывайте следующие ограничения:

  • Переназначение клавиш на комбинации не поддерживается. Например, пользователи не могут переназначить Shift + A на Ctrl + B или A на Shift + A.
  • Переназначение клавиш для InputActions с помощью кнопок мыши не поддерживается. Например, сочетание клавиш Shift + правая кнопка мыши не может быть переназначено.

Проверьте переназначение клавиш в играх Google Play на эмуляторе ПК.

Включить функцию переназначения клавиш в Google Play Games на ПК-эмуляторе можно в любое время, выполнив следующую команду adb:

adb shell dumpsys input_mapping_service --set RemappingFlagValue true

Изменение наложения показано на следующем изображении:

Наложение с включенной функцией переназначения клавиш.

Добавьте SDK

Установите Input SDK в соответствии с вашей платформой разработки.

Java и Kotlin

Чтобы получить Input SDK для Java или Kotlin, добавьте зависимость в файл build.gradle на уровне модуля:

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

Единство

Input SDK — это стандартный пакет Unity с несколькими зависимостями.

Для установки пакета необходимо установить все его зависимости. Существует несколько способов установки пакетов.

Установите файл .unitypackage

Загрузите файл Unitypackage для Input SDK со всеми его зависимостями. Вы можете установить .unitypackage , выбрав Assets > Import package > Custom Package и найдя загруженный файл.

Установка с помощью UPM

В качестве альтернативы вы можете установить пакет с помощью менеджера пакетов Unity , загрузив файл .tgz и установив его зависимости:

Установка с помощью OpenUPM

Вы можете установить пакет с помощью OpenUPM .

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

Примеры игр

Примеры интеграции с Input SDK можно найти в AGDK Tunnel для игр на Kotlin или Java и Trivial Kart для игр на Unity.

Создайте свои сочетания клавиш.

Зарегистрируйте свои сочетания клавиш, создав InputMap и вернув его с помощью InputMappingProvider . В следующем примере показан InputMappingProvider :

Котлин

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 InputActions должны иметь уникальные идентификаторы.

Если вы поддерживаете переназначение клавиш, вы можете определить, какие InputActions могут быть переназначены. Если ваша игра не поддерживает переназначение клавиш, вам следует отключить эту опцию для всех ваших InputActions , но Input SDK достаточно интеллектуален, чтобы отключить переназначение, если вы не поддерживаете его в вашем InputMap .

Этот пример отображает космос ключ к действию Drive .

Котлин

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

Всплывающее окно отображает действие ввода одной клавишей.

Действия также могут представлять собой ввод с мыши. В этом примере щелчок левой кнопкой мыши назначает действие «Перемещение» :

Котлин

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 . В этом примере. космос + сдвиг назначено на действие Turbo , которое работает даже когда Космос сопоставлен с диском .

Котлин

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

Всплывающее окно отображает действие ввода с помощью нескольких клавиш.

Встроенный SDK ввода позволяет комбинировать нажатие кнопок мыши и клавиш для выполнения одного действия. Этот пример демонстрирует, что Сдвиг В этом примере игры одновременное нажатие правой кнопки мыши добавляет точку маршрута:

Котлин

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 : строка, отображаемая в пользовательском интерфейсе для обозначения данного действия. Локализация не выполняется автоматически, поэтому выполните локализацию заранее.
  • InputControls : определяет элементы управления вводом, используемые в этом действии. Элементы управления соответствуют одинаковым глифам в наложенном изображении.
  • InputActionId : Объект InputIdentifier , хранящий числовой идентификатор и версию InputAction (подробнее см. в разделе «Идентификаторы ключей отслеживания »).
  • InputRemappingOption : одно из InputEnums.REMAP_OPTION_ENABLED или InputEnums.REMAP_OPTION_DISABLED . Определяет, разрешено ли переназначение действия. Если ваша игра не поддерживает переназначение, вы можете пропустить это поле или просто установить его в значение «отключено».
  • RemappedInputControls : объект InputControls только для чтения, используемый для чтения переназначенной пользователем клавиши при событиях переназначения (используется для получения уведомлений о событиях переназначения ).

InputControls представляет собой поля ввода, связанные с действием, и содержит следующие поля:

  • AndroidKeycodes : это список целых чисел, представляющих ввод с клавиатуры, связанный с действием. Они определяются в классе KeyEvent или в классе AndroidKeycode для Unity.
  • MouseActions : это список значений MouseAction , представляющих ввод данных с мыши, связанный с данным действием.

Определите группы ввода

InputActions группируются логически связанными действиями с помощью InputGroups для улучшения навигации и удобства обнаружения элементов управления во всплывающем окне. Идентификатор каждой InputGroup должен быть уникальным во всех InputGroups в вашей игре.

Сгруппировав действия ввода, вы упростите игроку поиск правильной комбинации клавиш для текущего контекста.

Если вы поддерживаете переназначение клавиш, вы можете определить, какие InputGroups могут быть переназначены. Если ваша игра не поддерживает переназначение клавиш, вам следует отключить эту опцию для всех ваших InputGroups , но Input SDK достаточно интеллектуален, чтобы отключить переназначение, если вы не поддерживаете его в вашем InputMap .

Котлин

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

В следующем примере на наложенном изображении отображаются группы элементов управления «Дорога» и « Меню» :

Наложение, отображающее InputMap, содержащее группы ввода элементов управления дорогами и элементов управления меню.

InputGroup содержит следующие поля:

  • GroupLabel : строка, которая будет отображаться во всплывающем окне и может использоваться для логической группировки набора действий. Эта строка не локализуется автоматически.
  • InputActions : список объектов InputAction определенных вами на предыдущем шаге. Все эти действия визуально отображаются под заголовком группы.
  • InputGroupId : Объект InputIdentifier , хранящий числовой идентификатор и версию InputGroup . Дополнительную информацию см. в разделе «Отслеживание идентификаторов ключей» .
  • InputRemappingOption : один из InputEnums.REMAP_OPTION_ENABLED или InputEnums.REMAP_OPTION_DISABLED . Если отключено, переназначение для всех объектов InputAction , принадлежащих этой группе, будет отключено, даже если для них указан параметр переназначения «включено». Если включено, все действия, принадлежащие этой группе, могут быть переназначены, если только для отдельных действий не указано, что этот параметр отключен.

Определите контексты ввода.

InputContexts позволяет вашей игре использовать разные наборы клавиш управления для разных сцен. Например:

  • Вы можете указать разные наборы команд для навигации по меню и для перемещения в игре.
  • В зависимости от способа передвижения в игре, например, вождения или ходьбы, вы можете указать разные наборы входных данных.
  • Вы можете указать различные наборы действий в зависимости от текущего состояния игры, например, для навигации по игровому миру или для прохождения отдельного уровня.

При использовании InputContexts наложение сначала отображает группы используемого контекста. Чтобы включить это поведение, вызовите setInputContext() , чтобы установить контекст всякий раз, когда ваша игра переходит в другую сцену. На следующем изображении показано это поведение: в сцене «вождение» действия управления дорогой отображаются в верхней части наложения. При открытии меню «магазин» действия управления меню отображаются в верхней части наложения.

Группы сортировки InputContexts в наложенном слое.

Обновление наложения достигается путем установки различных значений InputContext в разных точках игры. Для этого:

  1. Сгруппируйте InputActions по логически связанным действиям, используя InputGroups
  2. Назначьте эти InputGroups объекту InputContext для различных частей вашей игры.

InputGroups принадлежащие одному и тому же InputContext не могут иметь конфликтующих InputActions если используется один и тот же ключ. Рекомендуется назначать каждую InputGroup одному InputContext .

Приведенный ниже пример кода демонстрирует логику InputContext :

Котлин

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 : Объект InputIdentifier , хранящий числовой идентификатор и версию InputContext (подробнее см. в разделе «Отслеживание идентификаторов ключей »).
  • ActiveGroups : список InputGroups , которые будут использоваться и отображаться в верхней части наложения, когда данный контекст активен.

Создайте карту ввода.

InputMap — это набор всех объектов InputGroup , доступных в игре, и, следовательно, всех объектов InputAction которые игрок может ожидать выполнить.

При отправке сообщения о назначении клавиш вы создаёте InputMap со всеми InputGroups используемыми в вашей игре.

Если ваша игра не поддерживает переназначение клавиш, отключите эту опцию и оставьте поле зарезервированных клавиш пустым.

В следующем примере создается объект InputMap используемый для отображения коллекции InputGroups .

Котлин

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 : группы ввода, сообщаемые вашей игрой. Группы отображаются в наложении в порядке их следования, если не указаны текущие используемые группы, если не был вызван метод setInputContext() .
  • MouseSettings : Объект MouseSettings указывает, что чувствительность мыши можно регулировать и что курсор мыши инвертирован по оси Y.
  • InputMapId : Объект InputIdentifier , хранящий числовой идентификатор и версию InputMap (подробнее см. в разделе «Отслеживание идентификаторов ключей »).
  • InputRemappingOption : один из InputEnums.REMAP_OPTION_ENABLED или InputEnums.REMAP_OPTION_DISABLED . Определяет, включена ли функция переназначения.
  • ReservedControls : список InputControls на которые пользователям не будет разрешено переназначать кнопки.

Отслеживание ключевых идентификаторов

Объекты InputAction , InputGroup , InputContext и InputMap содержат объект InputIdentifier , который хранит уникальный числовой идентификатор и строковый идентификатор версии. Отслеживание строковой версии ваших объектов необязательно, но рекомендуется для отслеживания версий вашего InputMap . Если строковая версия не указана, строка пуста. Для объектов InputMap строковая версия обязательна.

В следующем примере строковая версия присваивается объектам InputActions или InputGroups :

Котлин

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 должны быть уникальными для всех объектов InputActions в вашем InputMap . Аналогично, идентификаторы объектов InputGroup должны быть уникальными для всех InputGroups в InputMap . Следующий пример демонстрирует, как использовать enum для отслеживания уникальных идентификаторов ваших объектов:

Котлин

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 : уникальный числовой идентификатор, предназначенный для однозначной идентификации заданного набора входных данных.
  • VersionString : удобочитаемая строка версии, предназначенная для идентификации версии входных данных между двумя версиями изменений входных данных.

Получать уведомления о событиях переназначения (необязательно)

Получайте уведомления о событиях переназначения клавиш, чтобы быть в курсе того, какие клавиши используются в вашей игре. Это позволяет вашей игре обновлять ресурсы, отображаемые на игровом экране, используемом для отображения элементов управления действиями.

На следующем изображении показан пример такого поведения после переназначения клавиш. Г , П и С к Дж. , X и Т Соответственно, элементы пользовательского интерфейса игры обновляются, отображая клавиши, заданные пользователем.

Пользовательский интерфейс реагирует на события переназначения с помощью функции обратного вызова InputRemappingListener.

Эта функциональность реализуется путем регистрации функции обратного вызова InputRemappingListener . Для реализации этой функции сначала зарегистрируйте экземпляр InputRemappingListener :

Котлин

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 устанавливайте контекст для каждого перехода на новую сцену, включая первый контекст, использованный для вашей начальной сцены. Вам необходимо установить InputContext после регистрации InputMap .

Если вы используете InputRemappingListeners для получения уведомлений о событиях переназначения клавиш, зарегистрируйте InputRemappingListener перед регистрацией InputMappingProvider , иначе ваша игра может пропустить важные события во время запуска.

Следующий пример демонстрирует, как инициализировать API:

Котлин

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 достаточно интеллектуален, чтобы избежать утечки ресурсов, если вы этого не сделаете:

Котлин

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 для автоматического тестирования и проверки.

Эмулятор Google Play Games на ПК проверяет правильность вашей карты ввода на наличие распространенных ошибок. В таких случаях, как дублирование уникальных идентификаторов, использование разных карт ввода или несоответствие правилам переназначения (если переназначение включено), наложение отображает сообщение об ошибке, как показано ниже: Наложение Input SDK.

Проверьте реализацию вашего Input SDK с помощью adb в командной строке. Чтобы получить текущую карту ввода, используйте следующую команду 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, чтобы гарантировать, что SDK не будет удален из итогового пакета:

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

Что дальше?

После интеграции Input SDK в вашу игру вы можете продолжить выполнение оставшихся требований Google Play Games на ПК. Для получения дополнительной информации см. раздел «Начало работы с Google Play Games на ПК» .