О чем этот пример
Работа с группой объектов по геометрической форме — мощный приём для создания сложных визуальных эффектов с минимальным кодом. В этом примере мы рассмотрим, как равномерно разместить спрайты по окружности и анимировать их движение, создавая гипнотизирующий вращающийся узор. Этот подход полезен для создания меню, индикаторов загрузки, спецэффектов или любого системного расположения объектов в вашей игре.
Версия 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('ball', 'assets/sprites/shinyball.png');
}
create ()
{
const circle = new Phaser.Geom.Circle(400, 300, 260);
this.group = this.add.group({ key: 'ball', frameQuantity: 32 });
Phaser.Actions.PlaceOnCircle(this.group.getChildren(), circle);
this.tween = this.tweens.addCounter({
from: 260,
to: 0,
duration: 3000,
delay: 2000,
ease: 'Sine.easeInOut',
repeat: -1,
yoyo: true
});
}
update ()
{
Phaser.Actions.RotateAroundDistance(this.group.getChildren(), { x: 400, y: 300 }, 0.02, this.tween.getValue());
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
В методе preload мы устанавливаем базовый URL для загрузки ресурсов из репозитория примеров Phaser и загружаем одно изображение спрайта — блестящий шарик. Этот спрайт будет использован для всех элементов группы.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('ball', 'assets/sprites/shinyball.png');
}
Создание геометрии и группы объектов
В методе create определяется логика инициализации. Сначала создаётся геометрический объект Phaser.Geom.Circle, представляющий окружность с центром в точке (400, 300) и начальным радиусом 260 пикселей.
Затем создаётся группа (this.add.group). Ключевой параметр key указывает на загруженное изображение 'ball', а frameQuantity определяет, сколько одинаковых спрайтов будет создано в этой группе — в нашем случае 32.
const circle = new Phaser.Geom.Circle(400, 300, 260);
this.group = this.add.group({ key: 'ball', frameQuantity: 32 });
Размещение объектов по окружности
Сразу после создания группы все её дочерние элементы (спрайты) равномерно размещаются на ранее заданной окружности с помощью статического метода Phaser.Actions.PlaceOnCircle. Первым аргументом передаётся массив детей группы, полученный через this.group.getChildren(), вторым — объект окружности.
Это действие мгновенно рассчитывает позиции для всех 32 спрайтов и расставляет их по кругу.
Phaser.Actions.PlaceOnCircle(this.group.getChildren(), circle);
Анимация пульсирующего радиуса
Чтобы оживить композицию, создаётся твин для числового значения (Counter). Он плавно изменяет значение от 260 (начальный радиус) до 0 и обратно. Параметры repeat: -1 и yoyo: true заставляют анимацию бесконечно повторяться в режиме «туда-обратно». Задержка delay в 2000 мс даёт игроку время рассмотреть начальное расположение.
Объект твина сохраняется в свойство сцены this.tween для дальнейшего использования.
this.tween = this.tweens.addCounter({
from: 260,
to: 0,
duration: 3000,
delay: 2000,
ease: 'Sine.easeInOut',
repeat: -1,
yoyo: true
});
Динамическое вращение в реальном времени
Сердцевина анимации находится в методе `update`, который вызывается на каждом кадре. Здесь используется метод `Phaser.Actions.RotateAroundDistance`. Он выполняет две операции одновременно для каждого спрайта в группе:
1. **Вращение:** Поворачивает позицию каждого спрайта вокруг центральной точки { x: 400, y: 300 } на угол 0.02 радиана за кадр.
2. **Изменение дистанции:** Устанавливает расстояние от спрайта до центра вращения равным текущему значению твина, полученному через `this.tween.getValue()`. Это заставляет круг из шариков пульсировать.
Таким образом, спрайты не просто вращаются, а делают это по постоянно сжимающейся и расширяющейся орбите.
update ()
{
Phaser.Actions.RotateAroundDistance(this.group.getChildren(), { x: 400, y: 300 }, 0.02, this.tween.getValue());
}
Что попробовать дальше
Комбинация PlaceOnCircle для начального размещения и RotateAroundDistance в цикле обновления — элегантный и производительный способ анимировать сложные паттерны. Для экспериментов попробуйте: изменить количество и тип спрайтов в группе, использовать другую геометрическую форму (например, PlaceOnEllipse), варьировать скорость вращения или радиус через твин, а также добавить обработку кликов по отдельным шарикам для создания интерактивных интерфейсов.
