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

RenderTexture в Phaser — это мощный холст, на котором можно рисовать и, что важно, стирать изображения в реальном времени. Эта техника открывает двери для создания динамических эффектов: от интерактивных ландшафтов, которые можно «выкапывать», до скрытых объектов, которые нужно «стереть», чтобы обнаружить. В этой статье мы разберем, как использовать метод `erase` объекта RenderTexture, чтобы превратить простой спрайт-кисть в инструмент для удаления частей текстуры по движению мыши.

Версия 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(0.5);

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

        });

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

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

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

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

В методе create сначала добавляется фоновое изображение. Затем создается ключевой объект — RenderTexture. Это специальный игровой объект, который действует как динамический холст.

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

Здесь создается RenderTexture шириной 800 пикселей и высотой 600 пикселей, центрированная в точке (400, 300). Далее на этот холст в виде сетки 2x2 рисуется текстура травы с помощью метода draw.

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

Важно вызвать метод render() после завершения отрисовки, чтобы изменения визуализировались.

rt.render();

Теперь у нас есть сплошной слой травы, наложенный поверх фона.

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

Для стирания нам нужна «кисть» — изображение, которое будет определять форму и область удаления. В примере кисть создается не как обычный спрайт, а через this.make.image. Это фабричный метод, который создает объект Image, но не добавляет его автоматически на дисплей. Это оптимально, так как сам спрайт нам видеть не нужно, мы будем использовать только его текстуру.

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

Параметр false указывает, что объект не должен быть добавлен в список отображения сцены. Масштаб уменьшен до 0.5 для удобства.

Основная магия происходит в обработчиках событий ввода. Логика одинакова для pointermove (движение с зажатой кнопкой) и pointerdown (клик).

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

Метод erase принимает два ключевых аргумента: 1. **Источник (brush)**: Игровой объект (Image, Sprite), текстура которого используется как маска для стирания. Пиксели кисти определяют, какие области RenderTexture будут удалены. 2. **Координаты (x, y)**: Позиция, в которую помещается кисть для стирания. В примере от координат указателя вычитается 16 пикселей для грубой центровки кисти.

Вызов render() после erase обязателен для применения изменений к текстуре и их отображения на экране.

Как работает метод `erase`

Важно понимать, что erase не делает пиксели прозрачными в классическом смысле. Вместо этого он вычитает текстуру кисти из текущего содержимого RenderTexture. Это операция на уровне буфера кадра.

* **Кисть как альфа-маска:** Область, где текстура кисти непрозрачна (имеет альфа-канал > 0), будет «вырезана» из RenderTexture, открывая слой под ним (в нашем случае — фоновое изображение). * **Накопительный эффект:** Если проводить кистью несколько раз по одной области, эффект стирания будет усиливаться, так как операция вычитания применяется повторно. * **Производительность:** Операции с RenderTexture выполняются на GPU и обычно очень эффективны. Однако частые вызовы render() (например, каждый кадр при движении мыши) требуют перерисовки текстуры, что может сказаться на производительности на слабых устройствах.

Именно эта комбинация — динамический холст (RenderTexture) и операция вычитания (erase) — позволяет создавать впечатляющие интерактивные эффекты.

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

Метод erase объекта RenderTexture — это элегантный и производительный способ добавить интерактивное разрушение или обнаружение в вашу игру на Phaser. Он превращает статичную графику в изменяемую поверхность. **Идеи для экспериментов:** 1. Создайте игру-раскраску, где нужно стирать верхний слой, чтобы открыть рисунок под ним. 2. Реализуйте мини-игру, где игрок «выкапывает» артефакты из песка, используя кисть разного размера. 3. Сделайте интерактивную замороженную поверхность, которую нужно растопить (стереть), чтобы увидеть, что под ней. 4. Используйте в качестве кисти анимированный спрайт (например, огонь или воду) для более живого эффекта стирания.