О чем этот пример
При разработке игр часто возникает необходимость запускать различные события в определённой последовательности и с заданными интервалами. Например, появление врагов в рейдовом бою, активация анимаций в кат-сцене или управление игровыми состояниями. Ручное управление таймерами и вызовами может превратиться в хаос. В Phaser для решения этой задачи есть удобный инструмент — `Timeline`. Этот объект позволяет декларативно описывать последовательность событий во времени и управлять их воспроизведением. В этой статье мы разберём пример использования таймлайнов для создания динамичной сцены с появлением врагов.
Версия 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');
}
create ()
{
this.add.image(400, 300, 'bg');
this.graphics = this.add.graphics();
this.add.text(10, 10, 'Click to play timeline', { font: '16px Courier', fill: '#ffffff' });
const timeline = this.add.timeline();
// Here we'll create 2 events, one to start the boss fight, and one to add a spider
timeline.on('BOSS_FIGHT_START', () => {
const boss = this.add.sprite(400, 300, 'timeline', 'pumpkin').setDepth(100).setAngle(-32);
this.tweens.add({
targets: boss,
scale: 2,
angle: 32,
duration: 1500,
ease: 'quad.inout',
yoyo: true,
repeat: -1
});
});
this.spiders = [];
timeline.on('ADD_SPIDER', () => {
const x = Phaser.Math.Between(50, 750);
const y = Phaser.Math.Between(200, 500);
const spider = this.add.sprite(x, -200, 'timeline', 'spider');
this.tweens.add({
targets: spider,
y,
duration: 2000,
ease: 'bounce.out',
yoyo: true,
hold: 2000,
repeat: -1,
repeatDelay: 1000
});
this.spiders.push(spider);
});
// Now we sequence the events
timeline.add([
{
at: 0,
event: 'ADD_SPIDER'
},
{
at: 500,
event: 'ADD_SPIDER'
},
{
at: 1000,
event: 'ADD_SPIDER'
},
{
at: 1500,
event: 'BOSS_FIGHT_START'
},
{
at: 2000,
event: 'ADD_SPIDER'
},
{
at: 2500,
event: 'ADD_SPIDER'
},
{
at: 3000,
event: 'ADD_SPIDER'
}
]);
this.input.once('pointerdown', () => {
timeline.play();
});
}
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0xffffff, 0.5);
this.spiders.forEach(spider => {
this.graphics.lineBetween(spider.x, 0, spider.x, spider.y);
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#020286',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Инициализация Timeline и загрузка ресурсов
Перед созданием таймлайна необходимо загрузить графические ресурсы, которые будут использоваться в событиях. В методе preload загружаем атлас спрайтов 'timeline' и фоновое изображение.
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');
}
В методе create создаём сам объект таймлайна. Это делается с помощью фабричного метода this.add.timeline().
const timeline = this.add.timeline();
Определение событий таймлайна
События таймлайна — это именованные действия, которые будут выполняться в заданные моменты времени. Каждое событие определяется с помощью метода timeline.on(), который принимает имя события и коллбэк-функцию.
В примере определены два события: BOSS_FIGHT_START и ADD_SPIDER.
Событие BOSS_FIGHT_START создаёт спрайт босса (тыкву) в центре экрана и запускает для него бесконечную твин-анимацию, которая меняет масштаб и угол поворота.
timeline.on('BOSS_FIGHT_START', () => {
const boss = this.add.sprite(400, 300, 'timeline', 'pumpkin').setDepth(100).setAngle(-32);
this.tweens.add({
targets: boss,
scale: 2,
angle: 32,
duration: 1500,
ease: 'quad.inout',
yoyo: true,
repeat: -1
});
});
Событие ADD_SPIDER создаёт спрайт паука в случайной позиции по оси X в верхней части экрана и запускает для него твин, который перемещает паука вниз с эффектом отскока (bounce.out). Созданный спрайт сохраняется в массив this.spiders для дальнейшего использования в отрисовке.
timeline.on('ADD_SPIDER', () => {
const x = Phaser.Math.Between(50, 750);
const y = Phaser.Math.Between(200, 500);
const spider = this.add.sprite(x, -200, 'timeline', 'spider');
this.tweens.add({
targets: spider,
y,
duration: 2000,
ease: 'bounce.out',
yoyo: true,
hold: 2000,
repeat: -1,
repeatDelay: 1000
});
this.spiders.push(spider);
});
Создание последовательности событий
После определения событий необходимо создать их последовательность. Для этого используется метод timeline.add(), который принимает массив объектов с описанием событий.
Каждый объект в массиве имеет два свойства:
* at — время в миллисекундах, когда должно сработать событие относительно начала воспроизведения таймлайна.
* event — строка с именем события, которое нужно вызвать.
timeline.add([
{ at: 0, event: 'ADD_SPIDER' },
{ at: 500, event: 'ADD_SPIDER' },
{ at: 1000, event: 'ADD_SPIDER' },
{ at: 1500, event: 'BOSS_FIGHT_START' },
{ at: 2000, event: 'ADD_SPIDER' },
{ at: 2500, event: 'ADD_SPIDER' },
{ at: 3000, event: 'ADD_SPIDER' }
]);
В данном примере создаётся паттерн: три паука появляются с интервалом в 500 мс, затем запускается босс, и ещё три паука появляются после него.
Запуск всего таймлайна происходит по клику мыши с помощью метода timeline.play().
this.input.once('pointerdown', () => {
timeline.play();
});
Визуализация и отладка
Для наглядной демонстрации позиций объектов в методе update реализована простая визуализация. Очищается графический объект this.graphics и для каждого паука из массива this.spiders рисуется вертикальная линия от верхнего края экрана до его текущей позиции.
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0xffffff, 0.5);
this.spiders.forEach(spider => {
this.graphics.lineBetween(spider.x, 0, spider.x, spider.y);
});
}
Этот приём полезен для отладки позиционирования и траекторий движения игровых объектов.
Что попробовать дальше
Timeline в Phaser — это мощный и гибкий инструмент для планирования событий. Он отлично подходит для создания скриптованных сцен, волн врагов, кат-сцен и сложных анимационных последовательностей. Код становится чище и понятнее, так как логика времени отделена от логики событий.
Для экспериментов попробуйте:
* Изменить временные интервалы в массиве событий.
* Добавить новые типы событий (например, ADD_BAT или CHANGE_BACKGROUND).
* Использовать метод timeline.seek() для перемотки таймлайна.
* Связать запуск таймлайна не с кликом, а с другим игровым событием, например, достижением определённой точки на карте.
