О чем этот пример

В разработке игр часто требуется визуализировать движение: следы за снарядом, шлейф за персонажем или, как в нашем примере, плавный след за курсором мыши. Эта статья покажет, как эффективно использовать объекты `Phaser.Math.Vector2` и их метод `copy()` для создания такого эффекта без лишнего потребления памяти. Вы научитесь переиспользовать объекты в пуле, что критически важно для оптимизации производительности в играх с большим количеством визуальных частиц.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const graphics = this.add.graphics({ fillStyle: { color: 0x2266aa } });

        const points = [];

        for (let k = 0; k < 150; k++)
        {
            points.push(new Phaser.Math.Vector2());
        }

        let i = 0;

        this.input.on('pointermove', pointer =>
        {
            // const vector2 = points[i];
            points[i].copy(pointer);

            i = (i + 1) % points.length;

            graphics.clear();

            graphics.fillPointShape(pointer, 25);

            for (let j = 0; j < points.length; j++)
            {
                points[j].x += 4 + Math.abs(j - 75) / 15;

                graphics.fillPointShape(points[j], 25);
            }
        });
    }
}

const config = {
    width: 800,
    height: 600,
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Суть метода copy()

Метод copy() объекта Phaser.Math.Vector2 копирует координаты (свойства `xиy) из одного вектора в другой. Это гораздо эффективнее, чем постоянно создавать новые объектыnew Phaser.Math.Vector2(x, y)` при каждом движении курсора, что приводит к частым операциям сборки мусора и падению FPS.

Вместо создания нового объекта, мы берём уже существующий вектор из заранее созданного массива и обновляем его данные, используя вектор-источник (в данном случае — объект pointer).

Подготовка: пул точек и слушатель события

Код начинает работу с создания графического контекста graphics для отрисовки и массива points, который выступает в роли пула (набора заранее созданных объектов). Этот массив заполняется 150 "пустыми" векторами.

Затем создаётся слушатель события движения курсора pointermove. Переменная `i` используется как индекс текущего вектора в пуле, который будет обновлён следующим.

const graphics = this.add.graphics({ fillStyle: { color: 0x2266aa } });
const points = [];
for (let k = 0; k < 150; k++) {
    points.push(new Phaser.Math.Vector2());
}
let i = 0;
this.input.on('pointermove', pointer => {
    // ... логика обработки
});

Обновление точек и создание шлейфа

Внутри обработчика события происходит магия. Сначала координаты курсора копируются в текущий вектор из пула.

points[i].copy(pointer);

Затем индекс `iсдвигается по кругу с помощью операции взятия остатка от деления (%`). Это гарантирует, что когда мы дойдём до конца массива из 150 элементов, следующий индекс снова станет равным 0. Таким образом, мы постоянно перезаписываем самую "старую" точку в шлейфе.

i = (i + 1) % points.length;

Перед отрисовкой нового кадра холст очищается, и отрисовывается большая точка в текущей позиции курсора.

graphics.clear();
graphics.fillPointShape(pointer, 25);

Анимация шлейфа: движение и отрисовка

Чтобы точки шлейфа не стояли на месте, а плавно двигались вправо, для каждой точки в массиве применяется смещение по оси X. Смещение зависит от индекса точки `j`, создавая волнообразный эффект: точки ближе к центру шлейфа (индекс 75) двигаются медленнее, чем точки по краям.

После обновления позиции каждая точка отрисовывается.

for (let j = 0; j < points.length; j++) {
    points[j].x += 4 + Math.abs(j - 75) / 15;
    graphics.fillPointShape(points[j], 25);
}

Ключевой момент: мы изменяем координаты `xу тех же самых объектов векторов, которые хранятся в пулеpoints`. Новые объекты не создаются, что и обеспечивает высокую производительность.

Что попробовать дальше

Использование пула объектов Vector2 и метода copy() — это фундаментальный паттерн оптимизации для создания динамических визуальных эффектов в Phaser. Попробуйте изменить формулу смещения (points[j].x += ...), чтобы шлейф улетал вверх, закручивался по спирали или реагировал на скорость движения курсора. Экспериментируйте с размером пула и размером отрисовываемых точек, чтобы создать эффекты огненного хвоста, магического следа или частиц дыма.