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

Анимации на основе tween (или 'твинов') — это мощный инструмент для создания плавного движения в играх. Но что, если нужно не просто запустить анимацию с начала, а перескочить в её середину или перемотать? Метод `tween.seek()` даёт вам точный контроль над временной шкалой твина, позволяя мгновенно перемещать объект в любую точку анимационного пути. Это полезно для реализации функций перемотки кат-сцен, создания интерактивных элементов, реагирующих на клик, или для отладки сложных последовательностей движения.

Версия 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/skies/space2.png');
        this.load.image('car', 'assets/sprites/car-yellow.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const car = this.add.image(0, 300, 'car')

        const tween = this.add.tween({
            targets: car,
            x: 800,
            duration: 4000,
            yoyo: true,
            ease: 'Linear',
            repeat: -1
        });

        // tween.seek(0.5);

        this.input.on('pointerdown', (pointer) => {

            // if (pointer.x > 400)
            // {
            //     tween.forward(500);
            // }
            // else
            // {
            //     tween.rewind(500);
            // }

            tween.seek(1000);

        });
    }
}

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

const game = new Phaser.Game(config);

Суть метода Tween.seek()

Метод tween.seek() позволяет мгновенно переместить воспроизведение твина в указанную временную позицию. Вы задаёте значение в миллисекундах от начала анимации, и все свойства целевых объектов немедленно обновляются, как если бы твин проигрывался до этого момента.

Это отличается от паузы или остановки. seek() не влияет на состояние воспроизведения (playback state) твина — если твин был активен, он продолжит проигрываться с новой позиции. Это мощный инструмент для нелинейного управления анимацией.

Разбираем пример кода

В примере создаётся бесконечно повторяющийся твин для автомобиля, который движется от x:0 до x:800 и обратно (благодаря флагу yoyo).

const tween = this.add.tween({
    targets: car,
    x: 800,
    duration: 4000,
    yoyo: true,
    ease: 'Linear',
    repeat: -1
});

Изначально строка tween.seek(0.5); закомментирована. Если её раскомментировать, твин начнёт воспроизведение не с начальной позиции машины (x=0), а с позиции, соответствующей 0.5 миллисекундам от его продолжительности. Так как длительность 4000мс, это практически начало, и видимого эффекта не будет. Для работы seek() с долями необходимо использовать значение от 0 до 1, что в текущем API Phaser 3 не поддерживается — метод ожидает время в миллисекундах.

Основная логика привязана к событию клика (pointerdown). При любом клике на сцену вызывается tween.seek(1000).

this.input.on('pointerdown', (pointer) => {
    tween.seek(1000);
});

Это означает: независимо от того, где сейчас находится машина в своём цикле анимации, она мгновенно «перепрыгнет» в состояние, в котором была бы на 1000-й миллисекунде (1 секунде) проигрывания твина с самого начала. Так как полный путь в одну сторону длится 4000мс, через 1000мс машина окажется на четверти пути, то есть примерно на x:200.

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

Метод seek() раскрывает свой потенциал в сочетании с другими методами управления твинами. В закомментированном коде примера показана альтернативная логика, основанная на позиции клика и методах forward() и rewind(). Это наводит на мысль о более сложных сценариях.

Например, можно создать интерактивный ползунок прогресса для анимации:

// Предположим, у нас есть слайдер, который возвращает значение от 0 до 1
slider.on('change', (value) => {
    // Переводим долю от общей длины анимации в миллисекунды
    const seekTime = value * tween.totalDuration;
    tween.seek(seekTime);
});

Или использовать seek() для синхронизации нескольких твинов, запуская их с разных временных точек для создания сложных сценарных последовательностей.

Важные нюансы и ограничения

1. **Аргумент в миллисекундах:** seek(time) принимает время в миллисекундах, а не нормализованное значение от 0 до 1. Используйте tween.totalDuration для расчётов. 2. **Влияние на repeat и yoyo:** Метод учитывает общую прогрессию твина, включая повторы (repeat) и возвраты (yoyo). seek(5000) в твине длительностью 4000мс с repeat: -1 переведёт его во время 1000мс второго повторения. 3. **Мгновенное обновление:** Переход происходит без интерполяции. Если нужна плавная перемотка, используйте комбинацию tween.stop(), tween.seek() и tween.play() или методы forward()/rewind(). 4. **Состояние твина:** seek() не меняет состояния (isPlaying, isPaused). Если твин был на паузе, он останется на паузе в новой позиции.

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

Метод Tween.seek() — это ваш шанс напрямую взаимодействовать с временной осью анимации в Phaser. Он открывает двери для создания нелинейного, реактивного и отлаживаемого геймплея. Для экспериментов попробуйте: создать систему «призрака» в гонке, записывая траекторию и используя seek() для её воспроизведения; реализовать интерактивную временную шкалу для кат-сцены; или связать прогресс твина со звуковой дорожкой для идеальной аудиовизуальной синхронизации.