О чем этот пример
Создание игровых сцен часто требует точного управления временем: когда должен появиться враг, активироваться бонус или начаться анимация. Phaser предлагает для этого мощный инструмент — Timeline. Эта статья покажет, как использовать Timeline для организации последовательных и отложенных событий в вашей игре, заменяя громоздкие цепочки `setTimeout` или ручной учёт времени. Вы научитесь создавать временные линии с событиями, которые могут запускаться в абсолютное время или с задержкой относительно предыдущего шага. Это особенно полезно для скриптовых сцен, туториалов, волн противников или сложных анимационных последовательностей.
Версия 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');
// The first even will start at a random time between 1 and 3 seconds
// All events after that will start 1 second after the previous one
const timeline = this.add.timeline([
{
at: Phaser.Math.Between(1000, 3000),
run: () => { this.add.sprite(100, 300, 'timeline', 'bat') }
},
{
from: 1000,
run: () => { this.add.sprite(200, 300, 'timeline', 'zombie') }
},
{
from: 1000,
run: () => { this.add.sprite(300, 300, 'timeline', 'spider') }
},
{
from: 1000,
run: () => { this.add.sprite(400, 300, 'timeline', 'pumpkin') }
},
{
from: 1000,
run: () => { this.add.sprite(500, 300, 'timeline', 'spider') }
},
{
from: 1000,
run: () => { this.add.sprite(600, 300, 'timeline', 'zombie') }
},
{
from: 1000,
run: () => { this.add.sprite(700, 300, 'timeline', 'bat') }
}
]);
timeline.play();
}
}
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, который управляет очередностью и временем выполнения функций (событий). В отличие от одиночных таймеров, таймлайн позволяет описать целую цепочку действий в декларативном стиле: что и когда должно произойти.
Основные преимущества: * **Читаемость:** Вся временная логика собрана в одном месте. * **Контроль:** Легко запустить, приостановить или перезапустить всю последовательность. * **Гибкость:** События можно задавать как в абсолютном времени от начала таймлайна, так и относительно друг друга.
В предоставленном примере Timeline используется для появления ряда спрайтов (летучая мышь, зомби, паук и т.д.) на экране с заданными интервалами.
Создание и конфигурация Timeline
Таймлайн создаётся с помощью метода this.add.timeline(), который принимает массив объектов-событий. Каждое событие — это объект с двумя основными свойствами: at или from и run.
const timeline = this.add.timeline([
// Событие 1
{
at: 2000,
run: () => { console.log('Случилось через 2 секунды') }
},
// Событие 2
{
from: 1000,
run: () => { console.log('Случилось через 1 секунду после предыдущего') }
}
]);
После создания таймлайн нужно явно запустить методом play().
timeline.play();
Ключевые свойства событий: `at` и `from`
Отличие между at и from — основа гибкости Timeline.
* **at** — задаёт абсолютное время в миллисекундах относительно момента запуска таймлайна (play()). В примере первое событие использует Phaser.Math.Between(1000, 3000), что означает его запуск через случайное время от 1 до 3 секунд после play().
{
at: Phaser.Math.Between(1000, 3000),
run: () => { this.add.sprite(100, 300, 'timeline', 'bat') }
}
* **from** — задаёт относительную задержку в миллисекундах от **предыдущего** события в цепочке. Все события, кроме первого, в примере используют from: 1000. Это значит, что каждое следующее существо появится ровно через 1 секунду после появления предыдущего, независимо от случайной задержки первого.
{
from: 1000, // Запустится через 1 секунду после появления зомби
run: () => { this.add.sprite(300, 300, 'timeline', 'spider') }
}
Запуск кода: свойство `run`
Свойство run ожидает функцию, которая выполнится в заданный момент времени. Это может быть любая логика вашей игры.
В примере внутри run создаются спрайты с помощью this.add.sprite(). Обратите внимание на параметры:
* Координаты X и Y (меняются для каждого спрайта).
* Ключ атласа 'timeline'.
* Имя кадра из этого атласа ('bat', 'zombie', 'spider', 'pumpkin').
run: () => {
// Добавляем спрайт зомби на координаты (200, 300)
// Используем кадр 'zombie' из атласа 'timeline'
this.add.sprite(200, 300, 'timeline', 'zombie');
}
Именно здесь происходит "магия" появления объектов в игре в запланированный момент.
Полный разбор примера из исходника
Давайте пройдёмся по коду примера шаг за шагом, чтобы закрепить понимание.
1. **Preload:** Загружаются ресурсы — атлас с изображениями существ и фон.
2. **Create:**
* Добавляется фон.
* Создаётся Timeline с массивом из 7 событий.
* **Событие 1:** Через случайное время (1-3 сек.) появляется спрайт летучей мыши (bat) на позиции (100, 300).
* **События 2-7:** Каждое следующее существо появляется на своей позиции (X увеличивается на 100) ровно через 1 секунду (from: 1000) после появления предыдущего. Порядок: Зомби, Паук, Тыква, Паук, Зомби, Летучая мышь.
3. **Запуск:** Цепочка событий активируется вызовом timeline.play().
В результате, после запуска сцены, вы увидите, как существа одно за другим появляются на линии с равными интервалами, образуя процессию. Случайная задержка первого события добавляет элемент неожиданности в самом начале.
Что попробовать дальше
Timeline в Phaser — это элегантное и мощное решение для планирования действий во времени. Он избавляет код от хаоса вложенных таймеров и делает временные скрипты наглядными.
**Идеи для экспериментов:**
* Создайте таймлайн для вступительной кат-сцены, где диалоги и анимации персонажей следуют друг за другом.
* Используйте timeline.pause() и timeline.resume() для управления скриптовой сценой по клику игрока.
* Организуйте волны противников в аркадной игре: первая волна — через 5 секунд, вторая — через 10 секунд после окончания первой.
* Комбинируйте at и from в одной цепочке для создания сложных ритмических паттернов.
