О чем этот пример
При разработке игр иногда возникает необходимость работать с графическими объектами, размеры которых превышают игровое окно или даже сам canvas. Например, для генерации больших изображений, текстур для масштабных карт или создания ассетов для экспорта. Стандартный подход с отображением в сцене здесь не подходит. В этой статье мы разберем, как использовать `DynamicTexture` для создания, заполнения и сохранения текстур любого размера, не ограничиваясь видимой областью рендерера.
Версия 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.image('uv', 'assets/pics/uv-grid-4096-ian-maclachlan.png');
}
create ()
{
/*
this.add.image(400, 300, 'uv');
this.renderer.snapshot(image => {
document.body.appendChild(image);
});
*/
const texture = this.textures.addDynamicTexture('test', 4096, 4096);
texture.stamp('uv', null, 2048, 2048);
texture.snapshot(image => {
document.body.appendChild(image);
});
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d6d',
scene: Example
};
const game = new Phaser.Game(config);
Проблема стандартного подхода
Обычно для работы с изображениями мы создаем игровой объект Image и добавляем его на сцену через this.add.image(). Phaser рендерит его в контекст Canvas, и мы можем сделать снимок этого Canvas с помощью метода this.renderer.snapshot(). Однако этот метод имеет ключевое ограничение: он захватывает только ту область Canvas, которая соответствует размеру игры, заданному в конфигурации (в нашем примере 800x600).
Любой контент, выходящий за пределы этих границ, обрезается или не рендерится вовсе. Таким образом, сохранить изображение, большее, чем игровое окно, стандартным способом невозможно. В закомментированном коде примера как раз показана эта попытка, которая не даст нужного результата для текстуры размером 4096x4096.
Динамические текстуры (DynamicTexture)
Решение — использовать объект DynamicTexture. Это специальный тип текстуры в Phaser, который существует независимо от основного холста игры. Его можно представить как виртуальный холст в памяти, на котором можно рисовать, не заботясь о размерах игрового окна.
В примере текстура создается в методе create() сцены. Давайте разберем процесс создания:
const texture = this.textures.addDynamicTexture('test', 4096, 4096);
* this.textures — это менеджер текстур сцены.
* .addDynamicTexture(key, width, height) — метод для создания динамической текстуры. Он принимает:
* key ('test') — уникальный строковый идентификатор для последующего доступа к текстуре.
* width и height (4096, 4096) — размеры создаваемой текстуры в пикселях. Эти значения могут быть любыми, в том числе значительно превышающими размеры game.config.
Работа с содержимым текстуры: метод stamp
После создания текстура пуста. Чтобы заполнить ее изображением, используется метод .stamp(). В примере на текстуру «ставится штамп» с другим графическим ресурсом.
texture.stamp('uv', null, 2048, 2048);
* 'uv' — ключ изображения, загруженного в preload(). Это исходник для штампа.
* null — в этом параметре можно передать индекс кадра или конфиг, но в нашем случае он не требуется.
* 2048, 2048 — координаты `xиy` *внутри динамической текстуры*, куда будет помещен центр исходного изображения. Это позволяет точно позиционировать контент на виртуальном холсте большого размера.
Сохранение результата: snapshot текстуры
Самое важное — возможность сохранить результат работы с DynamicTexture в виде обычного изображения (HTMLImageElement). Для этого у динамической текстуры есть собственный метод .snapshot().
texture.snapshot(image => {
document.body.appendChild(image);
});
* texture.snapshot(callback) — метод создает снимок *всей* динамической текстуры, независимо от ее размера (в нашем случае 4096x4096).
* callback — функция, которая получает созданный объект HTMLImageElement в качестве аргумента. В примере это изображение просто добавляется в тело HTML-документа (document.body.appendChild(image)), и его можно увидеть в браузере. На практике вы можете сохранить его на диск, отправить на сервер или использовать для дальнейшей обработки.
Что попробовать дальше
Использование DynamicTexture — мощный метод для оффскринной графической работы в Phaser. Он снимает ограничения, накладываемые размерами игрового окна, и открывает возможности для генерации контента, создания редакторов карт или подготовки ассетов.
**Идеи для экспериментов:**
1. Создайте текстуру и используйте методы texture.fill() или texture.draw() для рисования примитивов и текста.
2. Скомбинируйте несколько изображений, накладывая их друг на друга с разными координатами с помощью stamp().
3. Сгенерируйте текстуру для тайловой карты огромного размера и сохраните ее как файл для использования вне игры.
