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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const triangle = new Phaser.Geom.Triangle(400, 250, 300, 350, 500, 350);

        const graphics = this.add.graphics({ lineStyle: { color: 0xaaaa00 } });

        graphics.strokeTriangleShape(triangle);

        for (let i = 0; i < 25; i++)
        {
            triangle.setTo(
                triangle.x1 * 0.97, triangle.y1 * 0.95,
                triangle.x2 * 1.05, triangle.y2 * 1.04,
                triangle.x3 * 1.02, triangle.y3 * 1.01
            );

            graphics.strokeTriangleShape(triangle);
        }
    }
}

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

const game = new Phaser.Game(config);

Создание базового треугольника

Вся работа начинается с создания экземпляра геометрического треугольника. Конструктор класса Phaser.Geom.Triangle принимает шесть аргументов – координаты X и Y для каждой из трех вершин.

const triangle = new Phaser.Geom.Triangle(400, 250, 300, 350, 500, 350);

Этот код создает треугольник с вершинами в точках (400, 250), (300, 350) и (500, 350). Для его визуализации мы используем объект Graphics, который позволяет рисовать примитивы.

const graphics = this.add.graphics({ lineStyle: { color: 0xaaaa00 } });
graphics.strokeTriangleShape(triangle);

Здесь мы создаем контекст рисования с желтым (0xaaaa00) стилем линии и отрисовываем контур нашего треугольника с помощью метода strokeTriangleShape().

Магия метода `setTo()`

Ключевой инструмент трансформации – метод setTo(). Он не создает новый объект, а перезаписывает координаты существующего треугольника. Это эффективно с точки зрения производительности.

Сигнатура метода:

triangle.setTo(x1, y1, x2, y2, x3, y3);

Каждый вызов полностью заменяет старые координаты вершины 1 (x1, y1), вершины 2 (x2, y2) и вершины 3 (x3, y3) на новые значения. В исходном примере новые координаты вычисляются на основе старых, что и создает анимацию.

triangle.setTo(
    triangle.x1 * 0.97, triangle.y1 * 0.95,
    triangle.x2 * 1.05, triangle.y2 * 1.04,
    triangle.x3 * 1.02, triangle.y3 * 1.01
);

Здесь каждая вершина меняется по своему закону: первая сжимается к центру (умножение на 0.97 и 0.95), вторая и третья – расширяются (умножение на значения больше 1).

Цикл трансформации и отрисовки

Для создания последовательности кадров (множества треугольников) используется простой цикл. На каждой итерации мы: 1. Вычисляем новые координаты и применяем их через setTo(). 2. Отрисовываем текущее состояние треугольника.

for (let i = 0; i < 25; i++)
{
    // 1. Трансформируем треугольник
    triangle.setTo(
        triangle.x1 * 0.97, triangle.y1 * 0.95,
        triangle.x2 * 1.05, triangle.y2 * 1.04,
        triangle.x3 * 1.02, triangle.y3 * 1.01
    );
    // 2. Отрисовываем новый контур
    graphics.strokeTriangleShape(triangle);
}

Поскольку объект Graphics не очищается, каждый новый вызов strokeTriangleShape() добавляет в сцену контур треугольника в его новой позиции и форме. В результате мы видим не одно движущееся изображение, а след из 26 треугольников (исходный + 25 новых), что создает эффект постепенного искажения и движения.

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

Код примера является полноценным приложением Phaser. Конфигурационный объект задает основные параметры игры: размеры холста, тип рендерера и корневую сцену.

const config = {
    width: 800,
    height: 600,
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example // Наш класс сцены
};

const game = new Phaser.Game(config);

После создания экземпляра Phaser.Game с этой конфигурацией фреймворк автоматически инициализирует рендерер и запустит жизненный цикл сцены Example, вызвав в нужный момент метод create(), который мы и разобрали.

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

Метод setTo() для геометрических объектов в Phaser – это простой, но мощный способ анимировать форму напрямую, минуя систему анимаций спрайтов. Он идеально подходит для процедурной графики, визуальных эффектов и динамических коллайдеров. **Идеи для экспериментов:** 1. Свяжите изменение вершин треугольника с игровыми событиями (например, с каждым выстрелом). 2. Используйте Math.sin() или Math.cos() для вычисления новых координат, чтобы получить плавные колебания формы. 3. Примените этот же принцип к другим геометрическим объектам, например, Phaser.Geom.Rectangle, чтобы искажать игровые платформы или зоны поражения. 4. Комбинируйте setTo() с заливкой (graphics.fillTriangleShape), используя разные цвета на каждой итерации для создания градиентных эффектов.