О чем этот пример
Render Texture в Phaser — это мощный инструмент, который позволяет отрисовывать множество игровых объектов в один буфер, а затем работать с ним как с единым изображением. Это открывает возможности для создания сложных динамических эффектов, таких как вращающиеся камеры, зеркала, порталы или постобработка. В статье мы разберем практический пример, где различные объекты — спрайты, частицы, текст и графика — рисуются на одну текстуру, которая анимируется в реальном времени.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
iter = 0;
blitter;
tilesprite;
quad;
graphics;
bitmaptext;
particles;
bunny;
text;
bob;
rt;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bunny', 'assets/sprites/bunny.png');
this.load.image('pic', 'assets/pics/baal-loader.png');
this.load.atlas('flares', 'assets/particles/flares.png', 'assets/particles/flares.json');
this.load.bitmapFont('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');
this.load.image('image', 'assets/pics/sure-shot-by-made.png');
this.load.image('mushroom', 'assets/sprites/mushroom2.png');
this.load.image('atari', 'assets/sprites/atari130xe.png');
}
create ()
{
this.bunny = this.textures.getFrame('bunny');
this.text = this.add.text(0, 0, 'phaser 3?').setVisible(false);
this.bob = this.add.image(0, 0, 'bunny').setName('bob').setVisible(false);
this.particles = this.add.particles(200, 300, 'flares', {
frame: 'blue',
lifespan: 2000,
speed: { min: 400, max: 600 },
angle: 330,
gravityY: 300,
scale: { start: 0.4, end: 0 },
quantity: 2,
blendMode: 'ADD'
}).setVisible(false);
this.bitmaptext = this.add.bitmapText(0, 0, 'desyrel', 'PHASER 3\nRender Texture').setVisible(false);
this.graphics = this.add.graphics().setVisible(false);
this.graphics.fillStyle(0xffff00, 1);
this.graphics.slice(400, 300, 200, Phaser.Math.DegToRad(340), Phaser.Math.DegToRad(20), true);
this.graphics.fillPath();
this.tilesprite = this.add.tileSprite(400, 300, 250, 250, 'mushroom').setVisible(false);
this.blitter = this.add.blitter(0, 0, 'atari').setVisible(false);
this.blitter.create(0, 0);
this.rt = this.add.renderTexture(400, 300, 800, 600);
}
update ()
{
this.rt.camera.rotation -= 0.01;
this.rt.clear();
this.rt
.draw(this.graphics, 0, 0)
.draw(this.bob, 200, 200)
.draw(this.tilesprite, 200, 200)
.draw(this.blitter, 0, 0)
.draw(this.text, 100, 100)
.draw(this.bob, 300, 300)
.draw(this.bob, 400, 400)
.draw(this.text, 300, 200)
.draw(this.particles, 300, 0)
.draw(this.bitmaptext, 200, 100);
this.rt.render();
this.tilesprite.tilePositionX = Math.cos(-this.iter) * 400;
this.tilesprite.tilePositionY = Math.sin(-this.iter) * 400;
this.iter += 0.01;
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Что такое Render Texture и зачем она нужна
RenderTexture — это специальный тип игрового объекта (Game Object), который представляет собой динамически создаваемый буфер для отрисовки. В него можно "записывать" другие игровые объекты с помощью метода .draw(). После отрисовки RenderTexture ведет себя как обычное изображение: ее можно перемещать, вращать, масштабировать.
Основные преимущества:
* **Производительность:** Отрисовав сложную композицию один раз в текстуру, вы можете рендерить ее как единый спрайт, вместо того чтобы перерисовывать каждый объект отдельно в каждом кадре.
* **Гибкость:** Вы можете применять трансформации (вращение, масштаб) ко всей собранной сцене через камеру самой RenderTexture.
* **Создание эффектов:** Идеально подходит для реализации зеркал, мини-карт, снимков экрана или нестандартных систем частиц.
В примере мы создаем текстуру размером 800x600 пикселей в центре экрана.
Подготовка объектов для отрисовки
Перед использованием RenderTexture необходимо создать и настроить все объекты, которые мы хотим в нее нарисовать. Важный нюанс: сами эти объекты не должны отображаться на основной сцене. Для этого у каждого из них вызывается .setVisible(false).
В методе `create()` инициализируются:
* `this.bob`: Спрайт (`Image`) с зайцем.
* `this.text`: Текстовый объект (`Text`).
* `this.particles`: Система частиц (`ParticleEmitter`).
* `this.bitmaptext`: Растровый шрифт (`BitmapText`).
* `this.graphics`: Графический объект (`Graphics`) с нарисованной "долькой".
* `this.tilesprite`: `TileSprite` (плиточный спрайт) с изображением гриба.
* `this.blitter`: `Blitter` — низкоуровневый объект для быстрой отрисовки спрайтов.
this.bob = this.add.image(0, 0, 'bunny').setName('bob').setVisible(false);
this.text = this.add.text(0, 0, 'phaser 3?').setVisible(false);
this.particles = this.add.particles(200, 300, 'flares', {
frame: 'blue',
lifespan: 2000,
// ... другие настройки
}).setVisible(false);
После этого создается сама RenderTexture.
this.rt = this.add.renderTexture(400, 300, 800, 600);
Цикл отрисовки и анимация
Вся магия происходит в методе update(), который выполняется каждый кадр.
1. **Вращение камеры текстуры:** Мы вращаем не саму текстуру, а ее внутреннюю камеру. Это заставляет всю нарисованную сцену вращаться.
this.rt.camera.rotation -= 0.01;
2. **Очистка буфера:** Перед новой отрисовкой старый кадр нужно стереть.
this.rt.clear();
3. **Рисование объектов:** Метод .draw() принимает объект и координаты (относительно центра RenderTexture). Объекты можно рисовать несколько раз в разных местах.
this.rt
.draw(this.graphics, 0, 0)
.draw(this.bob, 200, 200)
.draw(this.bob, 300, 300) // Зайца рисуем повторно
.draw(this.text, 100, 100);
4. **Запрос на отрисовку:** После того как все объекты добавлены в очередь отрисовки, вызывается .render().
this.rt.render();
5. **Анимация TileSprite:** Отдельно анимируется смещение текстуры внутри TileSprite, чтобы создать эффект движения фона, который затем тоже рисуется в RenderTexture.
this.tilesprite.tilePositionX = Math.cos(-this.iter) * 400;
this.tilesprite.tilePositionY = Math.sin(-this.iter) * 400;
Ключевые методы API Render Texture
Работа с RenderTexture строится вокруг нескольких основных методов:
* clear(): Полностью очищает текстуру, делая ее прозрачной. Обязательно вызывайте этот метод в начале каждого кадра, если не хотите накапливать результат.
* draw(gameObject, x, y): Рисует переданный игровой объект в текстуру по указанным локальным координатам (x и y относительно центра текстуры). Объект может быть любым: Image, Sprite, Text, Graphics, TileSprite, ParticleEmitter и т.д.
* render(): Финализирует команды отрисовки за кадр. Хотя в некоторых случаях Phaser может вызвать этот метод автоматически, явный вызов является хорошей практикой.
* camera: Свойство, предоставляющее доступ к камере, "смотрящей" на содержимое текстуры. Через нее можно управлять rotation, zoom и scroll всей отрендеренной сцены.
// Типичная последовательность в update()
this.rt.clear();
this.rt.draw(someObject, 100, 50);
this.rt.draw(anotherObject, -50, -100);
this.rt.camera.zoom = 1.5; // Приближаем всю сцену в текстуре
this.rt.render();
Что попробовать дальше
Render Texture — это ваш холст для создания динамических композиций прямо во время выполнения игры. Используя этот инструмент, вы можете значительно оптимизировать рендеринг статичных сложных сцен или реализовать уникальные визуальные эффекты.
**Идеи для экспериментов:**
1. Попробуйте анимировать не rotation, а zoom камеры текстуры.
2. Создайте эффект "заморозки времени": нарисуйте в текстуру все объекты сцены в определенный момент, а затем отобразите эту текстуру как статичное изображение.
3. Используйте RenderTexture в качестве маски для другого объекта или для создания динамической мини-карты.
4. Поэкспериментируйте с порядком вызовов .draw() — последний нарисованный объект будет поверх остальных.
