О чем этот пример
Визуальные эффекты, такие как шлейфы или эхо-следы за курсором, оживляют игровой мир. Их можно создавать не только через частицы, но и через прямое управление геометрией. В этом примере мы разберем, как с помощью метода `Phaser.Geom.Ellipse.CopyFrom()` и простого массива объектов создать динамический след из увеличивающихся эллипсов, следующих за указателем мыши. Этот подход демонстрирует основы работы с геометрическими объектами, их копированием и анимацией в реальном времени, что полезно для создания кастомных эффектов, траекторий или областей влияния.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x00aaaa } });
const pointerEllipse = new Phaser.Geom.Ellipse(400, 300, 400, 300);
const ellipses = [];
for (let k = 0; k < 30; k++)
{
ellipses.push(new Phaser.Geom.Ellipse(0, 0, 0, 0));
}
let i = 0;
this.input.on('pointermove', pointer =>
{
pointerEllipse.setTo(pointer.x, pointer.y, pointer.x / 8, pointer.y / 6);
Phaser.Geom.Ellipse.CopyFrom(pointerEllipse, ellipses[i]);
i = (i + 1) % ellipses.length;
graphics.clear();
graphics.strokeEllipseShape(pointerEllipse);
for (let j = 0; j < ellipses.length; j++)
{
ellipses[j].width *= 1.1;
ellipses[j].height *= 1.1;
graphics.strokeEllipseShape(ellipses[j]);
}
});
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и структур данных
В начале метода create() инициализируются все необходимые объекты. Ключевых элемента три: объект Graphics для рисования, главный эллипс, привязанный к курсору, и массив для хранения истории эллипсов.
const graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x00aaaa } });
Здесь создается объект graphics с тонкой бирюзовой линией для отрисовки контуров.
const pointerEllipse = new Phaser.Geom.Ellipse(400, 300, 400, 300);
const ellipses = [];
for (let k = 0; k < 30; k++)
{
ellipses.push(new Phaser.Geom.Ellipse(0, 0, 0, 0));
}
pointerEllipse — это основной эллипс, который будет повторять положение курсора. Изначально он создается в центре с большими размерами. Массив ellipses из 30 элементов — это "память" для следа. Каждый элемент массива предварительно создается как пустой эллипс в точке (0,0) с нулевыми размерами. Это необходимо для последующего заполнения их данными через копирование.
Обработка движения курсора и копирование геометрии
Логика эффекта активируется при каждом движении мыши. Сначала обновляется главный эллипс, затем его состояние копируется в один из элементов массива по циклическому индексу.
this.input.on('pointermove', pointer =>
{
pointerEllipse.setTo(pointer.x, pointer.y, pointer.x / 8, pointer.y / 6);
Phaser.Geom.Ellipse.CopyFrom(pointerEllipse, ellipses[i]);
i = (i + 1) % ellipses.length;
Метод pointerEllipse.setTo() мгновенно перезаписывает координаты и размеры эллипса, основываясь на текущей позиции курсора (pointer.x, pointer.y). Ширина и высота зависят от координат, что создает интересную динамику формы.
Статический метод Phaser.Geom.Ellipse.CopyFrom(source, dest) является сердцем примера. Он копирует свойства эллипса-источника (pointerEllipse) в эллипс-назначение (ellipses[i]). Это эффективнее, чем создавать новый объект new Ellipse(...), так как переиспользует уже созданные экземпляры из массива.
Переменная `iвыступает в роли циклического индекса. Операция(i + 1) % ellipses.length` гарантирует, что после последнего элемента массива индекс снова станет равным 0, создавая кольцевой буфер.
Очистка, трансформация и отрисовка следа
После копирования данных необходимо очистить холст и перерисовать все эллипсы, включая основной и все эллипсы из массива. Каждый эллипс в массиве перед отрисовкой немного увеличивается.
graphics.clear();
graphics.strokeEllipseShape(pointerEllipse);
for (let j = 0; j < ellipses.length; j++)
{
ellipses[j].width *= 1.1;
ellipses[j].height *= 1.1;
graphics.strokeEllipseShape(ellipses[j]);
}
Вызов graphics.clear() стирает все, что было нарисовано на предыдущем кадре. Это важно для анимации в реальном времени.
Затем отрисовывается контур главного эллипса с помощью graphics.strokeEllipseShape().
В цикле по массиву ellipses каждый эллипс трансформируется: его ширина и высота умножаются на 1.1. Это простое действие создает иллюзию постепенного роста и "растворения" эллипсов, которые были скопированы ранее. После увеличения эллипс также рисуется на холсте. Поскольку отрисовка происходит после увеличения размеров, самый старый эллипс в буфере оказывается самым большим.
Что попробовать дальше
Метод Phaser.Geom.Ellipse.CopyFrom() предоставляет производительный способ дублирования геометрических данных, идеально подходящий для создания эффектов на основе истории объектов, как в примере со следом. Для экспериментов попробуйте изменить правило увеличения размеров (например, добавлять фиксированное значение), менять цвет или прозрачность (graphics.lineStyle) у старых эллипсов или реализовать подобный след для других геометрических объектов, используя аналогичные методы копирования, например, Phaser.Geom.Rectangle.CopyFrom().
