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

Анимация — это сердце любой динамичной игры. Phaser предоставляет мощную систему Tween, которая позволяет легко оживлять объекты. Одним из ключевых её элементов являются функции плавности (easing), определяющие характер движения. В этом примере мы не просто используем функцию плавности, а визуализируем её график в реальном времени, что идеально подходит для понимания разницы между типами `in`, `out` и `inout`. Этот подход поможет вам осознанно выбирать нужный тип анимации для своих игр.

Версия 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, 'Quintic Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);

        const types = [ 'quint.in', 'quint.out', 'quint.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 мы загружаем фоновое изображение, которое будет служить фоном для нашего графика.

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

Подготовка к визуализации

В методе create мы сначала добавляем фон и заголовок. Затем создаём ключевые объекты для отрисовки графика: - graph: объект Graphics для рисования линии графика. - rect: маленький зелёный прямоугольник, который будет «пером», оставляющим след. - rt: 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. Она выполняет три ключевые задачи: 1. Очищает предыдущий твин, график и текстуру (rt.clear()). 2. Настраивает новое рисование линии (graph.lineStyle, graph.beginPath). 3. Создаёт и запускает твин для прямоугольника rect.

Магия происходит в настройках твина. Мы анимируем две координаты с разными функциями плавности: движение по оси X — линейное (ease: 'linear'), а по оси Y — по выбранному квинтическому закону (ease: types[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();
    }
});

Колбэк onUpdate срабатывает на каждом кадре анимации. Мы проверяем, обновилась ли координата X (так как обе координаты меняются одновременно), и тогда: - Копируем текущее состояние прямоугольника в RenderTexture командой rt.draw(rect). Это создаёт эффект «шлейфа». - Добавляем новую точку в путь объекта graph с помощью graph.lineTo(rect.x, rect.y). После завершения анимации (onComplete) мы вызываем graph.stroke(), чтобы окончательно отрисовать накопленный путь линией.

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

Для интерактивности мы навешиваем обработчик клика (pointerdown) на всю сцену. При каждом клике индекс type увеличивается, циклически перебирая массив ['quint.in', 'quint.out', 'quint.inout']. После смены типа вызывается graphEase(), что запускает новую анимацию и перерисовывает график.

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

Это позволяет наглядно сравнить, как разные функции влияют на движение: in — ускорение от начала, out — замедление к концу, inout — комбинация обоих эффектов.

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

Этот пример — отличный инструмент для изучения easing-функций в Phaser. Вы можете экспериментировать: подставьте другие типы плавности (например, 'cubic', 'sine', 'back') из обширного списка Phaser. Попробуйте изменить длительность (duration) или начальные координаты, чтобы увидеть, как меняется график. Для практического применения скопируйте логику твина в свою игру, чтобы придать анимациям персонажей или интерфейса естественную инерцию и выразительность.