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

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

Версия 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('bg', 'assets/tweens/background-crt.jpg');
    }

    create ()
    {
        this.add.image(400, 300, 'bg').setScale(0.78);

        this.add.text(400, 28, 'Circular Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);

        const types = [ 'circ.in', 'circ.out', 'circ.inout' ];
        let type = 0;
        let tween;

        const label = this.add.text(400, 530).setColor('#00ff00').setFontSize(22).setShadow(1, 1).setOrigin(0.5, 0).setAlign('center');

        const graph = this.add.graphics();
        const rect = this.add.rectangle(100, 400, 2, 2, 0x00ff00);
        const rt = this.add.renderTexture(400, 300, 800, 600);

        const graphEase = () => {

            if (tween)
            {
                tween.stop();
            }

            rt.clear();

            graph.clear();
            graph.lineStyle(3, 0x00ff00);
            graph.beginPath();

            rect.setPosition(50, 450);

            label.setText([
                types[type],
                'Click to change type'
            ]);

            tween = this.tweens.add({
                targets: rect,
                x: { value: 750, ease: 'linear' },
                y: { value: 100, ease: types[type] },
                duration: 4000,
                onUpdate: (tween, target, key) => {
                    if (key === 'x')
                    {
                        rt.draw(rect);
                        graph.lineTo(rect.x, rect.y);
                    }
                },
                onComplete: () => {
                    graph.stroke();
                }
            });
        }

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

            type++;

            if (type === types.length)
            {
                type = 0;
            }

            graphEase();

        });

        graphEase();
    }
}

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

const game = new Phaser.Game(config);

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

Класс Example расширяет базовый Phaser.Scene. В методе preload загружается фоновое изображение с помощью this.load.image. Обратите внимание на использование setBaseURL для указания базового пути к ресурсам.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/tweens/background-crt.jpg');
}

Подготовка элементов визуализации

В методе create размещаются все необходимые элементы: фон, заголовок, метка с типом анимации, а также три ключевых графических объекта.

1.  `graph` (`this.add.graphics()`) — отвечает за отрисовку зеленой линии графика.
2.  `rect` (`this.add.rectangle()`) — маленький зеленый квадратик, чье движение мы анимируем.
3.  `rt` (`this.add.renderTexture()`) — текстура для рендеринга, которая сохраняет след от движения квадрата.
const graph = this.add.graphics();
const rect = this.add.rectangle(100, 400, 2, 2, 0x00ff00);
const rt = this.add.renderTexture(400, 300, 800, 600);

Ядро анимации: функция graphEase

Функция graphEase создает и запускает твин. Ключевой момент — использование разных функций easing для осей X и Y. По горизонтали движение линейное (ease: 'linear'), а по вертикали применяется одна из круговых функций из массива types.

Твин настраивается с помощью конфигурационного объекта. Параметры onUpdate и onComplete используются для отрисовки траектории.

tween = this.tweens.add({
    targets: rect,
    x: { value: 750, ease: 'linear' },
    y: { value: 100, ease: types[type] },
    duration: 4000,
    onUpdate: (tween, target, key) => {
        if (key === 'x')
        {
            rt.draw(rect);
            graph.lineTo(rect.x, rect.y);
        }
    },
    onComplete: () => {
        graph.stroke();
    }
});

В коллбеке onUpdate проверяется, обновляется ли свойство `x. Это гарантирует, что отрисовка вrtи построение линии вgraphпроисходят синхронно с движением, что создает плавный и непрерывный след. Линия графика отображается (graph.stroke()) только после завершения анимации вonComplete`.

Переключение типов анимации

Обработчик клика pointerdown циклически перебирает типы easing-функций из массива ['circ.in', 'circ.out', 'circ.inout'] и заново запускает graphEase для визуализации нового типа.

this.input.on('pointerdown', () => {
    type++;
    if (type === types.length)
    {
        type = 0;
    }
    graphEase();
});

- circ.in: Анимация начинается медленно и ускоряется к концу. - circ.out: Анимация начинается быстро и замедляется к концу. - circ.inout: Анимация плавно ускоряется от начала к середине и замедляется от середины к концу.

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

Этот пример — отличный инструмент для понимания и выбора подходящей easing-функции. Экспериментируйте: попробуйте изменить длительность (duration), стартовую позицию rect или визуализировать другие easing-функции из библиотеки Phaser (например, sine, quad, cubic). Можно заменить квадратик на спрайт и наблюдать, как разные типы easing влияют на восприятие движения игрового объекта.