О чем этот пример
Создание скриншотов (снимков экрана) прямо во время работы игры — мощный инструмент для отладки, создания систем фоторежима или просто для веселья. В этом примере мы разберем, как использовать встроенный метод `snapshot` рендерера Phaser, а также реализуем простую систему рисования на холсте игры с помощью мыши. Вы научитесь захватывать текущий кадр в изображение и добавлять на него интерактивные графические элементы.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
isKeyDown = false;
isMouseDown = false;
graphicsPath = [];
graphics;
snapHistory = [];
time = 0;
div = document.createElement('div');
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('myImage', 'assets/sprites/phaser1.png');
this.load.glsl('shader0', 'assets/shaders/shader0.frag');
}
create ()
{
this.div.innerHTML = 'PRESS SPACE TO TAKE SNAPSHOT<br>';
document.body.appendChild(this.div);
for (let i = 0; i < 5; ++i)
{
const image = this.add.image(Math.random() * 800, Math.random() * 600, 'myImage');
}
this.graphics = this.add.graphics({x: 0, y: 0});
game.canvas.onmousedown = e =>
{
this.isMouseDown = true;
this.graphics.clear();
this.graphicsPath.length = 0;
};
game.canvas.onmouseup = e =>
{
this.isMouseDown = false;
};
game.canvas.onmousemove = e =>
{
const mouseX = e.clientX - game.canvas.offsetLeft;
const mouseY = e.clientY - game.canvas.offsetTop;
if (this.isMouseDown)
{ this.graphicsPath.push({x: mouseX, y: mouseY}); }
};
window.onkeydown = e =>
{
if (e.keyCode === 32 && !this.isKeyDown)
{
game.renderer.snapshot(image =>
{
image.style.width = '160px';
image.style.height = '120px';
image.style.paddingLeft = '2px';
this.snapHistory.push(image);
console.log('snap!');
document.body.appendChild(image);
});
this.isKeyDown = true;
}
};
window.onkeyup = e =>
{
if (e.keyCode === 32)
{
this.isKeyDown = false;
}
};
}
update ()
{
const length = this.graphicsPath.length;
this.graphics.clear();
this.graphics.lineStyle(10.0, 0xFFFF00, 1.0);
this.graphics.beginPath();
for (let i = 0; i < length; ++i)
{
const node = this.graphicsPath[i];
if (i !== 0)
{
this.graphics.lineTo(node.x, node.y);
}
else
{
this.graphics.moveTo(node.x, node.y);
}
}
this.graphics.strokePath();
this.graphics.closePath();
this.time += 0.01;
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Инициализация сцены и загрузка ресурсов
В методе preload() загружаются необходимые ресурсы: спрайт и шейдер. Обратите внимание на использование this.load.setBaseURL() для указания базового пути, что удобно для загрузки удаленных ассетов из репозитория с примерами.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('myImage', 'assets/sprites/phaser1.png');
this.load.glsl('shader0', 'assets/shaders/shader0.frag');
}
В create() происходит основная настройка: создается текстовый блок для инструкций, добавляются несколько спрайтов в случайных позициях и инициализируется объект Graphics для рисования. Объект this.graphics будет использоваться для отображения линии, которую пользователь рисует мышью. Также здесь навешиваются обработчики событий мыши и клавиатуры непосредственно на глобальные объекты game.canvas и window.
Обработка ввода: рисуем мышью
Для реализации рисования используются стандартные DOM-события мыши, привязанные к холсту игры. Это демонстрирует, как можно комбинировать Phaser с нативным API браузера.
game.canvas.onmousedown = e =>
{
this.isMouseDown = true;
this.graphics.clear();
this.graphicsPath.length = 0;
};
При нажатии кнопки мыши (onmousedown) флаг isMouseDown устанавливается в true, а текущий графический путь очищается. Массив graphicsPath служит для хранения точек линии.
game.canvas.onmousemove = e =>
{
const mouseX = e.clientX - game.canvas.offsetLeft;
const mouseY = e.clientY - game.canvas.offsetTop;
if (this.isMouseDown)
{ this.graphicsPath.push({x: mouseX, y: mouseY}); }
};
При движении мыши с зажатой кнопкой координаты (mouseX, mouseY) вычисляются относительно холста и добавляются в массив graphicsPath. Это и есть наш «след» от мыши.
Создание скриншота с помощью `game.renderer.snapshot`
Ключевая функциональность — создание снимка текущего состояния рендерера. Это делается при нажатии клавиши Пробел (код 32).
window.onkeydown = e =>
{
if (e.keyCode === 32 && !this.isKeyDown)
{
game.renderer.snapshot(image =>
{
image.style.width = '160px';
image.style.height = '120px';
image.style.paddingLeft = '2px';
this.snapHistory.push(image);
console.log('snap!');
document.body.appendChild(image);
});
this.isKeyDown = true;
}
};
Метод game.renderer.snapshot() принимает коллбэк, в который передается готовый HTMLImageElement. В этом коллбэке мы настраиваем стили изображения (уменьшаем размер), добавляем его в массив истории snapHistory и вставляем прямо в тело документа. Обратите внимание на флаг isKeyDown, который предотвращает множественные срабатывания при зажатой клавише.
Отображение графического пути в `update()`
Чтобы нарисованная мышью линия отображалась в кадре игры (и, соответственно, попадала на скриншоты), ее необходимо перерисовывать каждый кадр в методе update(). Используется API объекта Graphics.
this.graphics.clear();
this.graphics.lineStyle(10.0, 0xFFFF00, 1.0);
this.graphics.beginPath();
for (let i = 0; i < length; ++i)
{
const node = this.graphicsPath[i];
if (i !== 0)
{
this.graphics.lineTo(node.x, node.y);
}
else
{
this.graphics.moveTo(node.x, node.y);
}
}
this.graphics.strokePath();
this.graphics.closePath();
Каждый кадр графический объект очищается, устанавливается стиль линии (толщина 10, желтый цвет), и затем по точкам из graphicsPath строится ломаная линия. moveTo задает начальную точку, а lineTo — последующие. strokePath() выполняет отрисовку. Это классический подход для рисования динамических линий в Phaser.
Что попробовать дальше
Вы изучили практический пример создания интерактивного скриншотера с рисованием в Phaser. Ключевые элементы: метод snapshot рендерера для захвата кадра, работа с DOM-событиями для ввода и объект Graphics для динамической векторной графики. Для экспериментов попробуйте: сохранять скриншоты не в DOM, а на сервер; рисовать не линию, а фигуры; применять к скриншотам фильтры из загруженного шейдера shader0; или интегрировать систему скриншотов в игровое меню.
