О чем этот пример
При разработке игр часто возникает задача отрисовки множества однотипных графических объектов, например, частиц, звёзд или элементов окружения. Простой перебор и создание отдельных спрайтов через `this.add.sprite()` может серьёзно снизить производительность. В этой статье разберём, как использовать систему `Blitter` в связке с `Multi Atlas` для эффективного вывода сотен и тысяч объектов на экран. Этот подход особенно полезен для создания динамических сцен с большим количеством визуальных элементов, где критична скорость отрисовки.
Версия 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.multiatlas('megaset', 'assets/loader-tests/texture-packer-multi-atlas.json', 'assets/loader-tests/');
}
create ()
{
const blitter = this.add.blitter(0, 0, 'megaset');
// Create some bobs, all using different frames from the same atlas texture.
// Note that the x/y coordinates are relative to the blitter position.
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 1024);
const y = Phaser.Math.Between(0, 768);
blitter.create(x, y, frames[i]);
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Что такое Blitter и зачем он нужен
Класс Blitter — это специальный игровой объект в Phaser, предназначенный для быстрого вывода множества изображений (так называемых "бобов" — bobs) из одного текстуры или атласа. В отличие от стандартных спрайтов, каждый "боб" не является самостоятельным игровым объектом со своей логикой обновления и физикой. Это делает Blitter идеальным инструментом для статичных или редко меняющихся объектов, где важна именно скорость отрисовки.
Использование Blitter может дать значительный прирост производительности, так как все его "бобы" рисуются за один вызов отрисовки (draw call) на GPU. Это гораздо эффективнее, чем сотни отдельных вызовов для каждого спрайта.
Подготовка ресурсов: загрузка Multi Atlas
Перед созданием Blitter необходимо загрузить графические ресурсы. В примере используется Multi Atlas — формат атласа, который может состоять из нескольких изображений (листов), описанных одним JSON-файлом.
Метод load.multiatlas() принимает три аргумента: ключ для дальнейшего обращения к атласу, путь к JSON-файлу с описанием кадров и базовый путь к самим изображениям.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.multiatlas('megaset', 'assets/loader-tests/texture-packer-multi-atlas.json', 'assets/loader-tests/');
После выполнения этого кода в preload() все изображения из атласа 'megaset' будут доступны в кэше текстур.
Создание контейнера Blitter и получение кадров
В функции create() мы создаём основной контейнер — объект Blitter. Его координаты (0, 0) задают точку отсчёта для всех добавляемых в него "бобов". Третий параметр — ключ текстуры, из которой будут браться изображения.
const blitter = this.add.blitter(0, 0, 'megaset');
Далее нам нужно получить список всех кадров (фреймов), которые содержатся в загруженном атласе. Для этого сначала получаем объект текстуры по ключу, а затем список имён её кадров.
const atlasTexture = this.textures.get('megaset');
const frames = atlasTexture.getFrameNames();
Массив frames теперь содержит строковые имена всех доступных изображений внутри атласа 'megaset'.
Генерация множества Bobs
Самый интересный этап — создание множества "бобов" внутри нашего Blitter. В примере для каждого кадра из атласа генерируется один "боб" со случайными координатами в пределах экрана.
Метод blitter.create() принимает координаты X, Y и имя кадра из атласа. Важно помнить, что координаты задаются относительно позиции самого Blitter (в нашем случае, так как он на (0,0), это абсолютные координаты на экране).
for (let i = 0; i < frames.length; i++)
{
const x = Phaser.Math.Between(0, 1024);
const y = Phaser.Math.Between(0, 768);
blitter.create(x, y, frames[i]);
}
Phaser.Math.Between используется для получения случайного целого числа в заданном диапазоне. Таким образом, на экране появляется столько "бобов", сколько кадров в атласе, каждый со своим уникальным изображением и позицией.
Сборка игры и конфигурация
Весь код должен быть помещён в сцену (Scene), которая затем передаётся в основную конфигурацию игры Phaser.Game. Конфиг задаёт базовые параметры, такие как тип рендерера, размер холста и корневую сцену.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
После создания экземпляра game Phaser автоматически запустит жизненный цикл сцены: вызовет preload(), а затем create(), в результате чего на экране появится наша сцена с Blitter.
Что попробовать дальше
Использование Blitter с Multi Atlas — это мощный и производительный способ отрисовки большого количества графических объектов в Phaser. Он идеально подходит для создания фонов, частиц, звёздного неба или любого другого статичного, но многочисленного контента. Для экспериментов попробуйте: анимировать "бобы", меняя их кадры с течением времени; привязать их движение к курсору мыши; или использовать один и тот же кадр много раз, чтобы создать эффект дождя или снегопада с тысячами частиц без падения FPS.
