О чем этот пример
В разработке игр часто требуется не просто запустить действие по таймеру, а сделать это с небольшой задержкой или начать с определенного момента. Встроенный менеджер времени Phaser предоставляет для этого мощный инструмент – параметр `startAt` в объекте события. Эта статья покажет, как использовать `startAt` для создания более гибких и контролируемых временных интервалов в вашей игре, например, для анимаций, спавна врагов или активации бонусов не с нуля, а с уже пройденной части времени.
Версия 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);
// A 10 second delay, but the first time it begins it'll start 5 seconds in, then on repeat will repeat for the full 10 seconds
this.timedEvent = this.time.addEvent({ delay: 10000, callback: this.onEvent, callbackScope: this, repeat: 1, startAt: 5000 });
}
update ()
{
this.text.setText(`Event.progress: ${this.timedEvent.getProgress().toString().substr(0, 4)}\nEvent.repeatCount: ${this.timedEvent.repeatCount}`);
}
onEvent ()
{
this.image.scaleX *= 0.75;
this.image.scaleY *= 0.75;
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);
Создание события с отложенным стартом
В методе create() сцены создается временное событие с помощью this.time.addEvent(). Ключевой параметр здесь – startAt: 5000.
this.timedEvent = this.time.addEvent({ delay: 10000, callback: this.onEvent, callbackScope: this, repeat: 1, startAt: 5000 });
Конфигурация объекта события:
* delay: 10000 – общая длительность одного цикла события составляет 10 секунд (10000 миллисекунд).
* callback: this.onEvent – функция, которая будет вызвана по завершении каждого цикла.
* callbackScope: this – контекст, в котором будет выполнена функция обратного вызова (обычно сама сцена).
* repeat: 1 – событие повторится один раз. Вместе с первоначальным запуском это означает, что callback выполнится дважды.
* startAt: 5000 – **самый важный параметр**. Он указывает, что первый запуск таймера начнется не с 0, а с отметки в 5 секунд. Таким образом, первый вызов onEvent произойдет не через 10, а через 5 секунд после создания события. Все последующие повторения (repeat) будут уже работать на полном интервале в 10 секунд.
Отслеживание прогресса события
Чтобы визуализировать работу таймера, в методе update() выводится текстовое поле с двумя значениями.
this.text.setText(`Event.progress: ${this.timedEvent.getProgress().toString().substr(0, 4)}\nEvent.repeatCount: ${this.timedEvent.repeatCount}`);
* `this.timedEvent.getProgress()` – метод, возвращающий число от 0 до 1, которое показывает, какая часть текущего цикла события уже прошла. При `startAt: 5000` и `delay: 10000` начальное значение прогресса будет равно 0.5 (5/10). Метод `substr(0,4)` используется для округления значения при выводе.
* `this.timedEvent.repeatCount` – свойство, которое показывает, сколько раз событие уже было повторено. Уменьшается на 1 с каждым завершением цикла.
Этот интерфейс позволяет вам в реальном времени видеть, как быстро движется прогресс и сколько повторений осталось.
Действие по завершению цикла
Функция onEvent – это callback, который срабатывает каждый раз, когда таймер события достигает конца своего цикла (прогресс становится равным 1).
onEvent ()
{
this.image.scaleX *= 0.75;
this.image.scaleY *= 0.75;
this.image.rotation += 0.04;
}
При каждом вызове функция: 1. Уменьшает масштаб изображения по осям X и Y на 25% (умножая текущий масштаб на 0.75). 2. Поворачивает изображение на небольшой угол (0.04 радиана).
Из-за параметра startAt первое такое изменение произойдет не через 10, а через 5 секунд после загрузки сцены, что и демонстрирует практическую пользу этого параметра для нестандартного старта анимаций.
Практическое применение startAt в играх
Параметр startAt — это не просто особенность API, а инструмент для решения конкретных игровых задач.
// Бонус, который активируется через 3 секунды после появления, но исчезнет через 10.
this.time.addEvent({ delay: 10000, callback: this.deactivateBonus, startAt: 3000 });
// Волна врагов, которая начинается не сразу, а когда игрок достигнет определенной точки.
// Таймер создания врагов запускается с прогрессом 0.8, чтобы первая группа появилась почти мгновенно.
this.waveTimer = this.time.addEvent({ delay: 5000, callback: this.spawnEnemy, repeat: 9, startAt: 4000 });
* **Постепенное начало:** Можно создать событие с большим delay (например, 30 секунд для смены времени суток), но установить startAt в 25 секунд. Это создаст эффект, будто цикл уже давно идет, и изменение наступит очень скоро.
* **Синхронизация:** Если у вас несколько событий должны сработать в разное время, но вы хотите создать их одновременно в коде инициализации уровня, startAt поможет задать им разное время первого срабатывания, сохранив общую длительность цикла.
Что попробовать дальше
Параметр startAt в Phaser.Time.TimerEvent — это простой, но мощный способ получить точный контроль над временем в вашей игре. Он позволяет создавать события, которые начинаются не с чистого листа, а с уже пройденной части своего цикла, что открывает двери для более сложных и интересных временных механик.
**Идеи для экспериментов:**
1. Создайте таймер обратного отсчета, который изначально показывает не полное время, а только последние 5 секунд.
2. Реализуйте систему сезонов (лето, осень, зима, весна), где смена происходит циклично, но игра начинается, например, с середины осени.
3. Сделайте «заряжающуюся» атаку, визуальный индикатор которой (progress bar) при активации сразу заполнен на 50%.
