О чем этот пример
В процессе разработки игры часто возникает необходимость сделать моментальный снимок текущего состояния сцены — для создания превью, отправки отчета об ошибке или реализации системы сохранения прогресса через скриншоты. Phaser 3 предоставляет простой и мощный метод `snapshot()` для захвата текущего кадра рендерера. В этой статье мы разберем, как не только сделать такой снимок и добавить его на страницу, но и преобразовать его в текстовый формат base64, который можно легко сохранить на сервере или в локальном хранилище браузера.
Версия 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('myImage', 'assets/sprites/phaser1.png');
this.load.glsl('fireball', 'assets/shaders/shader0.frag');
}
create ()
{
this.add.shader('fireball', 400, 300, 800, 600);
for (let i = 0; i < 16; ++i)
{
this.add.image(Math.random() * 800, Math.random() * 600, 'myImage');
}
this.input.once('pointerdown', () => {
this.renderer.snapshot(image => {
// For testing to see if the snapshot worked:
document.body.appendChild(image);
const snap = this.textures.createCanvas('snap', image.width, image.height);
snap.draw(0, 0, image);
const base64 = snap.canvas.toDataURL();
console.log(base64);
});
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и метод `snapshot()`
Основной инструмент для захвата кадра находится в рендерере игры. Метод this.renderer.snapshot() принимает функцию обратного вызова, единственным аргументом которой будет объект HTMLImageElement, содержащий изображение текущего состояния canvas.
Важно помнить, что для использования шейдеров и метода snapshot требуется контекст WebGL. Поэтому в конфигурации игры должен быть указан type: Phaser.WEBGL.
Следующий код загружает текстуру и шейдер, а затем по клику мыши делает снимок и временно добавляет его в тело HTML-документа для визуальной проверки.
this.input.once('pointerdown', () => {
this.renderer.snapshot(image => {
// Для проверки работоспособности снимка:
document.body.appendChild(image);
});
});
Создание текстуры из снимка
Просто получить изображение — лишь половина дела. Чтобы дальше работать с ним внутри Phaser (например, отрисовать как спрайт) или преобразовать в другие форматы, изображение нужно превратить в текстуру. Для этого используется метод this.textures.createCanvas(), который создает пустую canvas-текстуру с заданным именем и размерами.
Затем, используя метод .draw() этой текстуры, мы помещаем наше изображение в ее внутренний canvas. Теперь снимок доступен в менеджере текстур игры под ключом 'snap', и его можно использовать, например, так: this.add.image(400, 300, 'snap').
const snap = this.textures.createCanvas('snap', image.width, image.height);
snap.draw(0, 0, image);
Преобразование в формат base64
Ключевое преимущество canvas-текстуры в том, что мы получаем прямой доступ к DOM-элементу <canvas>. У каждого canvas есть стандартный метод .toDataURL(), который возвращает строку с изображением, закодированным в формате Data URL (base64). Это универсальный текстовый формат, который можно сохранить в localStorage, отправить на сервер через fetch или вставить как источник для тега <img>.
Полученная строка начинается с префикса (например, data:image/png;base64,), за которым следуют сами данные.
const base64 = snap.canvas.toDataURL();
console.log(base64); // Выводит длинную строку base64 в консоль
Полный код примера и конфигурация
Давайте соберем все шаги воедино. В этом примере после клика снимок будет добавлен на страницу, создана соответствующая текстура Phaser, а ее base64-представление выведено в консоль разработчика.
Обратите внимание на конфигурацию игры: для работы шейдера и метода snapshot необходим Phaser.WEBGL.
class Example extends Phaser.Scene {
preload() {
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('myImage', 'assets/sprites/phaser1.png');
this.load.glsl('fireball', 'assets/shaders/shader0.frag');
}
create() {
this.add.shader('fireball', 400, 300, 800, 600);
for (let i = 0; i < 16; ++i) {
this.add.image(Math.random() * 800, Math.random() * 600, 'myImage');
}
this.input.once('pointerdown', () => {
this.renderer.snapshot(image => {
document.body.appendChild(image);
const snap = this.textures.createCanvas('snap', image.width, image.height);
snap.draw(0, 0, image);
const base64 = snap.canvas.toDataURL();
console.log(base64);
});
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Метод snapshot() в связке с createCanvas() и toDataURL() открывает множество практических возможностей: от простого создания скриншотов для отладки до реализации сложных функций, таких как система фото-режима в игре или облачное сохранение прогресса через изображение. Для экспериментов попробуйте изменить формат изображения в toDataURL('image/jpeg', 0.8) для сжатия, автоматически отправлять base64 на моковый сервер или создать спрайт из текстуры 'snap' и анимировать его на самой сцене.
