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

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

Версия 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, 300, 'ball');

        var destX = 700;

        var tween = this.tweens.add({
            targets: image,
            duration: 500,
            yoyo: true,
            repeat: 8,
            ease: 'Sine.easeInOut',

            x: {

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

Что такое динамические свойства?

Обычно, создавая твин, вы задаете конкретные числовые значения для свойств, например, x: 700. Объект плавно переместится из текущей позиции в точку x=700.

Динамические свойства заменяют эти статические числа функциями-геттерами. Phaser будет вызывать эти функции в ключевые моменты анимации, чтобы получить актуальное значение. Это позволяет анимации адаптироваться к изменяющимся условиям в игре.

В примере используется два таких геттера: * getStart — вычисляет начальное значение свойства для каждого цикла твина. * getEnd — вычисляет конечное значение свойства для каждого цикла твина.

Разбор примера: мяч с сужающейся амплитудой

Давайте построчно разберем ключевую часть кода — конфигурацию твина. Создается объект image (мяч) в точке (100, 300).

var tween = this.tweens.add({
    targets: image,
    duration: 500,
    yoyo: true,
    repeat: 8,
    ease: 'Sine.easeInOut',

Здесь задаются базовые параметры: цель анимации (targets), длительность одного движения в миллисекундах (duration). Флаг yoyo: true означает, что после достижения конечной точки объект вернется назад. repeat: 8 установит количество полных повторов (туда-обратно). ease задает плавную синусоидальную функцию замедления в начале и конце.

Следующая часть — самое интересное: динамическое свойство `x`.

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

Как работают getStart и getEnd

Функции getEnd и getStart вызываются движком Phaser. Они получают три аргумента: сам анимируемый объект (target), имя свойства (key, в нашем случае 'x') и текущее базовое значение (value).

1. **getEnd**: Эта функция определяет, *куда* полетит мяч. При первом вызове переменная destX равна 700. Функция уменьшает её на 30 (destX -= 30), становясь 670, и возвращает это значение. Таким образом, с каждым новым циклом (каждым движением "туда") конечная точка сдвигается на 30 пикселей влево.

2. **getStart**: Эта функция определяет, *откуда* начнется движение мяча в новом цикле. Она берет переданное текущее значение (value) — а это будет значение `xв момент старта нового цикла — и прибавляет к нему 30. Поскольку твин работает в режимеyoyo, мяч возвращается в точку старта предыдущего цикла.getStart` смещает эту точку, создавая эффект "дрейфа" всей траектории.

**Итоговое поведение:** Мяч бегает туда-сюда, но с каждым разом амплитуда его бега по оси X уменьшается, а весь путь смещается влево. Визуально это выглядит как затухающие колебания.

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

Этот паттерн не ограничен свойством `x` или линейными изменениями. Вот несколько идей для использования:

* **Случайное движение:** Сделайте движение врага или частицы непредсказуемым.

y: {
    getEnd: function() {
        return Phaser.Math.Between(100, 500);
    }
}

* **Реакция на игрока:** Дистанция или сила прыжка противника может зависеть от расстояния до героя. * **Эффект "пьяной" камеры:** Динамически меняя scrollX и scrollY камеры через твины, можно создать эффект потрясения или дезориентации.

Важно помнить, что функции getStart и getEnd должны быть чистыми и быстрыми, чтобы не тормозить основной игровой цикл.

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

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