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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('lemming', 'assets/sprites/lemming.png');
    }

    create ()
    {
        const points = [];

        points.push(new Phaser.Math.Vector2(50, 400));
        points.push(new Phaser.Math.Vector2(200, 200));
        points.push(new Phaser.Math.Vector2(350, 300));
        points.push(new Phaser.Math.Vector2(500, 500));
        points.push(new Phaser.Math.Vector2(700, 400));

        const curve = new Phaser.Curves.Spline(points);

        const graphics = this.add.graphics();

        graphics.lineStyle(1, 0xffffff, 1);

        curve.draw(graphics, 64);

        graphics.fillStyle(0x00ff00, 1);

        for (let i = 0; i < points.length; i++)
        {
            graphics.fillCircle(points[i].x, points[i].y, 4);
        }

        const lemming = this.add.follower(curve, 50, 400, 'lemming');

        lemming.startFollow({
            duration: 6000,
            yoyo: true,
            repeat: -1,
            rotateToPath: true,
            startAt: 0.5
        });

        this.input.on('pointerdown', () =>
        {

            if (lemming.isFollowing())
            {
                lemming.pauseFollow();
            }
            else
            {
                lemming.resumeFollow();
            }

        });

    }
}

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

const game = new Phaser.Game(config);

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

Вся логика примера находится в методе create() нашей сцены. Первым делом мы определяем набор точек, которые станут опорными для нашей кривой. Для этого используется класс Phaser.Math.Vector2.

const points = [];
points.push(new Phaser.Math.Vector2(50, 400));
points.push(new Phaser.Math.Vector2(200, 200));
points.push(new Phaser.Math.Vector2(350, 300));
points.push(new Phaser.Math.Vector2(500, 500));
points.push(new Phaser.Math.Vector2(700, 400));

Затем эти точки передаются конструктору Phaser.Curves.Spline, который создает плавную кривую, проходящую через все заданные точки. Это гораздо более гибкий инструмент, чем простая ломаная линия.

const curve = new Phaser.Curves.Spline(points);

Визуализация пути

Чтобы видеть созданный путь на экране, мы используем объект Graphics. Это встроенный в Phaser инструмент для программного рисования.

Сначала задаем стиль линии: толщина 1 пиксель, белый цвет (0xffffff), полная непрозрачность (альфа = 1). Затем метод curve.draw() отрисовывает саму кривую на нашем графическом объекте, используя 64 отрезка для сглаживания.

const graphics = this.add.graphics();
graphics.lineStyle(1, 0xffffff, 1);
curve.draw(graphics, 64);

Для наглядности также отрисуем зеленые точки в местах опорных векторов, которые мы задали изначально.

graphics.fillStyle(0x00ff00, 1);
for (let i = 0; i < points.length; i++)
{
    graphics.fillCircle(points[i].x, points[i].y, 4);
}

Создание и запуск следящего объекта (Follower)

Самый важный объект в этом примере — follower. Он создается с помощью метода this.add.follower(), который принимает саму кривую, начальные координаты (они будут проигнорированы, так как позиция определяется кривой) и ключ текстуры спрайта.

const lemming = this.add.follower(curve, 50, 400, 'lemming');

Движение запускается методом startFollow(), который принимает объект конфигурации. Вот что значат его ключевые параметры: * duration: Время в миллисекундах, за которое объект пройдет весь путь от начала до конца. * yoyo: Если true, объект, дойдя до конца пути, развернется и пойдет обратно. * repeat: Количество повторений (-1 для бесконечного цикла). * rotateToPath: Автоматически поворачивает спрайт по направлению движения. * startAt: Самый интересный параметр. Он определяет точку старта на кривой как долю от ее общей длины. Значение 0.5 означает, что лемминг начнет движение ровно с середины пути.

lemming.startFollow({
    duration: 6000,
    yoyo: true,
    repeat: -1,
    rotateToPath: true,
    startAt: 0.5
});

Интерактивность: управление движением

Пример добавляет простую интерактивность. При клике мышью мы проверяем, находится ли follower в состоянии следования, с помощью метода isFollowing(). В зависимости от результата мы либо приостанавливаем движение методом pauseFollow(), либо возобновляем его методом resumeFollow(). Это демонстрирует, как легко можно интегрировать логику управления в геймплей.

this.input.on('pointerdown', () => {
    if (lemming.isFollowing())
    {
        lemming.pauseFollow();
    }
    else
    {
        lemming.resumeFollow();
    }
});

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

Компонент Follower в Phaser 3 — это мощный и удобный инструмент для декларативного описания сложного движения. Используя кривые и гибкие настройки анимации, вы можете создавать динамичные сцены буквально несколькими строками кода. Для экспериментов попробуйте: изменить тип кривой на Phaser.Curves.Path или Phaser.Curves.Ellipse; анимировать startAt для эффекта телепортации объекта по пути; привязать к движению по кривой не спрайт, а камеру для создания эффектных пролетов; или использовать несколько последовательных followers для построения целых анимированных цепочек.