О чем этот пример
Одна из ключевых задач в игровой разработке — анимировать группы объектов предсказуемо и с минимальными затратами производительности. Встроенный класс `Phaser.Actions` в Phaser 3 предлагает готовые, оптимизированные методы для таких манипуляций. Эта статья на практическом примере покажет, как заставить множество спрайтов плавно вращаться вокруг динамически меняющейся точки, используя всего одну строку кода в игровом цикле. Этот подход идеально подходит для создания гипнотических фоновых эффектов, визуализаций ауры или магических заклинаний.
Версия 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.spritesheet('diamonds', 'assets/sprites/diamonds32x24x5.png', { frameWidth: 32, frameHeight: 24 });
}
create ()
{
this.group = this.add.group();
for (var i = 0; i < 256; i++)
{
this.group.create(Phaser.Math.Between(200, 600), Phaser.Math.Between(100, 500), 'diamonds', Phaser.Math.Between(0, 4));
}
this.geomPoint = new Phaser.Math.Vector2(400, 300);
this.input.on('pointermove', function (pointer) {
this.geomPoint.setTo(pointer.x, pointer.y);
}, this);
}
update ()
{
Phaser.Actions.RotateAroundDistance(this.group.getChildren(), this.geomPoint, 0.1, 100);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание группы спрайтов
В методе preload загружается спрайтшит, содержащий несколько кадров с алмазами. Это стандартная практика для работы с набором однотипных спрайтов.
В create инициализируется группа (Group) — мощный контейнер Phaser для управления коллекциями игровых объектов. Цикл создает 256 спрайтов, случайно размещая их на поле и назначая случайный кадр из спрайтшита. Все они автоматически добавляются в группу this.group.
Также создается вектор geomPoint, который будет хранить целевую точку вращения. Изначально она установлена в центр экрана (400, 300). Событие pointermove обновляет координаты этой точки, следуя за курсором мыши.
this.group = this.add.group();
for (var i = 0; i < 256; i++)
{
this.group.create(Phaser.Math.Between(200, 600), Phaser.Math.Between(100, 500), 'diamonds', Phaser.Math.Between(0, 4));
}
this.geomPoint = new Phaser.Math.Vector2(400, 300);
this.input.on('pointermove', function (pointer) {
this.geomPoint.setTo(pointer.x, pointer.y);
}, this);
Магия одного вызова: RotateAroundDistance
Вся анимация вращения происходит в методе update, который вызывается каждый кадр. Ключевая роль здесь у статического метода Phaser.Actions.RotateAroundDistance.
Этот метод принимает четыре аргумента:
1. Массив объектов для вращения (`this.group.getChildren()`).
2. Целевую точку (`this.geomPoint`).
3. Угол поворота за вызов (в радианах). Значение `0.1` обеспечивает плавное вращение.
4. Фиксированное расстояние от объекта до точки вращения (в пикселях). Все объекты будут находиться на расстоянии 100 пикселей от `geomPoint`.
Метод вычисляет новую позицию для каждого спрайта в массиве, перемещая его по воображаемой окружности с заданным радиусом вокруг целевой точки.
Phaser.Actions.RotateAroundDistance(this.group.getChildren(), this.geomPoint, 0.1, 100);
Как это работает "под капотом"?
Важно понимать, что RotateAroundDistance не изменяет свойства вращения (rotation или angle) самих спрайтов. Вместо этого он напрямую пересчитывает и устанавливает их координаты `xиy` на основе тригонометрических формул.
Для каждого объекта вычисляется текущий угол относительно центральной точки, к нему прибавляется дельта (0.1), а затем по новому углу и заданному радиусу (100) рассчитываются новые декартовы координаты.
Поскольку расстояние фиксировано, все спрайты выстраиваются в идеальный круг, центром которого является geomPoint. Их первоначальное случайное положение игнорируется после первого же вызова метода в update. Движение мыши меняет центр этого круга, и вся система мгновенно перестраивается вокруг новой точки.
Конфигурация игры и запуск
Пример завершается стандартной конфигурацией игрового экземпляра Phaser. Ключевые параметры:
* type: Phaser.AUTO позволяет движку выбрать WebGL или Canvas рендеринг.
* width/height: Размеры игрового поля.
* scene: Указывает класс, который будет управлять логикой (наш Example).
Инициализация игры с этой конфигурацией запускает жизненный цикл сцены (preload, create, update).
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Phaser.Actions.RotateAroundDistance — это элегантное и производительное решение для анимации групп объектов по круговой траектории. Всего одна строка кода заменяет ручные тригонометрические расчеты в цикле. Для экспериментов попробуйте: динамически менять радиус вращения в зависимости от времени или положения мыши; привязать угол поворота к скорости движения курсора; использовать другой метод из Phaser.Actions, например, Call или PlaceOnCircle, для комбинирования поведения. Это откроет путь к созданию сложных и визуально насыщенных эффектов с минимальными усилиями.
