О чем этот пример

Render Texture (текстура рендеринга) в Phaser — это мощный холст в памяти, на котором можно рисовать, копировать и, что особенно интересно, стирать другие игровые объекты. Эта техника открывает двери для создания динамических поверхностей, интерактивных фонов или мини-игр по рисованию, где игрок может "соскабливать" слой, открывая изображение под ним. В этой статье мы разберем пример, где текстура травы рисуется на Render Texture, а затем стирается курсором мыши, обнажая фоновую картинку. Вы научитесь создавать такие интерактивные элементы и понимать, как управлять пикселями в реальном времени.

Версия 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('brush', 'assets/particles/sparkle1.png');
        this.load.image('tiles', 'assets/textures/grass.png');
        this.load.image('bg', 'assets/pics/turkey-1985086.jpg');
    }

    create ()
    {
        this.add.image(0, 0, 'bg').setOrigin(0);

        const rt = this.add.renderTexture(400, 300, 800, 600);

        for (let y = 0; y < 2; y++)
        {
            for (let x = 0; x < 2; x++)
            {
                rt.draw('tiles', x * 512 + 256, y * 512 + 256);
            }
        }

        rt.render();

        const brush = this.make.image({ key: 'brush' }, false).setScale(1);

        this.input.on('pointermove', pointer =>
        {

            if (pointer.isDown)
            {
                rt.erase(brush, pointer.x - 16, pointer.y - 16).render();
            }

        }, this);

        this.input.on('pointerdown', pointer =>
        {

            rt.erase(brush, pointer.x - 16, pointer.y - 16).render();

        }, this);
    }
}

const config = {
    type: Phaser.CANVAS,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#2d2d88',
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и создание Render Texture

В методе preload загружаются три изображения: кисть (brush) для стирания, тайловая текстура (tiles) для верхнего слоя и фоновая картинка (bg).

В create первым делом фоновая картинка добавляется на сцену с координатами (0,0) и началом в левом верхнем углу.

Затем создается объект RenderTexture. Это ключевой элемент. Он представляет собой невидимый холст заданного размера (800x600), расположенный в точке (400,300). На этом холсте мы и будем рисовать.

const rt = this.add.renderTexture(400, 300, 800, 600);

Заполнение Render Texture тайлами

Чтобы Render Texture стала видимой и заполнила пространство, на нее нужно что-то нарисовать. В примере используется двойной цикл for, который рисует текстуру травы (tiles) четыре раза, создавая сетку 2x2. Метод draw добавляет изображение в указанные координаты на Render Texture. Координаты 256 и 512 подобраны так, чтобы тайлы выровнялись по центру и образовали бесшовное покрытие.

После рисования необходимо вызвать метод render(), чтобы все отложенные операции рисования применились и изменения стали видны на экране.

for (let y = 0; y < 2; y++)
{
    for (let x = 0; x < 2; x++)
    {
        rt.draw('tiles', x * 512 + 256, y * 512 + 256);
    }
}
rt.render();

Создание кисти для стирания

Для операции стирания нужна кисть — изображение, которое будет определять форму и область стирания. В примере кисть создается через this.make.image. Важный момент: второй аргумент false указывает, что этот объект не должен автоматически добавляться на дисплейный список сцены. Он существует только в памяти как шаблон для операции erase. Масштаб кисти установлен в 1.

const brush = this.make.image({ key: 'brush' }, false).setScale(1);

Интерактивное стирание по движению мыши

Вся магия происходит в обработчиках событий ввода. Phaser предоставляет два события: pointerdown (кнопка мыши нажата) и pointermove (курсор перемещается).

Внутри этих обработчиков проверяется, нажата ли кнопка мыши (pointer.isDown). Если да, то вызывается ключевой метод rt.erase(). Этот метод принимает кисть (brush) и координаты (X, Y), куда ее нужно применить. В примере координаты корректируются на половину размера кисти (-16), чтобы центр кисти совпадал с позицией курсора.

Метод erase не применяет изменения моментально. Как и draw, он требует последующего вызова render(), чтобы обновить визуальное представление Render Texture. После стирания часть текстуры травы становится прозрачной, и сквозь нее проступает фоновая картинка, создавая эффект "соскабливания".

this.input.on('pointermove', pointer =>
{
    if (pointer.isDown)
    {
        rt.erase(brush, pointer.x - 16, pointer.y - 16).render();
    }
}, this);

this.input.on('pointerdown', pointer =>
{
    rt.erase(brush, pointer.x - 16, pointer.y - 16).render();
}, this);

Что попробовать дальше

Render Texture с методами draw и erase — это гибкий инструмент для работы с пикселями в реальном времени. Вы можете использовать эту технику не только для рисования, но и для создания динамических масок, интерактивных элементов интерфейса или нестандартных переходов между сценами. Попробуйте поэкспериментировать: измените изображение кисти на что-то большее или с градиентом прозрачности, чтобы стирание было не резким, а плавным. Используйте rt.fill для заливки цветом или rt.clear для полной очистки холста. Комбинируя эти методы, можно создавать по-настоящему уникальные игровые механики.