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

Анимация движения объектов — основа игровой динамики. Часто нужно, чтобы траектория или цель анимации менялась в процессе, реагируя на игровые события. Phaser Tweens позволяет задавать не только статичные значения, но и вычислять их динамически с помощью функций `getStart` и `getEnd`. Эта техника открывает путь к созданию сложных, "живых" движений, таких как постепенное смещение цели или пружинящие эффекты, без ручного управления кадрами. В статье разберем пример, где шар движется по сложной траектории, меняя точку отскока на каждом цикле.

Версия 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('ball', 'assets/sprites/shinyball.png');
    }

    create ()
    {
        var image = this.add.image(100, 100, 'ball');

        var destX = 700;

        //  TODO: Cache the start and end values so that on a Tween LOOP (not a TweenData repeat) it can reset them :)

        var tween = this.tweens.add({

            targets: image,

            props: {

                y: {
                    value: 500,
                    duration: 8000,
                    ease: 'Power1'
                },

                x: {
                    duration: 400,
                    yoyo: true,
                    repeat: 8,
                    ease: 'Sine.easeInOut',
                    value: {

                        getEnd: function (target, key, value)
                        {
                            destX -= 30;

                            return destX;
                        },

                        getStart: function (target, key, value)
                        {
                            return value + 30;
                        }

                    }
                }

            }

        });
    }
}

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

const game = new Phaser.Game(config);

Сцена и базовая загрузка

Код начинается с создания стандартной сцены Phaser 3. В методе preload загружается одно изображение — спрайт шара. Обратите внимание, что используется setBaseURL для указания базового пути к ресурсам. Это удобно, если ваши ассеты лежат в определенной структуре каталогов.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('ball', 'assets/sprites/shinyball.png');
}

Создание объекта и определение Tween

В методе create мы создаем экземпляр изображения шара и определяем переменную destX, которая будет хранить конечную координату X для анимации. Это наша внешняя переменная для управления логикой.

var image = this.add.image(100, 100, 'ball');
var destX = 700;

Далее создается объект твина с помощью this.tweens.add. Ключевой параметр targets указывает, к какому объекту применяется анимация. Внутри props мы определяем свойства, которые будут анимированы: `yиx`.

var tween = this.tweens.add({
    targets: image,
    props: {
        // ... свойства y и x будут здесь
    }
});

Простая анимация по оси Y

Свойство `yанимируется классическим способом: задается конечное значениеvalue, длительностьdurationи функция плавностиease. Шар будет двигаться вниз до Y=500 за 8 секунд с линейным ускорением (Power1`).

y: {
    value: 500,
    duration: 8000,
    ease: 'Power1'
}

Динамическая анимация по оси X

Вот где начинается магия. Для свойства `xмы задаем более сложную конфигурацию. Анимация будет короткой (400 мс), с отскоком (yoyo: true) и повторится 8 раз (repeat: 8). Функция плавностиSine.easeInOut` создает мягкий старт и остановку.

x: {
    duration: 400,
    yoyo: true,
    repeat: 8,
    ease: 'Sine.easeInOut',
    // ... динамическое значение value
}

Вместо статичного числа параметр value является объектом с двумя функциями: getEnd и getStart. Эти функции вызываются твином для вычисления значений в реальном времени.

Функция getEnd определяет конечную точку каждого цикла анимации. Каждый раз, когда она вызывается, она уменьшает глобальную переменную destX на 30 и возвращает новое значение. Таким образом, с каждым циклом цель движения по X смещается влево.

value: {
    getEnd: function (target, key, value)
    {
        destX -= 30;
        return destX;
    },
    getStart: function (target, key, value)
    {
        return value + 30;
    }
}

Функция getStart определяет начальную точку. Она берет текущее значение свойства (value) и добавляет к нему 30. Это создает эффект "шага": после каждого отскока шар начинает движение не с той же точки, а чуть правее, компенсируя смещение конечной цели. В итоге шар совершает серию горизонтальных колебаний, постепенно смещаясь влево, одновременно плавно опускаясь вниз.

Конфигурация и запуск игры

За пределами класса сцены определяется стандартный объект конфигурации игры config, в котором указывается тип рендерера, размеры холста, цвет фона, родительский элемент и основная сцена.

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

Игра инициализируется созданием нового экземпляра Phaser.Game с этой конфигурацией.

const game = new Phaser.Game(config);

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

Динамические свойства getStart и getEnd в Phaser Tweens — мощный инструмент для создания нелинейных, адаптивных анимаций. Они позволяют анимации реагировать на состояние игры или предыдущие циклы. Для экспериментов попробуйте

  1. Изменить логику в getEnd, чтобы цель смещалась по синусоиде или случайным образом
  2. Привязать значение destX к положению курсора мыши, чтобы шар "преследовал" его рывками
  3. Использовать getActive для модификации анимации прямо во время её выполнения, создавая эффекты инерции или сопротивления