О чем этот пример
Добавление поддержки геймпада раскрывает новые возможности управления для вашей игры, будь то платформер, шутер или аркада. Эта статья на практическом примере покажет, как в Phaser 3 обрабатывать подключение геймпада и использовать его стики или D-Pad для плавного перемещения спрайтов по экрану. Вы научитесь настраивать систему ввода, корректно обрабатывать момент подключения устройства и читать состояние его кнопок в реальном времени.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
sprites = [];
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sky', 'assets/skies/lightblue.png');
this.load.image('elephant', 'assets/sprites/elephant.png');
}
create ()
{
this.add.image(0, 0, 'sky').setOrigin(0);
let text;
if (this.input.gamepad.total === 0)
{
text = this.add.text(10, 10, 'Press any button on a connected Gamepad', { font: '16px Courier', fill: '#00ff00' });
this.input.gamepad.once('connected', function (pad)
{
console.log('connected', pad.id);
for (let i = 0; i < this.input.gamepad.total; i++)
{
this.sprites.push(this.add.sprite(Phaser.Math.Between(200, 600), Phaser.Math.Between(100, 500), 'elephant'));
}
text.destroy();
}, this);
}
else
{
for (let i = 0; i < this.input.gamepad.total; i++)
{
this.sprites.push(this.add.sprite(Phaser.Math.Between(200, 600), Phaser.Math.Between(100, 500), 'elephant'));
}
}
}
update ()
{
const pads = this.input.gamepad.gamepads;
for (let i = 0; i < pads.length; i++)
{
const gamepad = pads[i];
if (!gamepad)
{
continue;
}
const sprite = this.sprites[i];
if (gamepad.left)
{
sprite.x -= 4;
sprite.flipX = false;
}
else if (gamepad.right)
{
sprite.x += 4;
sprite.flipX = true;
}
if (gamepad.up)
{
sprite.y -= 4;
}
else if (gamepad.down)
{
sprite.y += 4;
}
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
input: {
gamepad: true
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка проекта и базовой конфигурации
Всё начинается с конфигурации игры. Ключевой момент — явное включение системы геймпадов в настройках ввода. Без этого Phaser не будет слушать события от подключённых контроллеров.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
input: {
gamepad: true // Включаем поддержку геймпадов
},
scene: Example
};
const game = new Phaser.Game(config);
Класс сцены Example содержит три основных метода жизненного цикла: preload, create и update. Также объявляется массив sprites = [] для хранения ссылок на все создаваемые спрайты.
Загрузка ресурсов и первичное создание сцены
В методе preload загружаются изображения для фона и спрайта. Важно использовать setBaseURL для указания базового пути, чтобы не дублировать полные URL для каждого ресурса.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sky', 'assets/skies/lightblue.png');
this.load.image('elephant', 'assets/sprites/elephant.png');
}
В create сначала добавляется фоновое изображение. Затем логика разделяется в зависимости от того, подключён ли уже геймпад к системе в момент запуска игры.
Обработка подключения геймпада
Phaser предоставляет доступ к количеству подключённых геймпадов через this.input.gamepad.total. Если на старте сцены геймпадов нет (total === 0), мы выводим текстовую подсказку и подписываемся на одноразовое событие connected.
if (this.input.gamepad.total === 0)
{
text = this.add.text(10, 10, 'Press any button on a connected Gamepad', { font: '16px Courier', fill: '#00ff00' });
this.input.gamepad.once('connected', function (pad)
{
console.log('connected', pad.id);
// Создание спрайтов после подключения
for (let i = 0; i < this.input.gamepad.total; i++)
{
this.sprites.push(this.add.sprite(Phaser.Math.Between(200, 600), Phaser.Math.Between(100, 500), 'elephant'));
}
text.destroy(); // Удаляем подсказку
}, this);
}
Обратите внимание на третий аргумент this в once. Он задаёт контекст выполнения callback-функции, чтобы внутри неё this ссылался на экземпляр сцены, а не на объект события. Если геймпад уже подключён, спрайты создаются сразу в основном потоке метода create.
Чтение состояния геймпада и управление спрайтом
Основная логика движения находится в методе update, который вызывается на каждом кадре игры. Сначала получаем массив всех геймпадов this.input.gamepad.gamepads. Каждый элемент массива — это объект Gamepad или null, если слот пуст.
update ()
{
const pads = this.input.gamepad.gamepads;
for (let i = 0; i < pads.length; i++)
{
const gamepad = pads[i];
if (!gamepad) { continue; } // Пропускаем пустые слоты
const sprite = this.sprites[i];
// ... логика движения
}
}
Объект gamepad содержит булевы свойства left, right, up, down, которые становятся true, когда нажата соответствующая направляющая кнопка или отклонён стик. В примере эти свойства используются для изменения координат спрайта и его отражения по горизонтали.
if (gamepad.left)
{
sprite.x -= 4;
sprite.flipX = false;
}
else if (gamepad.right)
{
sprite.x += 4;
sprite.flipX = true;
}
if (gamepad.up)
{
sprite.y -= 4;
}
else if (gamepad.down)
{
sprite.y += 4;
}
Что попробовать дальше
Теперь вы умеете подключать геймпад к игре на Phaser 3 и управлять объектами с его помощью. Это основа для создания локального мультиплеера, где каждый игрок использует свой контроллер. Для экспериментов попробуйте
- Назначить разным кнопкам (
buttons[0],buttons[1]) различные действия (прыжок, атака) - Использовать значения осей аналоговых стиков (
axes[0],axes[1]) для более плавного и точного управления скоростью движения - Реализовать вибрацию (
gamepad.vibration) в качестве тактильной отдачи
