О чем этот пример
Визуальные эффекты, такие как шлейфы или следы за движущимися объектами, — мощный инструмент для оживления игровой графики. В этой статье мы разберем пример, который создает красочный след из кругов, движущихся по ромбовидной траектории. Вы научитесь использовать цепочки tween для плавного движения, управлять историей координат и динамически отрисовывать графику с радужными цветами. Эти техники применимы для создания эффектов магии, движения планет или визуализации траекторий.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
start;
hsv;
graphics;
size = 64 * 2;
historyX = Array(this.size);
historyY = Array(this.size);
index = 0;
create ()
{
this.graphics = this.add.graphics();
// diamond shape from 400x300 =
// 400 x 100 (top middle)
// 100 x 300 (left)
// 700 x 300 (right)
// 400 x 500 (bottom)
this.start = new Phaser.Math.Vector2(400, 100);
const top = new Phaser.Math.Vector2(400, 100);
const left = new Phaser.Math.Vector2(100, 300);
const right = new Phaser.Math.Vector2(700, 300);
const bottom = new Phaser.Math.Vector2(400, 500);
this.hsv = Phaser.Display.Color.HSVColorWheel();
this.tweens.chain({
targets: this.start,
loop: -1,
duration: 2000,
tweens: [
{ x: left.x, y: left.y, ease: 'Sine.easeOut' },
{ x: bottom.x, y: bottom.y, ease: 'Sine.easeInOut' },
{ x: right.x, y: right.y, ease: 'Sine.easeInOut' },
{ x: top.x, y: top.y, ease: 'Sine.easeIn' }
]
});
// Fill the history array
for (let i = 0; i < this.size; i++)
{
this.historyX[i] = this.start.x;
this.historyY[i] = this.start.y;
}
}
update ()
{
this.historyX[this.index] = this.start.x;
this.historyY[this.index] = this.start.y;
this.index++;
if (this.index === this.size)
{
this.index = 0;
}
this.graphics.clear();
for (let i = 0; i < 64; i++)
{
this.graphics.fillStyle(this.hsv[i * 2].color, 1);
this.graphics.fillCircle(this.historyX[i * 2], this.historyY[i * 2], 64);
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.CANVAS,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и определение траектории
В методе create() инициализируются основные компоненты: объект Graphics для рисования и массив точек для хранения истории позиций. Траектория движения задается четырьмя точками, образующими ромб. Создается вектор start, который будет анимироваться.
this.graphics = this.add.graphics();
const top = new Phaser.Math.Vector2(400, 100);
const left = new Phaser.Math.Vector2(100, 300);
const right = new Phaser.Math.Vector2(700, 300);
const bottom = new Phaser.Math.Vector2(400, 500);
Также генерируется цветовое колесо HSV для получения радужной палитры и заполняется массив истории начальными координатами, чтобы избежать пустых значений при первом обновлении.
Настройка цепочки анимаций с помощью Tweens
Для плавного движения точки по ромбу используется this.tweens.chain(). Этот метод позволяет объединить несколько tween-анимаций в последовательную цепочку. Каждый tween перемещает вектор start к одной из вершин ромба с заданной функцией плавности (easing). Параметр loop: -1 обеспечивает бесконечное повторение цепочки.
this.tweens.chain({
targets: this.start,
loop: -1,
duration: 2000,
tweens: [
{ x: left.x, y: left.y, ease: 'Sine.easeOut' },
{ x: bottom.x, y: bottom.y, ease: 'Sine.easeInOut' },
{ x: right.x, y: right.y, ease: 'Sine.easeInOut' },
{ x: top.x, y: top.y, ease: 'Sine.easeIn' }
]
});
Использование разных функций ease (например, Sine.easeOut и Sine.easeIn) делает движение более естественным, имитируя ускорение и замедление.
Обновление истории позиций в реальном времени
В методе update() система постоянно обновляет историю координат. Текущие позиции вектора start записываются в массивы historyX и historyY по индексу index. Индекс увеличивается каждый кадр и сбрасывается при достижении размера массива, создавая кольцевой буфер. Это эффективно хранит последние 128 позиций.
this.historyX[this.index] = this.start.x;
this.historyY[this.index] = this.start.y;
this.index++;
if (this.index === this.size) {
this.index = 0;
}
Такой подход позволяет отслеживать траекторию движения без бесконечного роста массивов.
Динамическая отрисовка радужного следа
После обновления истории происходит отрисовка. Сначала вызывается this.graphics.clear(), чтобы удалить графику предыдущего кадра. Затем в цикле рисуются 64 круга, каждый со своим цветом из HSV-палитры. Координаты берутся из истории с шагом 2, что создает разреженный, но равномерный след. Радиус кругов фиксирован (64 пикселя).
this.graphics.clear();
for (let i = 0; i < 64; i++) {
this.graphics.fillStyle(this.hsv[i * 2].color, 1);
this.graphics.fillCircle(this.historyX[i * 2], this.historyY[i * 2], 64);
}
Использование fillStyle с цветом из this.hsv и fillCircle для отрисовки создает яркий градиентный эффект вдоль траектории.
Что попробовать дальше
Комбинируя tween цепочки, историю позиций и динамическую графику, вы можете создавать сложные визуальные эффекты с минимальным кодом. Для экспериментов попробуйте изменить форму траектории, добавить больше точек в историю, варьировать размер или прозрачность кругов, либо привязать эффект к движению игрового персонажа. Это откроет возможности для визуализации заклинаний, следов скорости или абстрактных фонов.
