О чем этот пример
В разработке игр на Phaser часто нужно создать последовательность анимаций, где один объект плавно переходит из состояния в состояние. Ещё интереснее, когда в этой цепочке участвуют сразу несколько объектов. Класс `TweenManager` в Phaser 3 предлагает мощный метод `chain()`, который позволяет легко создавать такие последовательности. В этой статье мы разберем пример, где две рыбы по очереди выполняют синхронное движение по сложной траектории, а фон оживляется отдельными твинами. Вы научитесь строить цепочки анимаций, управлять их порядком и настраивать поведение для создания живых, динамичных сцен.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sky', 'assets/skies/seablue.png');
this.load.image('clouds', 'assets/skies/seaclouds.png');
this.load.image('waves', 'assets/skies/waves.png');
this.load.atlas('assets', 'assets/atlas/tweenparts.png', 'assets/atlas/tweenparts.json');
}
create ()
{
this.add.image(400, 300, 'sky');
const clouds = this.add.tileSprite(400, 300, 800, 600, 'clouds');
const fish1 = this.add.image(0, 700, 'assets', 'fishing_134_t');
const fish2 = this.add.image(800, 700, 'assets', 'fishing_128_t');
const waves = this.add.tileSprite(400, 400, 800, 600, 'waves');
this.tweens.add({
targets: clouds,
tilePositionX: 800,
duration: 9000,
ease: 'linear',
repeat: -1,
});
this.tweens.add({
targets: waves,
tilePositionY: 100,
duration: 2000,
ease: 'sine.inout',
yoyo: true,
repeat: -1,
onUpdate: () => {
waves.tilePositionX += 4
}
});
fish2.setFlipX(true);
this.tweens.chain({
tweens: [
{
targets: fish1,
x: 400,
y: 200,
angle: 40,
duration: 1000,
ease: 'sine.out'
},
{
targets: fish1,
x: 800,
y: 700,
angle: 80,
duration: 1000,
ease: 'sine.in'
},
{
targets: fish2,
x: 400,
y: 200,
angle: -30,
duration: 1000,
ease: 'sine.out'
},
{
targets: fish2,
x: 0,
y: 700,
angle: -60,
duration: 1000,
ease: 'sine.in'
},
],
loop: -1
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
Класс Example расширяет Phaser.Scene. В методе preload() загружаются все необходимые ресурсы для сцены: фоновое небо (sky), слой облаков (clouds), слой волн (waves) и атлас спрайтов (assets), содержащий изображения рыб.
Важный момент: загрузчик настраивается на использование удаленного базового URL от репозитория с примерами Phaser. В вашем реальном проекте вы, скорее всего, будете загружать ресурсы с вашего сервера.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sky', 'assets/skies/seablue.png');
this.load.image('clouds', 'assets/skies/seaclouds.png');
this.load.image('waves', 'assets/skies/waves.png');
this.load.atlas('assets', 'assets/atlas/tweenparts.png', 'assets/atlas/tweenparts.json');
Создание фона с бесконечной анимацией
В методе create() сначала добавляется статичный фон (sky). Затем создаются два тайловых спрайта (TileSprite) для облаков и волн. Их особенность в том, что текстуру можно "смещать" (tilePosition), создавая эффект бесконечного движения.
Для облаков создается простой твин, который циклично двигает текстуру по горизонтали. Обратите внимание на параметр repeat: -1, который делает анимацию бесконечной.
this.tweens.add({
targets: clouds,
tilePositionX: 800,
duration: 9000,
ease: 'linear',
repeat: -1,
});
Анимация волн сложнее: твин двигает текстуру по вертикали с эффектом yoyo (туда-обратно) и на каждом кадре обновления (onUpdate) дополнительно сдвигает текстуру по горизонтали, создавая диагональное движение.
this.tweens.add({
targets: waves,
tilePositionY: 100,
duration: 2000,
ease: 'sine.inout',
yoyo: true,
repeat: -1,
onUpdate: () => {
waves.tilePositionX += 4
}
});
Создание и настройка целей анимации
Две рыбы создаются из атласа как изображения (Image) и размещаются за пределами видимой области по краям экрана. Метод setFlipX(true) зеркально отражает вторую рыбу, чтобы они смотрели друг на друга, что добавляет визуальной гармонии.
const fish1 = this.add.image(0, 700, 'assets', 'fishing_134_t');
const fish2 = this.add.image(800, 700, 'assets', 'fishing_128_t');
fish2.setFlipX(true);
Построение цепочки твинов
Здесь происходит самое важное. Метод this.tweens.chain() принимает объект конфигурации с массивом tweens. Каждый элемент этого массива — это конфигурация отдельного твина, который будет выполнен строго после завершения предыдущего.
В нашем примере цепочка состоит из четырех твинов: первые два управляют fish1, вторые два — fish2. Рыбы двигаются по траектории, меняя координаты (`x,y) и угол поворота (angle). Параметрloop: -1` заставляет всю цепочку повторяться бесконечно.
this.tweens.chain({
tweens: [
{
targets: fish1,
x: 400,
y: 200,
angle: 40,
duration: 1000,
ease: 'sine.out'
},
{
targets: fish1,
x: 800,
y: 700,
angle: 80,
duration: 1000,
ease: 'sine.in'
},
// ... аналогичные твины для fish2
],
loop: -1
});
Ключевое преимущество chain() в том, что вы можете комбинировать анимации для разных объектов (targets) в одной последовательности, создавая сложные сценарные движения.
Что попробовать дальше
Метод tweens.chain() — это мощный инструмент для создания последовательных, сценарных анимаций в Phaser. Он идеально подходит для кат-сцен, сложных патрульных путей NPC или любых действий, где важна строгая очередность событий.
**Идеи для экспериментов:**
1. Добавьте в цепочку твин, который меняет scale или alpha рыб для более выразительной анимации.
2. Попробуйте использовать разные функции ease (например, 'back.out', 'elastic.inout') для каждого сегмента движения, чтобы придать ему уникальный характер.
3. Создайте цепочку, где твины для fish1 и fish2 не идут строго друг за другом, а частично перекрываются по времени (для этого нужно будет использовать несколько отдельных вызовов chain() или комбинировать с delay).
4. Свяжите анимацию фоновых волн с движением рыб, например, изменяя скорость волн в коллбеке onUpdate твина рыбы.
