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

Создание игровых механик часто требует точного контроля над временем. Задержка перед появлением врага, перезарядка способности, периодическое начисление ресурсов – всё это реализуется с помощью таймеров. В Phaser 3 для этого есть мощная и простая в использовании система Time Events. В этой статье на примере из официальной документации разберём, как отложить выполнение функции и отслеживать прогресс таймера, чтобы делать ваши игры более динамичными и отзывчивыми.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    timedEvent;
    text;
    image;

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

    create ()
    {
        this.image = this.add.image(400, 300, 'einstein');

        this.text = this.add.text(32, 32);

        // timedEvent = this.time.addEvent({ delay: 2000, callback: onEvent, callbackScope: this });

        //  The same as above, but uses a method signature to declare it (shorter, and compatible with GSAP syntax)
        this.timedEvent = this.time.delayedCall(3000, this.onEvent, [], this);
    }

    update ()
    {
        this.text.setText(`Event.progress: ${this.timedEvent.getProgress().toString().substr(0, 4)}`);
    }

    onEvent ()
    {
        this.image.setScale(0.5);
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ассетов

Вся работа начинается в классе сцены, унаследованном от Phaser.Scene. В методе preload() мы загружаем изображение, которое позже будет использоваться для визуальной демонстрации работы таймера. Важно установить базовый URL для загрузчика, чтобы указывать относительные пути к файлам.

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

Создание объектов и запуск таймера

В методе create() происходит инициализация игровых объектов. Мы создаём изображение и текстовый объект для вывода информации. Самое важное здесь – создание отложенного вызова с помощью this.time.delayedCall(). Этот метод планирует выполнение указанной функции через заданное количество миллисекунд.

create ()
{
    this.image = this.add.image(400, 300, 'einstein');
    this.text = this.add.text(32, 32);
    this.timedEvent = this.time.delayedCall(3000, this.onEvent, [], this);
}
Метод `delayedCall` принимает четыре аргумента:
1.  `delay` (3000) – задержка в миллисекундах (3 секунды).
2.  `callback` (`this.onEvent`) – функция, которая будет вызвана.
3.  `args` (`[]`) – массив аргументов, которые будут переданы в callback-функцию (в данном случае пустой).
4.  `callbackScope` (`this`) – контекст (`this`), в котором будет вызвана функция. Это критически важно, чтобы внутри `onEvent` мы могли получить доступ к свойствам сцены, таким как `this.image`.

Альтернативный способ – использование this.time.addEvent() с конфигурационным объектом, что более наглядно для сложных повторяющихся событий.

Отслеживание прогресса выполнения

Метод update() выполняется каждый кадр игры. Здесь мы используем его для того, чтобы в реальном времени отображать прогресс до срабатывания нашего таймера. Объект timedEvent имеет метод getProgress(), который возвращает число от 0 (только что создан) до 1 (таймер сработал).

update ()
{
    this.text.setText(`Event.progress: ${this.timedEvent.getProgress().toString().substr(0, 4)}`);
}

Мы берём результат getProgress(), преобразуем его в строку и обрезаем до четырёх символов, чтобы получить читаемый вывод вида "0.56" в текстовом поле на экране. Это отличный способ визуализировать отсчёт времени для игрока, например, в виде полосы загрузки или анимации.

Обработка срабатывания таймера

Когда проходит 3 секунды, Phaser автоматически вызывает функцию, которую мы указали в delayedCall. В нашем примере это метод onEvent() класса сцены. Внутри него мы выполняем нужное действие – в данном случае изменяем масштаб изображения, уменьшая его вдвое.

onEvent ()
{
    this.image.setScale(0.5);
}

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

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

Система таймеров Phaser 3 (this.time) предоставляет простой и эффективный способ управления временными интервалами в игре. Используя delayedCall для разовых событий или addEvent для повторяющихся, вы можете легко реализовать задержки, кд (кулдауны) и периодические действия. Для экспериментов попробуйте: создать цикличный таймер для спавна врагов, реализовать прогресс-бар перезарядки на основе getProgress() или построить цепочку последовательных событий, где завершение одного запускает следующее.