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

Плавность анимации — ключевой элемент геймдизайна, влияющий на восприятие игрового процесса. В Phaser за неё отвечают функции плавности (eases), которые контролируют скорость изменения параметров твина. Этот пример наглядно показывает, как работают три основные вариации квадро-функции плавности (`quart.in`, `quart.out`, `quart.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, 'Quartic Ease').setColor('#00ff00').setFontSize(32).setShadow(2, 2).setOrigin(0.5, 0);

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

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

Инициализация графических элементов

В методе create размещается фон, заголовок и создаются все необходимые объекты для визуализации. Обратите внимание на роли каждого: - label — текстовый блок для отображения текущего типа плавности. - graph — объект Graphics для отрисовки линии графика. - rect — маленький прямоугольник (2x2 пикселя), который будет двигаться и служить пером для графика. - rtRenderTexture, которая рисует след движения прямоугольника.

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);

Функция визуализации графика

Функция graphEase — сердце примера. Она сбрасывает предыдущую анимацию, очищает холсты и запускает новый твин для прямоугольника rect. Ключевой момент — твин задаётся для двух свойств с разными функциями плавности: движение по X (`x) использует линейную плавность ('linear'), а по Y (y`) — выбранный квадро-тип.

tween = this.tweens.add({
    targets: rect,
    x: { value: 750, ease: 'linear' },
    y: { value: 100, ease: types[type] },
    duration: 4000,
    // ... обработчики onUpdate и onComplete
});

Логика отрисовки в реальном времени

В коллбэке onUpdate происходит магия. Он срабатывает на каждом кадре обновления твина. Условие if (key === 'x') гарантирует, что отрисовка выполняется только один раз за кадр (при обновлении свойства `x). Движущийся прямоугольник рисуется наRenderTexture(rt.draw(rect)), а его текущие координаты добавляются в путь объектаGraphics(graph.lineTo(rect.x, rect.y)). По завершении анимации (onComplete) путь обводится линией (graph.stroke()`), финализируя график.

onUpdate: (tween, target, key) => {
    if (key === 'x')
    {
        rt.draw(rect);
        graph.lineTo(rect.x, rect.y);
    }
},
onComplete: () => {
    graph.stroke();
}

Обработка ввода и переключение типа

Слушатель события pointerdown на сцене (this.input.on) позволяет циклически переключаться между типами плавности из массива types. При каждом клике индекс type увеличивается, а по достижении конца массива сбрасывается в ноль, после чего вызывается graphEase() для перезапуска анимации с новыми параметрами.

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

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

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