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

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

Версия 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.image('mushroom', 'assets/sprites/mushroom16x16.png');
    }

    create ()
    {
        //  64 x 32 = 2048

        for (let y = 0; y < 32; y++)
        {
            for (let x = 0; x < 64; x++)
            {
                this.add.image(x * 16, y * 16, 'mushroom').setOrigin(0);
            }
        }
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 1024,
    height: 512,
    render: {
        //  A custom batch size of 1024 quads
        batchSize: 1024
    },
    scene: Example
};

const game = new Phaser.Game(config);

Проблема: множественные вызовы отрисовки

Каждый спрайт, добавленный на сцену, должен быть отрисован. Если спрайтов тысячи, а система рендеринга отправляет их на GPU по одному, это создает огромную нагрузку и приводит к просадкам FPS.

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

// Без батчинга — 2048 отдельных вызовов отрисовки
for (let y = 0; y < 32; y++) {
    for (let x = 0; x < 64; x++) {
        this.add.image(x * 16, y * 16, 'mushroom').setOrigin(0);
    }
}

Решение: настройка `batchSize` в конфиге

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

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 1024,
    height: 512,
    render: {
        //  Устанавливаем кастомный размер батча в 1024 квада
        batchSize: 1024
    },
    scene: Example
};

В данном примере мы устанавливаем размер батча равным 1024. Это означает, что наши 2048 спрайтов будут разбиты на 2 батча (2048 / 1024 = 2), что в два раза уменьшает количество вызовов к GPU по сравнению с отрисовкой каждого спрайта по отдельности.

Как выбрать оптимальный размер?

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

* **Меньший размер (например, 256):** Требует меньше видеопамяти на один батч, но может увеличить количество вызовов рендеринга. Может быть полезно на слабых устройствах с ограниченной памятью. * **Больший размер (например, 4096):** Уменьшает количество вызовов рендеринга до минимума, но резервирует больше памяти. Идеально для статичных сцен с огромным количеством объектов на мощных ПК.

// Примеры различных конфигураций
// Для мобильной игры
render: { batchSize: 512 }

// Для десктопной игры с тяжелой сценой
render: { batchSize: 2048 }

Рекомендуется проводить тесты производительности (FPS, использование памяти) с разными значениями batchSize для ваших самых загруженных сцен.

Важные ограничения и детали

1. **Только для WebGL.** Данная настройка работает только с рендерером Phaser.WEBGL. В Phaser.CANVAS она не применяется. 2. **Влияние на память.** Батч резервирует область памяти (вершинный буфер) под максимальное количество объектов. Слишком большое значение batchSize может привести к неэффективному использованию памяти, особенно если в сцене редко используется полный батч. 3. **Смена текстуры.** Батч «рвется» при смене текстуры. Если вы отрисовываете спрайты с 'mushroom', а затем с 'star', Phaser завершит текущий батч и начнет новый. Поэтому для максимальной эффективности сортируйте объекты по используемым текстурам.

// Этот код вызовет два батча
this.add.image(100, 100, 'mushroom');
this.add.image(200, 200, 'star'); // Новый батч!
this.add.image(300, 300, 'mushroom'); // Снова батч для 'mushroom'

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

Ручная настройка batchSize — это мощный инструмент для тонкой оптимизации рендеринга в Phaser. Начните со значения по умолчанию и изменяйте его, только если профилирование показывает узкое место, связанное с вызовами отрисовки. Для экспериментов попробуйте создать сцену с 10 тысячами спрайтов и измерьте FPS при batchSize: 64, 512 и 4096. Также исследуйте, как на производительность влияет чередование спрайтов с разными текстурами, и попробуйте предварительно отсортировать их для минимизации разрывов батчей.