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

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

Версия 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);

        this.timedEvent = new Phaser.Time.TimerEvent({ delay: 4000 });

        this.time.addEvent(this.timedEvent);

        this.input.on('pointerdown', () =>
        {

            this.time.addEvent(this.timedEvent);

        }, this);
    }

    update ()
    {
        const progress = this.timedEvent.getProgress();

        this.text.setText([
            'Click to restart the Timer',
            `Event.progress: ${progress.toString().substr(0, 4)}`
        ]);

        this.image.setAngle(progress * 20);
    }
}

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

const game = new Phaser.Game(config);

Создание и конфигурация сцены

Класс Example расширяет Phaser.Scene. В нем объявлены три свойства: для хранения ссылок на таймер, текстовый объект и изображение.

В методе preload загружается одно изображение с использованием базового URL для удобства.

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`

В методе create происходит основная настройка. Сначала создаются и размещаются изображение и текстовый объект.

Ключевой момент — создание объекта Phaser.Time.TimerEvent. Он конфигурируется с задержкой в 4000 миллисекунд (4 секунды). Обратите внимание, что таймер создан, но еще не запущен. Он запускается вызовом this.time.addEvent(this.timedEvent).

Затем на событие клика (pointerdown) вешается обработчик. Его задача — не создавать новый таймер, а снова добавить в систему времени тот же самый объект timedEvent, что и приводит к его перезапуску.

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

        this.timedEvent = new Phaser.Time.TimerEvent({ delay: 4000 });
        this.time.addEvent(this.timedEvent);

        this.input.on('pointerdown', () =>
        {
            this.time.addEvent(this.timedEvent);
        }, this);
    }

Обновление состояния в `update`

Метод update вызывается каждый кадр. Здесь мы используем прогресс выполнения текущего таймера.

Метод this.timedEvent.getProgress() возвращает число от 0 (начало) до 1 (конец) — прогресс выполнения таймера. Это значение используется для двух целей: 1. Обновления текста на экране, чтобы визуализировать прогресс. 2. Вращения изображения. Угол поворота (setAngle) вычисляется как progress * 20, что за 4 секунды дает плавный поворот на 20 градусов.

update ()
    {
        const progress = this.timedEvent.getProgress();

        this.text.setText([
            'Click to restart the Timer',
            `Event.progress: ${progress.toString().substr(0, 4)}`
        ]);

        this.image.setAngle(progress * 20);
    }

Конфигурация и запуск игры

Стандартная конфигурация игры Phaser 3. Указывается тип рендерера, размеры холста, цвет фона, ID родительского элемента HTML и класс основной сцены.

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

const game = new Phaser.Game(config);

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

Повторное использование одного объекта TimerEvent — эффективный паттерн для реализации перезапускаемых действий. Это может быть полезно для перезарядки оружия, восстановления способностей, создания цикличной анимации или любого события, которое можно сбросить действием игрока. Для экспериментов попробуйте изменить delay в конфигурации таймера, привязать вращение или другие свойства игровых объектов к прогрессу более сложным образом или запускать/перезапускать таймер по другим событиям, например, столкновению объектов.