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

При разработке игр часто возникает задача отображения множества похожих объектов, например, частиц или статичных спрайтов. Использование отдельных игровых объектов для каждого из них может быть неэффективно. Система Blitter в Phaser предоставляет высокопроизводительный способ рендеринга множества изображений ("бобов") из одного текстурного атласа, минимизируя вызовы отрисовки (draw calls). В этой статье мы разберем, как создать Blitter, наполнить его спрайтами с разными кадрами из атласа и управлять их позиционированием. Этот подход особенно полезен для UI-элементов, фоновых деталей или систем частиц, где важна производительность.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    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 blitter = this.add.blitter(100, 200, 'atlas');

        //  Create some bobs, all using different frames from the same atlas texture.
        //  Note that the x/y coordinates are relative to the blitter position.

        blitter.create(0, 0, 'atari400');
        blitter.create(100, 0, 'bunny').setFlipX();
        blitter.create(200, 0, 'cokecan');
        blitter.create(300, 0, 'copy-that-floppy');
        blitter.create(400, 0, 'hotdog');
    }
}

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

const game = new Phaser.Game(config);

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

Blitter — это специальный игровой объект (Phaser.GameObjects.Blitter), предназначенный для высокопроизводительного вывода множества изображений (спрайтов) на экран. Его ключевое преимущество — возможность рендерить множество "бобов" (отдельных спрайтов) за один вызов отрисовки, если они используют одну и ту же текстуру (или один и тот же атлас).

Это достигается за счет того, что Blitter работает на более низком уровне, чем обычные Sprite объекты, и оптимизирован для групповой обработки. В контексте примера, все спрайты (atari400, bunny и др.) берутся из одного загруженного атласа 'atlas'.

const blitter = this.add.blitter(100, 200, 'atlas');

Здесь мы создаем объект Blitter с начальной позицией (100, 200) на сцене и указываем ключ текстуры 'atlas', из которой будут браться кадры для всех его "бобов".

Создание и настройка "бобов" (Bob)

Каждое отдельное изображение внутри Blitter называется "боб" (Phaser.GameObjects.Bob). Создать боб можно с помощью метода .create(). Важно понимать, что координаты (x, y) при создании боба являются относительными — они отсчитываются не от глобальных координат сцены, а от позиции самого Blitter'а, указанной при его создании (в нашем случае (100, 200)).

blitter.create(0, 0, 'atari400');

Этот код создаст боб, который отобразит кадр с именем 'atari400' из атласа. Боб будет размещен в точке (0, 0) относительно позиции Blitter'а, то есть его итоговая позиция на сцене будет (100 + 0, 200 + 0) = (100, 200).

Метод .create() возвращает созданный объект Bob, что позволяет сразу применять к нему методы, например, для отражения по горизонтали.

blitter.create(100, 0, 'bunny').setFlipX();

Здесь создается боб с кадром 'bunny' на 100 пикселей правее начала Blitter'а, и он сразу же отражается по оси X.

Работа с текстурным атласом

В примере используется текстурный атлас megaset-2. Атлас — это единое изображение, содержащее множество отдельных спрайтов (кадров), и JSON-файл с координатами этих кадров. Такой подход сокращает количество загрузок текстур и улучшает производительность.

Загрузка атласа происходит в методе preload():

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');
}

Метод this.load.atlas() принимает три аргумента: 1. Ключ (key), по которому атлас будет доступен в игре — 'atlas'. 2. Путь к файлу изображения атласа. 3. Путь к JSON-файлу с данными о кадрах (их именах и координатах).

После загрузки, при создании бобов, мы просто указываем имя нужного кадра из этого атласа (например, 'hotdog' или 'cokecan') в качестве третьего аргумента frame метода .create().

Практические аспекты позиционирования

Как уже упоминалось, координаты бобов — относительные. Давайте детальнее рассмотрим строки из примера:

blitter.create(200, 0, 'cokecan');
blitter.create(400, 0, 'hotdog');

* Первый боб (cokecan) будет отрисован в точке (100 + 200, 200 + 0) = (300, 200) на сцене. * Второй боб (hotdog) — в точке (100 + 400, 200 + 0) = (500, 200).

Это позволяет легко управлять группой спрайтов как единым целым, перемещая только родительский Blitter. Если изменить позицию Blitter'а после создания:

blitter.setPosition(50, 300);

Все его бобы автоматически сместятся, сохраняя свои относительные позиции друг относительно друга. Это удобно для создания сложных составных объектов или интерфейсов.

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

Blitter — это мощный инструмент Phaser для оптимизации рендеринга множества статичных или редко меняющихся спрайтов из общего атласа. Он идеально подходит для создания фонов, интерфейсов, тайловых карт или простых систем частиц, где критически важна производительность. Для экспериментов попробуйте: 1. Анимировать положение всего Blitter'а, чтобы двигать группу спрайтов вместе. 2. Создать несколько Blitter'ов с разными атласами и сравнить производительность с использованием обычных Sprite объектов. 3. Реализовать простую систему знаков или иконок на карте, динамически создавая и уничтожая бобов в одном Blitter'е в зависимости от игровой логики.