О чем этот пример

В игровой разработке отзывчивое управление — это основа. Часто для обработки ввода используют события клавиатуры, но в игровом цикле, особенно для постоянного движения персонажа, удобнее напрямую опрашивать состояние клавиш. В этой статье мы разберем, как с помощью метода `addKey()` в Phaser создать объект клавиши, состояние которой можно проверять в методе `update()`, что идеально подходит для плавного и предсказуемого игрового процесса.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    highlight1;
    keySpace;
    key5;
    keyA;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('keyboard', 'assets/input/keyboard-opreem.png');
        this.load.image('highlight', 'assets/input/key1.png');
    }

    create ()
    {
        this.drawKeyboard();

        //  Create a Key object we can poll directly.

        //  This is especially useful if you need to poll the key in a tight loop, such as for player controls.

        this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);

        this.key5 = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.FIVE);

        this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

    }

    update ()
    {

        if (this.keyA.isDown)
        {
            console.log('A');
        }

        if (this.key5.isDown)
        {
            console.log('5');
        }

        if (this.keySpace.isDown)
        {
            console.log('spacebar');
        }

    }

    drawKeyboard ()
    {
        this.add.image(0, 0, 'keyboard').setOrigin(0);

        this.highlight1 = this.add.image(108, 112, 'highlight').setOrigin(0);

        /*
        var row = [1,2,3,4,5,6,7,8,9,0,'Minus','Plus','Backspace_Alt'];

        x = 100;
        y = 100;
        spacing = 106;

        for (var i = 0; i < row.length; i++)
        {
            var key = row[i];

            this.add.image(x, y, 'keyboard', key);

            x += spacing;
        }
        */

    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    backgroundColor: '#0072bc',
    scene: Example
};

const game = new Phaser.Game(config);

Создание объектов клавиш

В Phaser управление клавиатурой централизовано в объекте this.input.keyboard. Метод .addKey() этого объекта позволяет создать специальный объект, представляющий конкретную клавишу. Этот объект можно сохранить в свойстве класса и использовать в любом месте программы, но чаще всего — в основном игровом цикле.

Код создает три таких объекта для клавиш A, 5 и Пробела. Константы с кодами клавиш находятся в объекте Phaser.Input.Keyboard.KeyCodes.

this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
this.key5 = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.FIVE);
this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

Опрос состояния в игровом цикле

Сердце любой игры на Phaser — метод update(), который вызывается на каждом кадре. Здесь мы проверяем состояние созданных ранее объектов клавиш. У каждого объекта есть булево свойство .isDown, которое возвращает true, если клавиша нажата в данный момент.

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

if (this.keyA.isDown)
{
    console.log('A');
}
if (this.key5.isDown)
{
    console.log('5');
}
if (this.keySpace.isDown)
{
    console.log('spacebar');
}

Преимущества прямого опроса

Почему addKey() и проверка .isDown в update() часто лучше событий?

1. **Предсказуемость и синхронизация:** Логика управления выполняется строго в игровом цикле, синхронно с физикой и отрисовкой. Это исключает задержки или "прострелы" кадров, которые могут возникать при асинхронных событиях. 2. **Идеально для непрерывных действий:** Проверка isDown идеально подходит для действий, которые должны длиться, пока зажата клавиша: бег, ускорение, прицеливание. 3. **Простота кода:** Не нужно создавать и удалять многочисленные обработчики событий. Состояние всех нужных клавиш проверяется в одном месте — в update().

Для одиночных действий (прыжок, использование предмета) лучше подходит событие on('down') у объекта клавиши, чтобы действие не повторялось, пока клавиша зажата.

Практическое применение: управление персонажем

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

Сначала создадим объекты клавиш в create(), а затем в update() будем менять скорость спрайта в зависимости от их состояния.

create() {
    this.player = this.physics.add.sprite(400, 300, 'player');
    this.cursors = this.input.keyboard.createCursorKeys(); // Альтернативный способ для стрелок
    // Или так, если нужен больший контроль:
    this.keyLeft = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
    this.keyRight = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
}

update() {
    const speed = 200;
    this.player.setVelocityX(0); // Сбрасываем скорость каждый кадр

    if (this.cursors.left.isDown || this.keyLeft?.isDown) {
        this.player.setVelocityX(-speed);
    }
    else if (this.cursors.right.isDown || this.keyRight?.isDown) {
        this.player.setVelocityX(speed);
    }
}

Что попробовать дальше

Метод addKey() — это мощный и эффективный инструмент для создания отзывчивого игрового управления через прямое опрос состояния клавиш. Он становится незаменимым, когда важна непрерывность и синхронизация действий с игровым циклом. **Идеи для экспериментов:** 1. Создайте комбинацию клавиш (например, SHIFT + A), проверяя состояние двух объектов одновременно в update(). 2. Реализуйте "ускорение" при долгом удержании клавиши, увеличивая скорость персонажа со временем. 3. Сравните производительность и отзывчивость управления через addKey() в update() и через обработчики событий on('down') в сценарии быстрого таппинга.