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

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

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

    create ()
    {
        this.marker = this.add.image(100, 300, 'block').setAlpha(0.3);
        this.image = this.add.image(100, 300, 'block');

        this.moveBlock();
    }

    resetBlock()
    {
        this.tweens.add({
            targets: this.image,
            x: 100,
            duration: 1000,
            ease: 'Power2',
            completeDelay: 2000,
            onComplete: function () {
                this.moveBlock();
            }.bind(this),
        });
    }

    moveBlock()
    {
        this.tweens.add({
            targets: this.image,
            x: 700,
            duration: 1000,
            ease: 'Power2',
            completeDelay: 2000,
            onComplete: function () {
                this.resetBlock();
            }.bind(this),
        });
    }
}

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

var game = new Phaser.Game(config);

Что делает параметр `completeDelay`?

Параметр completeDelay в конфигурации твина задает задержку в миллисекундах **перед** вызовом функции обратного вызова onComplete. Это ключевой момент: задержка происходит уже после того, как основная анимация (например, перемещение спрайта из точки А в точку Б) завершена, но до того, как выполнится код в onComplete.

Это позволяет создавать четкие последовательности: действие -> пауза -> следующее действие. Без этого параметра пришлось бы вручную создавать таймеры или вкладывать твины, что усложнило бы код.

Разбор примера: бесконечное движение блока

В предоставленном примере создается сцена с двумя спрайтами: основной (this.image) и полупрозрачный маркер (this.marker), показывающий начальную позицию.

Цикл анимации запускается в методе create() вызовом this.moveBlock().

create ()
{
    this.marker = this.add.image(100, 300, 'block').setAlpha(0.3);
    this.image = this.add.image(100, 300, 'block');

    this.moveBlock();
}

Основная логика заложена в двух методах: moveBlock() и resetBlock(). Они вызывают друг друга через onComplete, создавая бесконечный цикл 'туда-сюда'.

Анализ метода `moveBlock`

Метод moveBlock запускает твин, который перемещает блок от координаты x=100 к x=700.

this.tweens.add({
    targets: this.image, // Цель анимации
    x: 700,             // Конечная координата X
    duration: 1000,     // Длительность движения (1 секунда)
    ease: 'Power2',     // Функция плавности
    completeDelay: 2000, // Ключевой параметр: пауза в 2 секунды ПОСЛЕ движения
    onComplete: function () {
        this.resetBlock(); // После паузы вызываем метод сброса
    }.bind(this),
});

Последовательность: 1. Блок движется вправо за 1 секунду. 2. Блок **останавливается** на 2 секунды (благодаря completeDelay: 2000). 3. Срабатывает onComplete и вызывает resetBlock().

Анализ метода `resetBlock`

Метод resetBlock зеркально противоположен. Он возвращает блок на стартовую позицию.

this.tweens.add({
    targets: this.image,
    x: 100,            // Возвращаем блок обратно, к маркеру
    duration: 1000,
    ease: 'Power2',
    completeDelay: 2000, // Снова пауза в 2 секунды после возвращения
    onComplete: function () {
        this.moveBlock(); // После паузы снова запускаем движение вправо
    }.bind(this),
});

Таким образом, цикл становится бесконечным: движение вправо (1с) -> пауза (2с) -> движение влево (1с) -> пауза (2с) -> и так далее. Обратите внимание на использование .bind(this) в onComplete — это необходимо, чтобы внутри функции-колбэка this указывал на экземпляр сцены, а не на сам твин.

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

Параметр completeDelay универсален. Вот несколько идей, как его можно применить:

* **Патрулирование врага:** Задайте несколько ключевых точек. После достижения каждой точки добавляйте completeDelay, чтобы враг 'осматривался'. * **Мигание элемента интерфейса:** Твин на изменение alpha с completeDelay создаст эффект мигания с паузами. * **Задержка перед исчезновением:** После получения урона спрайт может покраснеть (твин цвета), подождать (completeDelay) и только потом исчезнуть или вернуться в нормальное состояние.

Важно: completeDelay — это задержка **после** анимации. Если вам нужна задержка **перед** началом анимации, используйте параметр delay.

// Задержка ПЕРЕД началом движения
this.tweens.add({
    targets: this.image,
    x: 700,
    duration: 1000,
    delay: 2000, // Ждем 2 секунды, потом начинаем движение
    onComplete: function() { /* ... */ }
});

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

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