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

Производительность графики — ключевой фактор для плавного геймплея, особенно в проектах с большим количеством объектов. Пример 'webgl debug.js' демонстрирует не только базовые принципы управления спрайтами, но и встроенные инструменты Phaser для мониторинга производительности в реальном времени. Умение отслеживать FPS и эффективно управлять объектами за пределами камеры — это первый шаг к созданию оптимизированной игры, которая не будет тормозить на слабых устройствах.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();

        this.donuts = [];
        this.fps;
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('bg', 'assets/skies/grid.png');
        this.load.image('donut', 'assets/sprites/donut.png');
    }

    create ()
    {
        this.add.image(400, 600, 'bg').setOrigin(0.5, 1);

        this.cameras.main.setBounds(0, 0, 800, 600);

        for (let i = 0; i < 16; i++)
        {
            const x = Phaser.Math.Between(0, 800);
            const y = Phaser.Math.Between(200, 600);

            this.donuts.push(this.add.image(x, y, 'donut'));
        }

        this.fps = this.add.text(16, 100, 'fps');
    }

    update ()
    {
        Phaser.Actions.IncX(this.donuts, -2, -0.5);

        Phaser.Actions.WrapInRectangle(this.donuts, this.cameras.main.getBounds(), 128);

        this.fps.setText(this.game.renderer.getFps());
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ресурсов

Класс сцены Example расширяет Phaser.Scene. В конструкторе инициализируются два свойства: массив donuts для хранения спрайтов и переменная fps для текстового объекта.

В методе preload задаётся базовый URL для загрузки и загружаются два изображения: фон (bg) и спрайт пончика (donut). Использование setBaseURL упрощает указание путей к ресурсам.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('bg', 'assets/skies/grid.png');
    this.load.image('donut', 'assets/sprites/donut.png');
}

Создание игрового мира и управление камерой

В методе create сначала добавляется фоновое изображение. Важно отметить использование setOrigin(0.5, 1), которое устанавливает точку привязки (anchor) изображения по центру по X и снизу по Y. Это размещает фон внизу сцены.

Затем устанавливаются границы (bounds) для основной камеры (this.cameras.main). Эти границы определяют область, в которой камера может перемещаться, что полезно для скроллинговых игр.

create ()
{
    this.add.image(400, 600, 'bg').setOrigin(0.5, 1);
    this.cameras.main.setBounds(0, 0, 800, 600);
}

Генерация и анимация объектов

В цикле создаётся 16 спрайтов пончиков. Их начальные координаты X и Y задаются случайным образом с помощью Phaser.Math.Between. Каждый созданный спрайт добавляется в массив this.donuts для последующего управления.

Также создаётся текстовый объект this.fps, который будет отображать текущий FPS. Изначально ему задаётся строка 'fps'.

for (let i = 0; i < 16; i++)
{
    const x = Phaser.Math.Between(0, 800);
    const y = Phaser.Math.Between(200, 600);
    this.donuts.push(this.add.image(x, y, 'donut'));
}
this.fps = this.add.text(16, 100, 'fps');

Обновление состояния и ключевая логика отладки

Метод update выполняется каждый кадр. С помощью Phaser.Actions.IncX всем пончикам в массиве уменьшается координата X на 2, при этом добавляется небольшой случайный разброс в -0.5 пикселя. Это создаёт эффект параллельного скроллинга.

Затем вызывается Phaser.Actions.WrapInRectangle. Эта функция проверяет, находятся ли спрайты в пределах переданного прямоугольника (границ камеры). Если спрайт вышел за его левую границу, он переносится на правую сторону. Это эффективная альтернатива ручной проверке координат для бесконечного фона.

update ()
{
    Phaser.Actions.IncX(this.donuts, -2, -0.5);
    Phaser.Actions.WrapInRectangle(this.donuts, this.cameras.main.getBounds(), 128);
}

Мониторинг производительности (FPS)

Самая важная для отладки строка — this.game.renderer.getFps(). Этот метод рендерера возвращает текущее количество кадров в секунду. Значение обновляется в текстовом объекте this.fps каждый кадр, позволяя в реальном времени наблюдать за производительность рендеринга WebGL (или Canvas).

Это встроенный инструмент Phaser, который не требует подключения дополнительных библиотек.

this.fps.setText(this.game.renderer.getFps());

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

Объект config содержит стандартные настройки игры: автоматический выбор рендерера (WebGL/Canvas), размеры холста, цвет фона, ID родительского HTML-элемента и класс основной сцены.

Игра инициализируется созданием нового экземпляра Phaser.Game с этой конфигурацией.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: Example
};
const game = new Phaser.Game(config);

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

Этот пример наглядно показывает, как с помощью нескольких строк кода можно организовать движение объектов, управлять их положением относительно камеры и, что критически важно, отслеживать производительность. Для экспериментов попробуйте увеличить количество пончиков в цикле и понаблюдайте за изменением FPS. Или замените WrapInRectangle на Wrap для циклического движения по всем границам экрана. Используйте getFps() во всех своих прототипах, чтобы сразу выявлять проблемы с оптимизацией.