О чем этот пример
В разработке игр часто требуется запускать последовательность событий: перемещение объектов, включение визуальных эффектов и запуск систем частиц с определенными задержками. Обычно для этого используется множество таймеров и твинов, что быстро превращает код в запутанный лабиринт. Timeline в Phaser решает эту проблему, позволяя описывать сложные сцены с временными метками в одном месте. Эта статья покажет, как с помощью `Timeline` и метода `repeat()` создать зацикленную сцену с плавной анимацией, эффектами свечения и частицами, управляемыми по расписанию.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('timeline', 'assets/atlas/timeline.png', 'assets/atlas/timeline.json');
this.load.image('bg', 'assets/skies/spookysky.jpg');
this.load.atlas('flares', 'assets/particles/flares.png', 'assets/particles/flares.json');
}
create ()
{
this.add.image(400, 300, 'bg');
const crystalball = this.add.sprite(400, 800, 'timeline', 'crystalball');
crystalball.enableFilters();
const glowFX = crystalball.filters.internal.addGlow();
const emitter = this.add.particles(400, 300, 'flares', {
frame: 'white',
blendMode: 'ADD',
lifespan: 1200,
gravityX: 100,
gravityY: -100,
scale: { start: 0.3, end: 0 },
emitting: false
});
emitter.addEmitZone({ source: new Phaser.Geom.Circle(0, -20, 90) });
const timeline = this.add.timeline([
{
at: 0,
run: () => {
glowFX.setActive(false);
glowFX.outerStrength = 0;
glowFX.innerStrength = 0;
},
tween: {
targets: crystalball,
y: 300,
ease: 'sine.in',
duration: 750
}
},
{
at: 1000,
loop: () => {
emitter.gravityX *= -1;
},
run: () => {
glowFX.setActive(true);
emitter.start();
},
tween: {
targets: glowFX,
outerStrength: 16,
innerStrength: 8,
ease: 'sine.in',
yoyo: true,
duration: 500,
repeat: 3
}
},
{
at: 4000,
run: () => {
emitter.stop();
},
tween: {
targets: crystalball,
y: 800,
ease: 'sine.in',
duration: 500
}
},
{
at: 5000,
stop: true
}
]);
// The `repeat` method accepts a positive value or undefined, true and a negative number values to go on indefinitely.
timeline.repeat().play();
// This is the same as setting the amount of times it should loop to 0.
// timeline.repeat(false);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Timeline и зачем он нужен
Timeline — это мощный инструмент Phaser для управления временной шкалой событий. Вместо того чтобы вручную настраивать setTimeout или цепочки промисов для последовательных или параллельных действий, вы описываете всю последовательность в виде массива объектов. Каждый объект определяет, что должно произойти в определенный момент времени (at).
Внутри каждого события вы можете:
- Запускать функции (run).
- Запускать твины (tween).
- Выполнять код в начале каждой итерации цикла (loop).
- Останавливать всю шкалу (stop).
Это делает код предсказуемым, легко читаемым и изменяемым. Вся логика анимации собрана в одном месте.
Разбор структуры Timeline в примере
В предоставленном примере шкала состоит из четырех ключевых событий, разнесенных во времени. Давайте посмотрим на их назначение.
const timeline = this.add.timeline([
{
at: 0,
run: () => { /* ... */ },
tween: { /* ... */ }
},
// ... другие события
]);
**Событие 1 (0 мс):** Инициализация. Отключается эффект свечения (glowFX) и запускается твин, поднимающий кристальный шар (crystalball) с нижней части экрана в центр.
**Событие 2 (1000 мс):** Основное действие. Включается свечение, запускается эмиттер частиц (emitter.start()). Твин для эффекта свечения делает его пульсирующим (yoyo: true, repeat: 3). Функция loop инвертирует гравитацию по оси X каждый раз, когда Timeline повторяется, создавая интересную динамику для частиц.
**Событие 3 (4000 мс):** Завершение активной фазы. Эмиттер частиц останавливается (emitter.stop()), и шар начинает опускаться вниз.
**Событие 4 (5000 мс):** Полная остановка. Параметр stop: true указывает Timeline остановиться. Однако, как мы увидим далее, метод repeat() перезапускает всю шкалу, делая эту остановку временной.
Сила метода repeat()
Ключ к созданию бесконечной или повторяющейся сцены лежит в методе timeline.repeat(). После создания шкалы ее нужно не только запустить (play()), но и настроить повторение.
timeline.repeat().play();
Вызов repeat() без аргументов (или со значением -1) делает шкалу бесконечно повторяющейся. После того как Timeline доходит до последнего события (в нашем случае — до остановки в 5000 мс), он автоматически начинает проигрываться заново с нулевой отметки.
Это поведение можно контролировать:
- timeline.repeat(3) — повторит последовательность 3 раза.
- timeline.repeat(0) или timeline.repeat(false) — отключит повторение, и шкала остановится после первого проигрыша.
В нашем примере бесконечное повторение создает циклическую анимацию: шар поднимается, светится с частицами, опускается — и все начинается снова. Функция loop в событии на 1000 мс выполняется каждый раз при *повторении* всей шкалы, меняя направление гравитации частиц и добавляя вариативность.
Практические советы по работе с Timeline
1. **Планирование:** Перед написанием кода набросайте временную шкалу на бумаге. Отметьте ключевые моменты (задержки at) и действия в них.
2. **Отладка:** Используйте run для вывода в консоль и отслеживания, какое событие сейчас выполняется.
run: () => { console.log('Достигнута метка 1000мс'); }
3. **Комбинация действий:** В одном событии можно одновременно выполнить run, tween и loop. Они запустятся параллельно в указанный момент at.
4. **Перезапуск сбросом:** Если вам нужно сбросить состояние объектов (например, позицию спрайта) перед каждым повторением, добавьте соответствующую логику в функцию run самого первого события (с at: 0).
Что попробовать дальше
Timeline — это идеальный инструмент для создания скриптованных кат-сцен, циклических заставок, сложных анимаций UI или поведения боссов в играх. Он превращает хаос из таймеров в четкий, декларативный план. Для экспериментов попробуйте: изменить порядок и задержки событий, добавить новые твины для масштабирования или цвета, заменить эмиттер частиц на другой визуальный эффект или использовать repeat(N) для создания точного количества циклов атаки босса.
