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

По умолчанию Phaser создаёт и управляет HTML-элементом `<canvas>` самостоятельно. Однако для продвинутых сценариев, таких как интеграция с другими графическими библиотеками, использование нестандартных атрибутов контекста или ручное управление DOM-структурой, вам может потребоваться предоставить фреймворку свой собственный, предварительно созданный canvas. В этой статье мы разберём, как передать Phaser'у пользовательский WebGL-контекст, настроить его параметры и интегрировать результат в страницу, получив полный контроль над процессом инициализации графического вывода.

Версия 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("pic", "assets/pics/baal-loader.png");
    }

    create() {
        this.add.image(400, 300, "pic");
    }
}

const contextCreationConfig = {
    alpha: false,
    depth: false,
    antialias: true,
    premultipliedAlpha: true,
    stencil: true,
    preserveDrawingBuffer: false,
    failIfMajorPerformanceCaveat: false,
    powerPreference: "default",
};

const myCustomCanvas = document.createElement("canvas");
const myCustomContext = myCustomCanvas.getContext(
    "webgl",
    contextCreationConfig
);

myCustomCanvas.id = "myCustomCanvas";
myCustomCanvas.style = "border: 8px solid green";

document.body.appendChild(myCustomCanvas);

const config = {
    type: Phaser.WEBGL,
    parent: "phaser-example",
    width: 800,
    height: 600,
    canvas: myCustomCanvas,
    context: myCustomContext,
    scene: Example,
};

const game = new Phaser.Game(config);

Зачем нужен пользовательский канвас?

Передача своего canvas и контекста в конфигурацию игры позволяет:

* **Интеграция с существующей инфраструктурой:** Внедрить Phaser в уже созданный и стилизованный элемент страницы. * **Тонкая настройка WebGL:** Задать специфические параметры контекста рендеринга (alpha, antialias, powerPreference и др.), которые не доступны через стандартную конфигурацию Phaser. * **Разделение контекстов:** Использовать один canvas для Phaser, а другой — для сторонних WebGL-библиотек или кастомного рисования, избегая конфликтов. * **Прямой доступ к элементу:** Получить ссылку на DOM-элемент canvas до создания экземпляра Phaser.Game, чтобы, например, добавить обработчики событий или применить сложные CSS-стили.

В примере мы создадим canvas с зелёной рамкой и настроенным WebGL-контекстом, который затем будет использован движком.

Создание и настройка WebGL-контекста

Перед инициализацией Phaser необходимо создать HTMLCanvasElement и получить от него графический контекст. Ключевой момент — объект конфигурации контекста, который передаётся вторым аргументом в getContext().

const contextCreationConfig = {
    alpha: false,
    depth: false,
    antialias: true,
    premultipliedAlpha: true,
    stencil: true,
    preserveDrawingBuffer: false,
    failIfMajorPerformanceCaveat: false,
    powerPreference: "default",
};

Разберём важные параметры: * alpha: false — отключает альфа-канал в заднем буфере, что может незначительно улучшить производительность. * antialias: true — включает сглаживание (если поддерживается железом и драйвером). * preserveDrawingBuffer: false — буфер очищается после композиции браузером. Установка в true требуется, если вы планируете читать пиксели из canvas (например, через toDataURL). * powerPreference: "default" — даёт браузеру решить, использовать ли энергоэффективный ("low-power") или высокопроизводительный ("high-performance") GPU. Можно явно указать "high-performance".

Теперь создадим сам canvas и контекст:

const myCustomCanvas = document.createElement("canvas");
const myCustomContext = myCustomCanvas.getContext(
    "webgl",
    contextCreationConfig
);

После получения контекста можно настроить сам DOM-элемент и добавить его в документ.

myCustomCanvas.id = "myCustomCanvas";
myCustomCanvas.style = "border: 8px solid green";
document.body.appendChild(myCustomCanvas);

Передача canvas и контекста в Phaser

Следующий шаг — сообщить Phaser, чтобы он использовал наши созданные объекты, а не создавал свои. Для этого в объекте конфигурации игры существуют два специальных поля: canvas и context.

const config = {
    type: Phaser.WEBGL, // Важно указать тип WebGL
    parent: "phaser-example",
    width: 800,
    height: 600,
    canvas: myCustomCanvas,   // Передаём наш элемент
    context: myCustomContext, // Передаём наш контекст
    scene: Example,
};

**Критически важно:** Если вы передаёте canvas, то почти всегда нужно передать и соответствующий ему context. Phaser попытается использовать переданный контекст для всего рендеринга. Также убедитесь, что тип рендерера (type) соответствует созданному контексту (в нашем случае Phaser.WEBGL).

После этого создание экземпляра игры происходит стандартным образом:

const game = new Phaser.Game(config);

Phaser возьмёт под контроль переданный canvas, настроит его внутренние размеры (в пикселях) согласно width и height, и начнёт рендерить в предоставленный контекст. Сцена Example загрузит и отобразит изображение именно на нашем зелёном canvas.

Важные нюансы и порядок операций

1. **DOM-дерево:** Canvas можно добавить в документ как до, так и после создания Phaser.Game. Phaser не управляет позиционированием элемента в DOM, если вы передали его явно. 2. **Размеры:** Свойства width и height в конфиге задают логическое разрешение игры. Физические размеры canvas в пикселях (атрибуты width и height элемента) будут установлены Phaser в соответствии с этими значениями. Если вам нужны нестандартные физические размеры, установите их для элемента myCustomCanvas перед передачей в конфиг. 3. **Контекст Canvas 2D:** Аналогичным образом можно передать и 2D-контекст, указав type: Phaser.CANVAS. 4. **Один контекст:** Не пытайтесь использовать один и тот же графический контекст для Phaser и другой библиотеки одновременно. 5. **Ошибки контекста:** Если создать контекст с параметрами, которые не поддерживаются системой пользователя (например, antialias: true на некоторых мобильных GPU), getContext() вернёт null. Phaser в таком случае выбросит ошибку. Всегда проверяйте результат getContext().

const myCustomContext = myCustomCanvas.getContext("webgl", config);
if (!myCustomContext) {
    console.error("WebGL context creation failed. Fallback to default or 2D.");
    // Здесь можно попробовать создать контекст с другими параметрами
    // или переключиться на Phaser.CANVAS
}

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

Использование собственного canvas и контекста WebGL открывает для разработчика Phaser-игр новые возможности по интеграции и тонкой настройке графического вывода. Этот подход необходим для сложных гибридных приложений. Для экспериментов попробуйте: * Создать canvas внутри конкретного DOM-контейнера с определёнными CSS-стилями. * Передать контекст с preserveDrawingBuffer: true и затем использовать canvas.toDataURL() для создания скриншотов прямо из игрового цикла. * Поиграть с параметром powerPreference, чтобы увидеть (в инструментах разработчика браузера), на каком GPU запускается игра. * Создать несколько независимых canvas с разными контекстами и запустить на них отдельные экземпляры Phaser.Game.