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

Создание сложных геометрических паттернов из игровых объектов — частая задача при разработке визуальных эффектов, построении интерфейсов или расположении врагов. Вручную рассчитывать позиции для каждого спрайта утомительно и неэффективно. Класс `Phaser.Actions` в Phaser 3 предлагает набор методов для автоматического размещения группы объектов по заданной геометрии. В этой статье мы разберем метод `Phaser.Actions.PlaceOnTriangle()`, который позволяет мгновенно распределить любое количество спрайтов вдоль сторон треугольника. Это мощный инструмент для прототипирования, создания формаций и визуализации данных, который избавляет от необходимости писать циклы и геометрические расчеты вручную.

Версия 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/chunk.png');
    }

    create ()
    {
        const triangle1 = new Phaser.Geom.Triangle.BuildEquilateral(200, 90, 280);

        const group1 = this.add.group({ key: 'ball', frameQuantity: 64 });

        Phaser.Actions.PlaceOnTriangle(group1.getChildren(), triangle1);

        const triangle2 = new Phaser.Geom.Triangle.BuildRight(400, 500, 300, 200);

        const group2 = this.add.group({ key: 'ball', frameQuantity: 64 });

        Phaser.Actions.PlaceOnTriangle(group2.getChildren(), triangle2);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ресурсов

Как и в любом проекте Phaser, работа начинается с создания сцены. В методе preload() мы загружаем спрайт, который будет использован для визуализации.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('ball', 'assets/sprites/chunk.png');
}

Здесь setBaseURL задает базовый путь для загрузки, а load.image загружает текстуру chunk.png под ключом 'ball'. Этот ключ мы будем использовать для создания группы спрайтов.

Создание геометрии: треугольники

Phaser предоставляет удобный класс Phaser.Geom.Triangle для работы с треугольниками. В примере создаются два треугольника разной формы с помощью статических методов-конструкторов.

const triangle1 = new Phaser.Geom.Triangle.BuildEquilateral(200, 90, 280);
const triangle2 = new Phaser.Geom.Triangle.BuildRight(400, 500, 300, 200);

* BuildEquilateral(x, y, size) создает равносторонний треугольник. Параметры `xиyзадают координаты верхней вершины, аsize` — длину стороны. * BuildRight(x, y, width, height) создает прямоугольный треугольник. Точка (x, y) — это прямой угол (90 градусов). width и height задают длины катетов, идущих по осям X и Y соответственно. Эти объекты содержат координаты трех вершин, которые и будут использованы для размещения спрайтов.

Формирование групп объектов

Чтобы работать с множеством одинаковых объектов эффективно, в Phaser используется концепция Group. Группа управляет пулом объектов, их созданием и обновлением.

const group1 = this.add.group({ key: 'ball', frameQuantity: 64 });
const group2 = this.add.group({ key: 'ball', frameQuantity: 64 });

Метод this.add.group() создает новую группу. В конфигурационном объекте: * key указывает на текстуру, которая будет использована для создания каждого члена группы. * frameQuantity определяет, сколько именно спрайтов (ball) должно быть создано и добавлено в группу сразу. В нашем случае создается по 64 спрайта для каждого треугольника. Метод group.getChildren() возвращает массив всех созданных спрайтов.

Магия размещения: PlaceOnTriangle

Ключевой метод статьи — Phaser.Actions.PlaceOnTriangle(). Он принимает массив объектов и геометрический треугольник, равномерно распределяя объекты по его периметру.

Phaser.Actions.PlaceOnTriangle(group1.getChildren(), triangle1);
Phaser.Actions.PlaceOnTriangle(group2.getChildren(), triangle2);

Метод работает следующим образом: 1. Он берет массив детей группы (спрайтов ball). 2. Для каждого спрайта в массиве вычисляется позиция на одной из трех сторон треугольника triangle1 или triangle2. 3. Позиции рассчитываются так, чтобы расстояние между соседними спрайтами вдоль всего периметра было примерно одинаковым. Спрайты последовательно размещаются на сторонах AB, BC и CA. 4. Координаты `xиy` каждого спрайта мгновенно обновляются. Весь процесс происходит за один вызов, без необходимости в циклах и ручных математических расчетах.

Конфигурация и запуск игры

Завершающий шаг — стандартная настройка экземпляра игры Phaser.Game с помощью конфигурационного объекта.

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

const game = new Phaser.Game(config);

В конфиге задается автоматический выбор рендерера (AUTO), размеры холста, ID родительского HTML-элемента и наша сцена Example. После создания экземпляра game Phaser берет на себя управление циклом рендеринга, и мы видим результат: две группы по 64 спрайта, аккуратно выстроенные вдоль сторон равностороннего и прямоугольного треугольников.

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

Метод Phaser.Actions.PlaceOnTriangle — это яркий пример того, как Phaser упрощает рутинные задачи. Вместо десятков строк кода для расчета позиций достаточно одного вызова. Этот подход не только экономит время, но и делает код чище и понятнее. **Идеи для экспериментов:** 1. **Анимация паттерна:** Попробуйте анимировать вершины треугольника (изменяя его геометрию каждый кадр) и вызывать PlaceOnTriangle в update(). Спрайты будут плавно двигаться вдоль меняющегося контура. 2. **Комбинирование действий:** Используйте PlaceOnTriangle вместе с другими методами из Phaser.Actions, например, RotateAround или SetTint, чтобы создать вращающийся или цветной геометрический узор. 3. **Динамическое количество объектов:** Свяжите frameQuantity в создании группы с каким-либо параметром (например, уровнем сложности) или изменяйте его во время выполнения игры, чтобы паттерн становился плотнее или реже.