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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    iter = 0;
    images = [];

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.atlas('atlas', 'assets/atlas/megaset-2.png', 'assets/atlas/megaset-2.json');
    }

    create ()
    {
        const frames = [ 'atari400', 'bunny', 'cokecan', 'copy-that-floppy', 'hotdog' ];
        for (let i = 0; i < frames.length; ++i)
        {
            this.images[i] = this.add.tileSprite(i * 160, 0, 160, 600, 'atlas', frames[i]);
            this.images[i].originX = 0;
            this.images[i].originY = 0;
        }
    }

    update ()
    {
        let x = 1;
        for (let i = 0; i < this.images.length; ++i)
        {
            this.images[i].tilePositionX += x;
            x *= -1;
        }
        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);

Загрузка ресурсов: работа с атласом

Перед созданием спрайтов необходимо загрузить текстуры. Вместо загрузки множества отдельных изображений используется один атлас — большая картинка со всеми спрайтами и JSON-файл, описывающий координаты каждого кадра.

В методе preload мы задаём базовый URL для загрузки и вызываем метод load.atlas. Он принимает три аргумента: уникальный ключ атласа, путь к изображению и путь к JSON-файлу с данными.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('atlas', 'assets/atlas/megaset-2.png', 'assets/atlas/megaset-2.json');

Создание Tile Sprite из кадров атласа

В методе create мы создаём игровые объекты. Ключевой момент — использование this.add.tileSprite. В отличие от обычного спрайта, Tile Sprite предназначен для повторяющейся (плиткуемой) текстуры.

Сначала мы определяем массив с именами кадров (frames), которые хотим использовать из загруженного атласа. Затем в цикле создаём Tile Sprite для каждого кадра.

Конструктор add.tileSprite принимает координаты X, Y, ширину, высоту, ключ текстуры (атласа) и имя конкретного кадра из этого атласа.

Свойствам originX и originY задаётся значение 0. Это меняет точку привязки (origin) спрайта с центра (значение по умолчанию) на его левый верхний угол. Это упрощает позиционирование, особенно когда спрайты выстраиваются в ряд.

const frames = [ 'atari400', 'bunny', 'cokecan', 'copy-that-floppy', 'hotdog' ];
for (let i = 0; i < frames.length; ++i)
{
    this.images[i] = this.add.tileSprite(i * 160, 0, 160, 600, 'atlas', frames[i]);
    this.images[i].originX = 0;
    this.images[i].originY = 0;
}

Анимация через управление tilePosition

Магия движения Tile Sprite заключается в свойстве tilePosition. Оно представляет собой объект Phaser.Math.Vector2, который определяет смещение внутренней текстуры относительно её начала.

Изменяя tilePositionX или tilePositionY, мы сдвигаем текстуру внутри спрайта. Поскольку текстура зациклена, это создаёт плавное, бесконечное движение.

В методе update, который вызывается каждый кадр, мы проходим по массиву созданных спрайтов this.images и прибавляем к свойству tilePositionX значение переменной `x. Особенность данного примера — переменнаяxменяет знак на противоположный для каждого следующего спрайта (x *= -1`). Это заставляет соседние плитки двигаться в противоположных направлениях, создавая интересный визуальный эффект.

let x = 1;
for (let i = 0; i < this.images.length; ++i)
{
    this.images[i].tilePositionX += x;
    x *= -1;
}
this.iter += 0.01;

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

Для запуска примера необходима стандартная конфигурация игры Phaser. В объекте config мы указываем тип рендерера (WEBGL), размеры холста, цвет фона, ID родительского HTML-элемента и класс нашей сцены Example.

Затем создаётся экземпляр игры new Phaser.Game(config), который автоматически инициализирует сцену и начинает игровой цикл.

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

const game = new Phaser.Game(config);

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

Tile Sprite — мощный и производительный инструмент для создания динамических фонов и текстурных анимаций. Изменяя скорость и направление tilePosition, можно легко симулировать течение реки, движение облаков или бег по бесконечной дороге. Для экспериментов попробуйте: управлять tilePositionY для вертикального скролла; привязать скорость смещения к скорости игрового персонажа для эффекта параллакса; использовать разные математические функции для tilePosition, чтобы создать волнообразное или прерывистое движение.