Gamepad API в Phaser

Phaser предоставляет встроенную поддержку работы с геймпадами через Gamepad Plugin, доступный через объект this.input.gamepad в сцене. Для начала работы необходимо убедиться, что плагин активирован (он включён по умолчанию в Phaser 3) и что подключение контроллера отслеживается:

this.input.gamepad.once('connected', function (pad) {
    console.log('Геймпад подключен:', pad.id);
});

Событие 'connected' срабатывает при подключении нового контроллера. Внутри обработчика доступен объект pad типа Phaser.Input.Gamepad.Gamepad, содержащий информацию о кнопках, стиках и состоянии подключения.

Для проверки наличия геймпадов в любой момент можно использовать массив this.input.gamepad.gamepads, который содержит все подключенные устройства. Неиспользуемые элементы в массиве будут равны null.


Основные свойства объекта Gamepad

Объект Gamepad предоставляет несколько ключевых свойств:

  • id — строка с идентификатором устройства.
  • index — числовой индекс контроллера, присваиваемый браузером.
  • buttons — массив объектов Phaser.Input.Gamepad.Button, отражающих состояние каждой кнопки.
  • axes — массив числовых значений стиков (от -1 до 1), где каждая пара значений соответствует одному стику.
  • connected — булевое значение, показывающее, подключен ли контроллер.
  • mapping — строка 'standard' или нестандартная, указывающая на тип схемы кнопок.

Объект кнопки имеет свойства:

  • pressed — булевое, если кнопка нажата.
  • value — числовое, от 0 до 1, для аналоговых кнопок.
  • touched — булевое, отражающее, коснулся ли пользователь кнопки.

Обработка кнопок

Для отслеживания нажатий кнопок Phaser предоставляет два подхода:

  1. Проверка состояния в игровом цикле:
update() {
    const pad = this.input.gamepad.getPad(0);
    if (pad) {
        if (pad.buttons[0].pressed) {
            console.log('Нажата кнопка A');
        }
    }
}
  1. Использование событий кнопок:
  • 'down' — срабатывает при нажатии кнопки.
  • 'up' — при отпускании.
  • 'axis' — при изменении положения стика.

Пример:

this.input.gamepad.on('down', function (pad, button, index) {
    console.log(`Кнопка ${index} нажата`);
});

События позволяют реагировать на действия мгновенно, без постоянной проверки состояния кнопок в update.


Работа с аналоговыми стиками

Стики представлены как массив чисел axes, где:

  • axes[0] и axes[1] — горизонтальная и вертикальная ось левого стика.
  • axes[2] и axes[3] — оси правого стика.

Значения изменяются от -1 до 1, при этом 0 соответствует нейтральному положению. Для плавного управления объектами в игре удобно использовать мёртвую зону, чтобы игнорировать незначительные смещения стика:

const deadZone = 0.2;
const x = Math.abs(pad.axes[0].getValue()) > deadZone ? pad.axes[0].getValue() : 0;
const y = Math.abs(pad.axes[1].getValue()) > deadZone ? pad.axes[1].getValue() : 0;
player.x += x * player.speed;
player.y += y * player.speed;

Поддержка нескольких геймпадов

Phaser позволяет работать с несколькими контроллерами одновременно. Каждый новый контроллер добавляется в массив this.input.gamepad.gamepads. Для получения конкретного геймпада используют метод getPad(index):

const pad1 = this.input.gamepad.getPad(0);
const pad2 = this.input.gamepad.getPad(1);

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


Встроенные методы и утилиты

  • pad.isButtonDown(index) — возвращает true, если кнопка нажата.
  • pad.justPressed(index, duration) — проверяет, была ли кнопка нажата недавно (в течение duration миллисекунд).
  • pad.justReleased(index, duration) — проверка на отпускание кнопки.
  • pad.getAxis(index) — возвращает текущее значение оси.

Эти методы упрощают обработку событий без постоянной проверки состояния кнопок и стиков вручную.


Практическое применение

Пример управления персонажем с геймпадом:

update() {
    const pad = this.input.gamepad.getPad(0);
    if (!pad) return;

    const speed = 200;
    let dx = 0;
    let dy = 0;

    dx = Math.abs(pad.axes[0].getValue()) > 0.1 ? pad.axes[0].getValue() : 0;
    dy = Math.abs(pad.axes[1].getValue()) > 0.1 ? pad.axes[1].getValue() : 0;

    player.x += dx * speed * this.game.loop.delta / 1000;
    player.y += dy * speed * this.game.loop.delta / 1000;

    if (pad.isButtonDown(0)) {
        player.jump();
    }
}

В этом примере учтена плавная обработка осей, а действия кнопок и стиков интегрированы с игровым циклом.


Совместимость и особенности

  • На десктопе большинство браузеров поддерживают Gamepad API через стандартную схему кнопок.
  • На мобильных устройствах поддержка ограничена, часто требуется внешний геймпад.
  • Для разных производителей контроллеров схемы могут отличаться; использование свойства mapping позволяет корректно обрабатывать стандартные кнопки без привязки к конкретному устройству.

Gamepad API в Phaser обеспечивает гибкое управление, позволяя реализовать поддержку различных контроллеров с минимальными затратами на код и обеспечивая плавный отклик игрока.