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

Создание плавной анимации — ключевая часть игрового процесса. Но что, если вам нужно замедлить или ускорить все происходящее на сцене, например, для эффекта замедления времени или ускоренного перемотки? Phaser предоставляет для этого мощный инструмент — глобальный `timeScale` для системы твинов. В этой статье мы разберем, как централизованно управлять скоростью всех анимаций в игре, используя всего одно свойство. Это полезно для создания паузы, эффектов буста или синхронизации анимаций с игровыми событиями.

Версия 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/ui/undersea-bg.png');
        this.load.image('up', 'assets/ui/up-bubble.png');
        this.load.image('down', 'assets/ui/down-bubble.png');
        this.load.spritesheet('fish', 'assets/sprites/fish-136x80.png', { frameWidth: 136, frameHeight: 80 });
    }

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

        const image1 = this.add.image(0, 80, 'fish', 0);

        this.tweens.add({
            targets: image1,
            props: {
                x: { value: 700, duration: 4000, flipX: true },
                y: { value: 500, duration: 8000,  },
            },
            ease: 'Sine.easeInOut',
            yoyo: true,
            repeat: -1
        });

        const image2 = this.add.image(400, 80, 'fish', 1);

        this.tweens.add({
            targets: image2,
            props: {
                x: { value: 500, duration: 2000, flipX: true },
                y: { value: 500, duration: 10000,  },
            },
            ease: 'Sine.easeInOut',
            yoyo: true,
            repeat: -1
        });

        const image3 = this.add.image(800, 200, 'fish', 2).setFlipX(true);

        this.tweens.add({
            targets: image3,
            props: {
                x: { value: 70, flipX: true },
                y: { value: 250 },
            },
            duration: 3000,
            ease: 'Power1',
            yoyo: true,
            repeat: -1
        });

        const image4 = this.add.image(100, 550, 'fish', 2).setScale(0.75);

        this.tweens.add({
            targets: image4,
            props: {
                x: { value: 700, duration: 2000, flipX: true },
                y: { value: 50, duration: 15000,  },
            },
            ease: 'Sine.easeInOut',
            yoyo: true,
            repeat: -1
        });

        //  Buttons to control the Tween timescale

        const text = this.add.text(180, 0, 'Global timeScale: 1').setFont('32px Arial Black').setFill('#ffffff').setShadow(2, 2, "#333333", 2);

        const downButton = this.add.image(70, 530, 'down').setInteractive();

        downButton.on('pointerdown', () => {

            if (this.tweens.timeScale > 0)
            {
                this.tweens.timeScale -= 0.1;
            }

            text.setText('Global timeScale: ' + this.tweens.timeScale.toFixed(2));

        });

        const upButton = this.add.image(730, 530, 'up').setInteractive();

        upButton.on('pointerdown', () => {

            if (this.tweens.timeScale < 9.9)
            {
                this.tweens.timeScale += 0.1;
            }

            text.setText('Global timeScale: ' + this.tweens.timeScale.toFixed(2));

        });
    }
}

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

const game = new Phaser.Game(config);

Что такое `tweens.timeScale`?

Система твинов в Phaser (this.tweens) управляет всеми плавными анимациями (tweens) в сцене. Свойство timeScale — это глобальный множитель скорости для всех активных твинов, созданных через этот менеджер.

Значение 1.0 означает нормальную скорость. Значение 0.5 замедлит все анимации вдвое, а 2.0 — ускорит в два раза. Установка значения в `0` фактически поставит все твины на паузу, не удаляя их.

Создание анимированной сцены

Прежде чем управлять скоростью, нужно создать анимации. В примере загружается фон и спрайты рыб, после чего для каждой рыбы создается индивидуальный tween с помощью this.tweens.add(). Каждый tween описывает движение по осям X и Y с разной длительностью (duration), эффектом отскока (yoyo) и бесконечным повторением (repeat: -1).

this.tweens.add({
    targets: image1,
    props: {
        x: { value: 700, duration: 4000, flipX: true },
        y: { value: 500, duration: 8000,  },
    },
    ease: 'Sine.easeInOut',
    yoyo: true,
    repeat: -1
});

Ключевой момент: все эти твины автоматически регистрируются в глобальном менеджере this.tweens текущей сцены. Поэтому изменение this.tweens.timeScale повлияет на них одновременно.

Добавление элементов управления

Для интерактивного управления скоростью в сцене создаются две кнопки (спрайты 'up' и 'down') и текстовое поле. Кнопкам назначается обработчик события 'pointerdown'.

При нажатии на кнопку downButton значение timeScale уменьшается на 0.1, но не опускается ниже 0. Аналогично, кнопка upButton увеличивает timeScale на 0.1, ограничиваясь сверху значением 9.9.

downButton.on('pointerdown', () => {
    if (this.tweens.timeScale > 0) {
        this.tweens.timeScale -= 0.1;
    }
    text.setText('Global timeScale: ' + this.tweens.timeScale.toFixed(2));
});

Текст обновляется после каждого изменения, отображая текущее значение с округлением до двух знаков. Обратите внимание на прямой доступ к свойству this.tweens.timeScale — это и есть весь API, необходимый для управления.

Как это работает под капотом?

Когда вы создаете tween, Phaser рассчитывает приращения для его свойств (например, координат) на каждом кадре, основываясь на прошедшем времени и заданной длительности (duration). Глобальный timeScale модифицирует это "прошедшее время" для расчетов всех твинов.

Фактически, внутренний таймер системы твинов умножается на timeScale. Поэтому при timeScale = 0 время для твинов не течет, и их значения не меняются — анимация замирает. При timeScale = 0.5 время для твинов течет в два раза медленнее, и они достигают конечных точек за вдвое большее реальное время.

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

Глобальный timeScale — это не просто демо-эффект. Вот несколько практических сценариев его использования: * **Эффект замедления времени (Bullet Time):** Установите this.tweens.timeScale = 0.3 при активации специальной способности. * **Пауза в меню:** При открытии игрового меню установите значение `0`, чтобы анимации на заднем плане (например, парящие частицы) остановились. * **Ускоренная перемотка:** В стратегических играх для ускорения длительных процессов можно временно выставить значение 3.0 или 5.0.

Важно: timeScale влияет только на твины, созданные через this.tweens. Анимации спрайтов (anims), таймеры событий (time.addEvent) или физика имеют свои отдельные настройки масштаба времени.

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

Глобальное свойство this.tweens.timeScale — это элегантный и мощный способ централизованного управления скоростью анимаций в Phaser. С его помощью можно легко реализовать сложные визуальные эффекты и игровые механики, связанные с течением времени. **Идеи для экспериментов:** 1. Свяжите изменение timeScale не с кнопками, а с колесом мыши для плавного контроля. 2. Создайте tween для самого свойства this.tweens.timeScale, чтобы скорость анимаций плавно нарастала или спадала. 3. Попробуйте привязать значение timeScale к здоровью игрока — чем оно меньше, тем медленнее мир вокруг (эффект "близости к смерти").