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

Плавность анимации часто определяет качество игрового процесса. Phaser предлагает мощную систему твинов, но без правильных функций плавности (easing) движения могут выглядеть механически. В этой статье мы разберем три экспоненциальные функции плавности (`expo.in`, `expo.out`, `expo.inOut`) на практическом примере с летающими тарелками. Вы узнаете, как выбирать нужный 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/space.png');
        this.load.image('ufo1', 'assets/tweens/ufo1.png');
        this.load.image('ufo2', 'assets/tweens/ufo2.png');
        this.load.image('ufo3', 'assets/tweens/ufo3.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');
        this.add.text(20, 20, 'Exponential Ease').setFontSize(32).setShadow(2, 2);

        this.add.text(750, 70, 'Expo.in').setShadow(2, 2).setOrigin(1, 0);
        this.add.text(750, 255, 'Expo.out').setShadow(2, 2).setOrigin(1, 0);
        this.add.text(750, 440, 'Expo.inOut').setShadow(2, 2).setOrigin(1, 0);

        const ufo1 = this.add.image(100, 140, 'ufo1');
        const ufo2 = this.add.image(100, 325, 'ufo2');
        const ufo3 = this.add.image(100, 510, 'ufo3');

        this.tweens.add({
            targets: ufo1,
            x: 700,
            duration: 2000,
            repeat: -1,
            hold: 500,
            repeatDelay: 500,
            ease: 'expo.in'
        });

        this.tweens.add({
            targets: ufo2,
            x: 700,
            duration: 2000,
            repeat: -1,
            hold: 500,
            repeatDelay: 500,
            ease: 'expo.out'
        });

        this.tweens.add({
            targets: ufo3,
            x: 700,
            duration: 2000,
            repeat: -1,
            hold: 500,
            repeatDelay: 500,
            ease: 'expo.inout'
        });
    }
}

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

const game = new Phaser.Game(config);

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

Как и в любом проекте Phaser, работа начинается с метода preload. Здесь мы загружаем фоновое изображение и три спрайта летающих тарелок с удаленного сервера, установленного через this.load.setBaseURL.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/tweens/space.png');
    this.load.image('ufo1', 'assets/tweens/ufo1.png');
    this.load.image('ufo2', 'assets/tweens/ufo2.png');
    this.load.image('ufo3', 'assets/tweens/ufo3.png');
}

В методе create мы размещаем фон, заголовок и текстовые подписи для каждого типа плавности. Важно отметить использование метода .setOrigin(1, 0) для текстовых меток — он выравнивает текст по правому краю (originX = 1) и верхнему краю (originY = 0), что удобно для расположения у правой границы экрана.

this.add.text(750, 70, 'Expo.in').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 255, 'Expo.out').setShadow(2, 2).setOrigin(1, 0);
this.add.text(750, 440, 'Expo.inOut').setShadow(2, 2).setOrigin(1, 0);

Создание объектов и настройка твинов

Мы создаем три спрайта тарелок (ufo1, ufo2, ufo3), размещая их вертикально на левой стороне экрана. Каждая тарелка будет двигаться с уникальным типом плавности.

const ufo1 = this.add.image(100, 140, 'ufo1');
const ufo2 = this.add.image(100, 325, 'ufo2');
const ufo3 = this.add.image(100, 510, 'ufo3');

Далее настраиваем анимацию через систему твинов, вызывая this.tweens.add. Ключевой параметр здесь — ease. Он определяет математическую функцию, которая вычисляет промежуточные значения свойства `xмежду начальной (100) и конечной (700) точками за времяduration(2000 мс). Параметрыrepeat: -1,holdиrepeatDelay` заставляют анимацию бесконечно повторяться с паузами, что идеально для демонстрации.

Экспоненциальный вход (Expo.in)

Функция expo.in создает эффект плавного старта с последующим быстрым ускорением. Движение начинается очень медленно, почти незаметно, а затем резко набирает скорость, как ракета при старте. Это достигается за счет экспоненциального роста значения плавности от 0 до 1.

this.tweens.add({
    targets: ufo1,
    x: 700,
    duration: 2000,
    repeat: -1,
    hold: 500,
    repeatDelay: 500,
    ease: 'expo.in'
});

Такой тип хорошо подходит для объектов, которые появляются из состояния покоя: вылет снаряда, начало рывка персонажа.

Экспоненциальный выход (Expo.out)

Функция expo.out — это зеркальная противоположность expo.in. Объект начинает движение быстро, а затем резко замедляется, плавно «притягиваясь» к конечной точке. Это похоже на приземление или точную остановку.

this.tweens.add({
    targets: ufo2,
    x: 700,
    duration: 2000,
    repeat: -1,
    hold: 500,
    repeatDelay: 500,
    ease: 'expo.out'
});

Используйте expo.out для анимаций завершения: заход на посадку, точное размещение предмета в инвентаре, мягкое касание границы экрана.

Экспоненциальный вход-выход (Expo.inOut)

Функция expo.inOut объединяет оба эффекта: плавный старт, быстрый разгон в середине пути и плавное замедление в конце. Это создает наиболее естественное и приятное для глаза движение, имитирующее инерцию реальных объектов.

this.tweens.add({
    targets: ufo3,
    x: 700,
    duration: 2000,
    repeat: -1,
    hold: 500,
    repeatDelay: 500,
    ease: 'expo.inout'
});

Обратите внимание на написание значения: 'expo.inout' (все строчные). Этот тип — универсальный выбор для перемещения персонажей, открытия меню или любых циклических действий, где важны и начало, и конец анимации.

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

Экспоненциальные функции плавности в Phaser — это простой способ добавить анимациям физическую достоверность и динамику. Поэкспериментируйте, комбинируя разные ease-функции для нескольких свойств одновременно (например, `xиscale). Попробуйте применитьexpo.inк прозрачности (alpha) для эффекта постепенного появления илиexpo.outк вращению (rotation`) для реалистичной остановки вращающегося элемента. Это мгновенно повысит полировку вашей игры.