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

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

Версия 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/sky1.png');
        this.load.image('golem1', 'assets/tweens/golem1.png');
        this.load.image('golem2', 'assets/tweens/golem2.png');
        this.load.spritesheet('ice', 'assets/tweens/ice-48x48.png', { frameWidth: 48, frameHeight: 48 });
    }

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

        const golem = this.add.image(400, 300, 'golem2');

        //  This is just a particle emitter for our effect
        const particles = this.add.particles(400, 230, 'ice', {
            lifespan: 2000,
            speed: 360,
            angle: { start: 40, end: 150, steps: 8 },
            quantity: 0
        });

        //  Here we'll change the texture from 'golem1' to 'golem2' after 1000 ms.
        //  Because we set it to repeat -1 it will then set it back to golem1
        //  again after a second, then repeat. We also set the particles to explode
        //  during the onRepeat callback.
        this.tweens.add({
            targets: golem,
            texture: 'golem1',
            duration: 1000,
            repeat: -1,
            onRepeat: () => {
                particles.explode(8);
            }
        });
    }
}

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

const game = new Phaser.Game(config);

Подготовка ресурсов

Первым делом в методе preload загружаются все необходимые изображения. Обратите внимание на разные типы загрузки: для статичного фона и двух текстур голема используется this.load.image, а для листа спрайтов частиц льда — this.load.spritesheet с указанием размера кадра.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/skies/sky1.png');
    this.load.image('golem1', 'assets/tweens/golem1.png');
    this.load.image('golem2', 'assets/tweens/golem2.png');
    this.load.spritesheet('ice', 'assets/tweens/ice-48x48.png', { frameWidth: 48, frameHeight: 48 });
}

Здесь 'golem1' и 'golem2' — это разные текстуры одного и того же персонажа, которые мы будем менять местами. Лист спрайтов 'ice' будет использован для системы частиц.

Создание сцены и объектов

В методе create мы размещаем фон и нашего персонажа — голема. Изначально он отображается с текстурой 'golem2'. Также создаётся эмиттер частиц, который будет использован для визуального акцента при смене текстуры.

create ()
{
    this.add.image(400, 300, 'bg');
    const golem = this.add.image(400, 300, 'golem2');
    const particles = this.add.particles(400, 230, 'ice', {
        lifespan: 2000,
        speed: 360,
        angle: { start: 40, end: 150, steps: 8 },
        quantity: 0
    });
}

Эмиттер частиц настроен на нулевое начальное количество (quantity: 0), поэтому при создании частицы не появляются. Они будут испускаться позже по команде. Параметры lifespan, speed и angle определяют, как долго и в каком направлении будут лететь частицы.

Магия твина: анимируем смену текстуры

Сердце примера — создание твина с помощью this.tweens.add. Ключевой момент: мы анимируем не стандартное свойство вроде `xилиalpha, а свойствоtexture`.

this.tweens.add({
    targets: golem,
    texture: 'golem1',
    duration: 1000,
    repeat: -1,
    onRepeat: () => {
        particles.explode(8);
    }
});

Разберём параметры: * targets: golem: Указывает, к какому объекту применяется твин. * texture: 'golem1': Целевое значение свойства. Твин плавно сменит текущую текстуру объекта ('golem2') на указанную ('golem1'). * duration: 1000: Длительность одной смены текстуры — 1000 миллисекунд (1 секунда). * repeat: -1: Заставляет твин повторяться бесконечно. После достижения текстуры 'golem1' твин автоматически возвращается к начальному значению ('golem2') и начинает снова. * onRepeat: Callback-функция, которая вызывается каждый раз, когда твин начинает повтор (то есть в момент возврата к началу цикла). Здесь мы запускаем взрыв частиц.

Синхронизация с визуальными эффектами

Чтобы сделать смену текстуры более зрелищной, в callback onRepeat мы вызываем метод explode у эмиттера частиц.

onRepeat: () => {
    particles.explode(8);
}

Метод particles.explode(8) мгновенно создаёт 8 частиц в точке эмиттера (400, 230) с теми параметрами, которые были заданы при его создании (скорость, угол, время жизни). Поскольку onRepeat срабатывает в момент "переключения" текстуры обратно, взрыв частиц визуально сопровождает каждый цикл анимации, создавая эффект магического превращения или ломки льда.

Таким образом, у нас получается бесконечный цикл: голем меняет текстуру с golem2 на golem1 за 1 секунду, затем обратно на golem2 за следующую секунду, и при каждом обратном переключении вверху экрана взрываются ледяные частицы.

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

Твины в Phaser — мощный инструмент не только для перемещения объектов, но и для анимации их свойств, включая смену текстур. Этот приём отлично подходит для создания эффектов мигания, фазовых превращений, индикации состояния (например, получение урона) или простой покадровой анимации из двух кадров. Для экспериментов попробуйте: изменить duration для более быстрой или медленной смены; использовать yoyo: true вместе с repeat для другого паттерна повторения; анимировать свойство frame для спрайтов из атласа; или привязать вызов particles.explode к другому событию твина, например onUpdate.