О чем этот пример
Работа с анимациями — ключевая часть геймдева. Phaser 3 предлагает мощный API для создания последовательностей твинов через `tweens.chain()`. Однако в его использовании есть нюанс, который может сбить с толку: почему событие `onStart` в конфигурации цепочки и отдельный слушатель `TWEEN_START` ведут себя по-разному? Эта статья разберет пример кода, демонстрирующий эту особенность, и покажет, как правильно управлять событиями в цепочках анимаций.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create() {
this.objToTween = this.add.circle(this.scale.width / 2,this.scale.height / 2,50,0xff0000);
const chain = this.tweens.chain({
tweens: [
{ targets: this.objToTween, duration: 500, props: { alpha: 0 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 1 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 0 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 1 } },
],
onStart: () => console.log('chain start!')
});
chain.on(Phaser.Tweens.Events.TWEEN_START, () => console.log('added on start listener')); // I assume this should work
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d88',
scene: Example
};
const game = new Phaser.Game(config);
Что делает пример?
В данном примере создается простая сцена, в которой красный круг поочередно исчезает и появляется четыре раза. Эта анимация реализована не отдельными твинами, а единой цепочкой (chain).
class Example extends Phaser.Scene
{
create() {
this.objToTween = this.add.circle(this.scale.width / 2,this.scale.height / 2,50,0xff0000);
Сначала в центре экрана создается графический объект — красный круг. Его позиция вычисляется динамически, относительно размеров игрового окна (this.scale.width / 2).
Создание цепочки твинов
Основная логика заключена в методе this.tweens.chain(). Этот метод принимает конфигурационный объект, который определяет массив последовательных твинов и обработчики событий.
const chain = this.tweens.chain({
tweens: [
{ targets: this.objToTween, duration: 500, props: { alpha: 0 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 1 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 0 } },
{ targets: this.objToTween, duration: 500, props: { alpha: 1 } },
],
onStart: () => console.log('chain start!')
});
Ключевые параметры:
* tweens: Массив объектов-конфигураций для каждого твина в цепочке. Здесь четыре одинаковых по длительности (500 мс) твина, которые меняют свойство alpha (прозрачность) цели (targets) — нашего красного круга — между 0 (невидимый) и 1 (полностью видимый).
* onStart: **Callback-функция**, которая вызывается один раз — в момент запуска всей цепочки. В консоль выведется сообщение 'chain start!'.
Нюанс: Событие TWEEN_START vs onStart
После создания цепочки к ее объекту добавляется еще один слушатель события. И здесь проявляется важное различие в API Phaser.
chain.on(Phaser.Tweens.Events.TWEEN_START, () => console.log('added on start listener'));
* chain.on(...): Этот метод добавляет слушатель на событие Phaser.Tweens.Events.TWEEN_START. **Важно:** Это событие генерируется не для всей цепочки, а для КАЖДОГО отдельного твина внутри нее в момент его старта. В нашем примере сообщение 'added on start listener' появится в консоли четыре раза — по разу для каждого из четырех твинов.
* onStart в конфигурации: Это callback, который срабатывает строго один раз — при запуске первого твина в цепочке (т.е. при старте всей последовательности).
Таким образом, onStart — это событие старта цепочки, а TWEEN_START — событие старта каждого ее звена. Их нельзя использовать как взаимозаменяемые.
Конфигурация игры и запуск
Код завершается стандартной для Phaser 3 конфигурацией игрового экземпляра.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d88',
scene: Example
};
const game = new Phaser.Game(config);
Здесь создается игра с размером холста 800x600 пикселей, темно-синим фоном (#2d2d88) и единственной сценой — классом Example, логика которого была описана выше.
Что попробовать дальше
Использование tweens.chain() — отличный способ создавать сложные последовательные анимации без ручного управления временными интервалами. Главный вывод: четко различайте события для всей цепочки (onStart в конфиге) и события для отдельных ее элементов (TWEEN_START). Для экспериментов попробуйте: добавить слушатель TWEEN_COMPLETE для отслеживания завершения каждого твина; изменить цепочку, чтобы твины двигали круг по экрану (`x,y); или создать вложенную цепочку, запускаемую из callback'аonComplete` другой.
