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

Статичные фоны — это скучно. В современных играх даже задние планы оживают, создавая иллюзию глубины и движения. В этой статье разберем мощный инструмент Phaser 3 — `TileSprite` (плиточный спрайт). Он позволяет бесшовно заполнять область выбранной текстурой и управлять её смещением, что идеально подходит для создания бесконечных прокручивающихся фонов, анимированных небес или водных поверхностей. Мы на практическом примере посмотрим, как заставить две текстуры плавно вращаться вокруг друг друга, создавая гипнотический визуальный эффект.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    iter = 0;
    image1;
    image0;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('image0', 'assets/pics/ra-einstein.png');
        this.load.image('image1', 'assets/sprites/mushroom2.png');
    }

    create ()
    {
        this.image0 = this.add.tileSprite(400, 300, 500, 500, 'image0');

        this.image0.setAngle(25);
        this.image0.setScale(0.5);

        this.image1 = this.add.tileSprite(400, 300, 150, 150, 'image1');
    }

    update ()
    {
        this.image0.tilePositionX = Math.cos(this.iter) * 400;
        this.image0.tilePositionY = Math.sin(this.iter) * 400;
        this.image1.tilePositionX = Math.cos(-this.iter) * 400;
        this.image1.tilePositionY = Math.sin(-this.iter) * 400;
        this.iter += 0.01;
    }
}

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

const game = new Phaser.Game(config);

Что такое TileSprite и зачем он нужен

TileSprite — это игровой объект, который повторяет (плиткует) заданное изображение, чтобы заполнить область указанного размера. В отличие от обычного спрайта, который просто отображает картинку один раз, TileSprite создает из нее бесшовный узор.

Ключевое свойство — вы можете управлять позицией этого узора с помощью параметров tilePositionX и tilePositionY. Меняя их, вы создаете эффект плавного скольжения текстуры. Это основа для параллакс-прокрутки, текущей реки или мерцающего звездного поля.

В нашем примере создаются два таких объекта: большой фоновый и маленький поверх него. Оба будут анимированы.

Загрузка ресурсов и создание сцены

Вся работа начинается в стандартных методах жизненного цикла сцены Phaser. В preload() мы загружаем две текстуры.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('image0', 'assets/pics/ra-einstein.png');
    this.load.image('image1', 'assets/sprites/mushroom2.png');
}

В методе create(), который выполняется один раз после загрузки, мы создаем наши TileSprite объекты. Обратите внимание на аргументы: координаты X и Y центра спрайта, его ширина и высота, и ключ текстуры.

create ()
{
    // Создаем фоновый TileSprite размером 500x500 пикселей
    this.image0 = this.add.tileSprite(400, 300, 500, 500, 'image0');

    // Поворачиваем его на 25 градусов и уменьшаем масштаб
    this.image0.setAngle(25);
    this.image0.setScale(0.5);

    // Создаем второй, меньший TileSprite поверх первого
    this.image1 = this.add.tileSprite(400, 300, 150, 150, 'image1');
}

Магия анимации в методе update

Чтобы оживить текстуры, нужно изменять их свойства в каждом кадре. Для этого используется метод update(). Мы будем менять свойства tilePositionX и tilePositionY, которые определяют смещение внутренней текстуры.

Логика проста: используем тригонометрические функции Math.cos и Math.sin от переменной-счетчика this.iter. Это дает нам плавные циклические значения в диапазоне от -1 до 1, которые мы умножаем на коэффициент (400) для увеличения амплитуды движения.

update ()
{
    // Двигаем текстуру фона (image0) по кругу
    this.image0.tilePositionX = Math.cos(this.iter) * 400;
    this.image0.tilePositionY = Math.sin(this.iter) * 400;

    // Двигаем текстуру спрайта (image1) по кругу, но в противоположном направлении
    this.image1.tilePositionX = Math.cos(-this.iter) * 400;
    this.image1.tilePositionY = Math.sin(-this.iter) * 400;

    // Увеличиваем счетчик, чтобы angle (аргумент для cos/sin) менялся со временем
    this.iter += 0.01;
}

Изменяя знак у this.iter для второго спрайта (-this.iter), мы заставляем его вращаться в противоположную сторону, создавая сложный и интересный визуальный паттерн.

Конфигурация и запуск игры

Финальный шаг — стандартная конфигурация игры Phaser и её инстанцирование. Обратите внимание, что в config.type указан Phaser.WEBGL для использования аппаратного рендеринга.

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

const game = new Phaser.Game(config);

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

TileSprite — это элегантное решение для создания динамических фонов и текстурных эффектов. Меняя tilePosition и tileScale, вы можете имитировать поток воды, бег облаков или движение через гиперпространство. Для экспериментов попробуйте: изменить множитель 400 на другие значения для контроля скорости и радиуса «вращения» текстуры; анимировать свойство tileScaleX/Y для эффекта «приближения»; или привязать смещение текстуры к скорости игрового персонажа для реалистичной параллакс-прокрутки.