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

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

Версия 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 ()
    {
        console.log('create');

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

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

        this.timedEvent = this.time.addEvent({ delay: 500, callback: this.onEvent, callbackScope: this, loop: true });
    }

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

    onEvent ()
    {
        this.image.rotation += 0.04;
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ассетов

Как и в любой сцене Phaser, мы начинаем с методов жизненного цикла. В preload загружаем необходимое изображение, устанавливая базовый URL для удобства. Ключевые свойства класса (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.time.addEvent. Объект конфигурации принимает три ключевых параметра: - delay: интервал между вызовами в миллисекундах. - callback: функция, которая будет вызвана по истечении каждого интервала. - callbackScope: контекст (this), в котором будет вызвана функция-колбэк. Без этого указания контекст будет потерян. - loop: флаг, указывающий, что событие должно повторяться бесконечно.

create ()
{
    console.log('create');

    this.image = this.add.image(400, 300, 'einstein');
    this.text = this.add.text(32, 32);
    this.timedEvent = this.time.addEvent({ delay: 500, callback: this.onEvent, callbackScope: this, loop: true });
}

Колбэк события и отслеживание прогресса

Функция onEvent — это наш колбэк, который выполняется каждые 500 мс. В данном примере она инкрементирует свойство rotation изображения, заставляя его вращаться.

onEvent ()
{
    this.image.rotation += 0.04;
}

В методе update, который вызывается на каждом кадре, мы выводим в текстовый объект текущий прогресс выполнения события. Метод getProgress() возвращает число от 0 (начало цикла) до 1 (момент вызова колбэка). Это полезно для создания плавных интерполяций или индикаторов загрузки.

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

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

Финальный шаг — создание экземпляра игры Phaser.Game с конфигурационным объектом. В нем мы указываем тип рендерера, размеры холста, цвет фона, родительский HTML-элемент и главную сцену.

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

const game = new Phaser.Game(config);

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

TimeEvent — это декларативный и эффективный способ работы со временем в Phaser. В отличие от ручного подсчета кадров в update, он позволяет создавать независимые временные линии. Для экспериментов попробуйте изменить параметр delay, чтобы ускорить или замедлить вращение. Используйте getElapsed() и getRemaining() для получения абсолютного времени. Остановите событие методом remove() или паузу через paused. Попробуйте привязать событие не к вращению, а, например, к изменению масштаба (scale) или прозрачности (alpha) спрайта.