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

Управление с помощью стрелок — основа многих игр. Но что, если вам нужно знать не просто факт нажатия, а сколько времени игрок удерживает кнопку? Это может пригодиться для зарядки прыжка, ускорения или активации особых способностей. В этой статье мы разберем официальный пример Phaser, который показывает, как использовать объект `Key` для точного замера длительности нажатий. Вы научитесь получать временные метки и рассчитывать продолжительность удержания клавиши, что откроет новые возможности для геймдизайна.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    downKeyDebug;
    upKeyDebug;
    rightKeyDebug;
    leftKeyDebug;
    player;
    cursors;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('elephant', 'assets/sprites/elephant.png');
    }

    create ()
    {
        this.cursors = this.input.keyboard.createCursorKeys();

        this.player = this.physics.add.image(400, 300, 'elephant');

        this.player.setCollideWorldBounds(true);

        this.leftKeyDebug = this.add.text(10, 300, 'Left', { font: '16px Courier', fill: '#00ff00' });
        this.rightKeyDebug = this.add.text(570, 300, 'Right', { font: '16px Courier', fill: '#00ff00' });
        this.upKeyDebug = this.add.text(300, 10, 'Up', { font: '16px Courier', fill: '#00ff00' });
        this.downKeyDebug = this.add.text(300, 530, 'Down', { font: '16px Courier', fill: '#00ff00' });
    }

    update ()
    {
        this.player.setVelocity(0);

        if (this.cursors.left.isDown)
        {
            this.player.setVelocityX(-300);
        }
        else if (this.cursors.right.isDown)
        {
            this.player.setVelocityX(300);
        }

        if (this.cursors.up.isDown)
        {
            this.player.setVelocityY(-300);
        }
        else if (this.cursors.down.isDown)
        {
            this.player.setVelocityY(300);
        }

        this.leftKeyDebug.setText([
            `Left: ${this.cursors.left.isDown}`,
            `down: ${this.cursors.left.timeDown}`,
            `up: ${this.cursors.left.timeUp}`,
            `duration: ${(this.cursors.left.isDown) ? this.cursors.left.getDuration() : this.cursors.left.duration}`
        ]);

        this.rightKeyDebug.setText([
            `Right: ${this.cursors.right.isDown}`,
            `down: ${this.cursors.right.timeDown}`,
            `up: ${this.cursors.right.timeUp}`,
            `duration: ${(this.cursors.right.isDown) ? this.cursors.right.getDuration() : this.cursors.right.duration}`
        ]);

        this.upKeyDebug.setText([
            `Up: ${this.cursors.up.isDown}`,
            `down: ${this.cursors.up.timeDown}`,
            `up: ${this.cursors.up.timeUp}`,
            `duration: ${(this.cursors.up.isDown) ? this.cursors.up.getDuration() : this.cursors.up.duration}`
        ]);

        this.downKeyDebug.setText([
            `Down: ${this.cursors.down.isDown}`,
            `down: ${this.cursors.down.timeDown}`,
            `up: ${this.cursors.down.timeUp}`,
            `duration: ${(this.cursors.down.isDown) ? this.cursors.down.getDuration() : this.cursors.down.duration}`
        ]);
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    backgroundColor: '#0072bc',
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Настройка сцены и создание объектов

В методе preload() загружается спрайт, а в create() происходит основная инициализация. Ключевой момент — создание объекта cursors, который представляет собой группу из четырех объектов Key (для стрелок). Каждый такой объект содержит свойства для работы со временем.

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

create ()
{
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.physics.add.image(400, 300, 'elephant');
    this.player.setCollideWorldBounds(true);
    // Создание текстовых полей для отладки
    this.leftKeyDebug = this.add.text(10, 300, 'Left', { font: '16px Courier', fill: '#00ff00' });
    // ... аналогично для rightKeyDebug, upKeyDebug, downKeyDebug
}

Базовое движение и свойства времени

В методе update() реализовано стандартное движение игрока с помощью setVelocity(). Логика проста: если нажата стрелка, задается скорость по соответствующей оси.

Объект Key (например, this.cursors.left) предоставляет несколько полезных свойств, связанных со временем: * isDown — логический флаг, нажата ли клавиша в данный момент. * timeDown — временная метка (в миллисекундах), когда клавиша была нажата в последний раз. * timeUp — временная метка, когда клавиша была отпущена в последний раз. * duration — продолжительность (в миллисекундах), сколько клавиша была отпущена. Актуально, когда isDown равно false.

if (this.cursors.left.isDown)
{
    this.player.setVelocityX(-300);
}
// ... обработка остальных направлений

Измерение длительности нажатия

Самая интересная часть — расчет, сколько времени клавиша удерживается нажатой. Для этого у объекта Key есть метод getDuration(). Он возвращает количество миллисекунд, прошедших с момента последнего нажатия (timeDown).

В примере используется тернарный оператор для выбора отображаемого значения: если клавиша нажата сейчас, вызывается getDuration(), чтобы получить актуальную длительность удержания. Если клавиша отпущена, отображается свойство duration, которое хранит, как долго она была нажата в последний раз.

this.leftKeyDebug.setText([
    `Left: ${this.cursors.left.isDown}`,
    `down: ${this.cursors.left.timeDown}`,
    `up: ${this.cursors.left.timeUp}`,
    `duration: ${(this.cursors.left.isDown) ? this.cursors.left.getDuration() : this.cursors.left.duration}`
]);

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

Получение точного времени нажатия открывает множество игровых механик. Вот несколько идей: 1. **Прыжок с переменной высотой:** Чем дольше игрок удерживает клавишу прыжка, тем выше и дальше прыжок. 2. **Заряжаемое оружие или атака:** Длительность нажатия определяет мощность выстрела или область эффекта. 3. **Бег или ускорение:** Персонаж начинает с обычной скорости, а после удержания клавиши движения 1-2 секунды переходит на спринт. 4. **Тайминговые события в диалогах или QTE:** Проверка, удержал ли игрок клавишу достаточно долго для успешного действия.

Для реализации таких механик достаточно в update() проверять значение, возвращаемое getDuration(), и на его основе менять параметры игрового объекта.

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

Использование временных свойств объекта Key в Phaser — это мощный инструмент, выходящий за рамки простого «нажато/отпущено». Он позволяет создавать более глубокие и отзывчивые игровые механики, основанные на времени реакции игрока. Для экспериментов попробуйте изменить пример: добавьте визуальный индикатор заряда (например, растущую полоску), который зависит от getDuration(). Или реализуйте систему комбо, где следующее действие должно быть выполнено в строго определенный временной интервал после предыдущего нажатия, используя свойства timeDown и timeUp для расчетов.