О чем этот пример
Создание игровых ассетов на лету — мощный приём, который открывает двери к процедурной генерации и динамическим эффектам. В Phaser 3 для этого есть удобный метод `generateTexture()`. В этой статье мы разберём, как из объекта `Graphics` (который обычно используется для временного рисования) создать полноценную текстуру, которую можно присвоить спрайту или изображению. Это позволит вам генерировать уникальные формы, иконки или фоны прямо во время выполнения игры, без загрузки внешних файлов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
starGraphics;
create() {
this.starGraphics = this.make.graphics({x: 0, y: 0, add: false});
this.drawStar(this.starGraphics, 105, 105, 5, 100, 50, 0xFFFF00, 0xFF0000);
this.starGraphics.generateTexture('starGraphics', 210, 210);
const image = this.add.image(400, 300, 'starGraphics');
}
drawStar (graphics, cx, cy, spikes, outerRadius, innerRadius, color, lineColor) {
var rot = Math.PI / 2 * 3;
var x = cx;
var y = cy;
var step = Math.PI / spikes;
graphics.lineStyle(10, lineColor, 1.0);
graphics.fillStyle(color, 1.0);
graphics.beginPath();
graphics.moveTo(cx, cy - outerRadius);
for (let i = 0; i < spikes; i++) {
x = cx + Math.cos(rot) * outerRadius;
y = cy + Math.sin(rot) * outerRadius;
graphics.lineTo(x, y);
rot += step;
x = cx + Math.cos(rot) * innerRadius;
y = cy + Math.sin(rot) * innerRadius;
graphics.lineTo(x, y);
rot += step;
}
graphics.lineTo(cx, cy - outerRadius);
graphics.closePath();
graphics.fillPath();
graphics.strokePath();
}
}
const config = {
type: Phaser.CANVAS,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
От временного рисунка к постоянной текстуре
Объект Graphics в Phaser — это, по сути, холст для векторного рисования. Его содержимое обычно отрисовывается прямо на игровом слое и не предназначено для повторного использования как самостоятельный ассет. Но что, если нарисованную звезду, взрыв или сложный узор нужно использовать много раз?
Метод generateTexture() решает эту проблему. Он захватывает текущее состояние объекта Graphics и создаёт из него текстуру — полноценное изображение, которое затем можно использовать как любой другой загруженный ассет через его уникальный строковый ключ.
В нашем примере мы сначала создаём объект Graphics с флагом add: false. Это важно: объект не будет автоматически добавлен на сцену, а будет существовать лишь как инструмент для рисования в памяти.
Пошаговый разбор кода: Создание и рисование
Давайте подробно рассмотрим, что происходит в методе create() сцены.
Сначала мы создаём изолированный объект Graphics с помощью фабрики this.make.graphics(). Ключевой параметр add: false говорит системе не добавлять этот объект в список отображения сцены.
this.starGraphics = this.make.graphics({x: 0, y: 0, add: false});
Затем мы вызываем вспомогательный метод drawStar(), передавая ему созданный графический объект, параметры звезды (координаты центра, количество лучей, радиусы) и цвета. Этот метод использует API Graphics для отрисовки контура и заливки.
this.drawStar(this.starGraphics, 105, 105, 5, 100, 50, 0xFFFF00, 0xFF0000);
Теперь в объекте this.starGraphics хранится векторное описание жёлтой звезды с красной обводкой.
Волшебство метода `generateTexture()`
Это самый важный шаг. Мы берём нарисованную векторную графику и «печатаем» её в растровую текстуру.
this.starGraphics.generateTexture('starGraphics', 210, 210);
Метод принимает три аргумента:
1. 'starGraphics' — уникальный строковый ключ, по которому эта текстура будет доступна в кэше текстур игры.
2. 210 — ширина генерируемой текстуры в пикселях.
3. 210 — высота генерируемой текстуры в пикселях.
После выполнения этой строки в менеджере текстур Phaser (this.textures) появляется новая текстура с ключом 'starGraphics'. Исходный объект Graphics (this.starGraphics) можно даже удалить — текстура уже сохранена независимо от него.
Использование сгенерированной текстуры
Как только текстура создана и зарегистрирована под своим ключом, с ней можно работать как с любой другой предзагруженной картинкой. Мы создаём обычный игровой объект Image и указываем ключ нашей новой текстуры.
const image = this.add.image(400, 300, 'starGraphics');
Теперь image — это полноценный спрайт, использующий динамически сгенерированную текстуру. Его можно анимировать, трансформировать, применять к нему физику — делать всё то же самое, что и со спрайтом из файла. Главное преимущество в том, что исходная форма была создана алгоритмически прямо в коде игры.
Алгоритм рисования звезды
Для полноты картины разберём вспомогательную функцию drawStar. Она не является частью API Phaser, а демонстрирует, как с помощью методов Graphics можно создавать сложные формы.
Функция использует тригонометрию для расчёта вершин звезды, чередуя внешний и внутренний радиус. Основные используемые методы Graphics:
- graphics.lineStyle() — задаёт стиль линии (толщина, цвет, альфа).
- graphics.fillStyle() — задаёт цвет и альфа-канал заливки.
- graphics.beginPath(), graphics.moveTo(), graphics.lineTo(), graphics.closePath() — стандартные команды векторного контура.
- graphics.fillPath() и graphics.strokePath() — применяют заливку и обводку к нарисованному контуру.
graphics.lineStyle(10, lineColor, 1.0);
graphics.fillStyle(color, 1.0);
graphics.beginPath();
// ... расчёт точек и вызовы lineTo ...
graphics.closePath();
graphics.fillPath();
graphics.strokePath();
Именно этот нарисованный контур и будет позже захвачен в текстуру.
Что попробовать дальше
Метод generateTexture() — это мост между гибкостью векторного рисования и производительностью использования готовых растровых текстур. С его помощью можно создавать интерфейсные элементы, уникальные power-up'ы, процедурные ландшафты или эффекты частиц, которые зависят от состояния игры.
**Идеи для экспериментов:**
1. Генерируйте текстуры для снарядов разного калибра или элементов брони прямо в зависимости от характеристик предмета.
2. Создайте систему простых иконок для диалогов или карты, рисуя их форму на основе данных (например, цвет фракции).
3. Скомбинируйте несколько простых Graphics-объектов в одну сложную текстуру для тайловой карты.
4. Используйте generateTexture() для создания «снимка» части игрового мира и применения к нему эффектов (размытия, затемнения).
