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

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

Версия 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/sprites/brush1.png');
    }

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

        const brush = this.textures.getFrame('brush');
        const hsv = Phaser.Display.Color.HSVColorWheel();
        let i = 0;

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

            if (pointer.isDown)
            {
                rt.draw(brush, pointer.x - 32, pointer.y - 32, 1, hsv[i].color).render();

                i++;

                if (i === 360)
                {
                    i = 0;
                }
            }

        }, this);
    }
}

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

const game = new Phaser.Game(config);

Создание динамического холста: Render Texture

Первым делом в методе create() создается объект Render Texture. Это специальный игровой объект, который ведет себя как текстура, но ее содержимое можно изменять программно.

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

Здесь rt — это наша текстура для рисования. Аргументы метода: координаты X (400) и Y (300) центра текстуры на сцене, а также ее ширина (800) и высота (600) в пикселях. По сути, мы создали прозрачный холст размером во весь экран (так как конфигурация игры также 800x600) и разместили его по центру.

Подготовка кисти и цветовой палитры

Чтобы рисовать, нужны кисть и краски. Кистью будет спрайт, загруженный в preload(). Мы получаем его кадр (Frame) из менеджера текстур.

const brush = this.textures.getFrame('brush');

Для красок используется встроенный генератор цветового круга HSV (Оттенок, Насыщенность, Яркость). Этот метод возвращает массив из 360 объектов, каждый из которых содержит цвет в числовом формате и другие представления, что соответствует полному кругу оттенков.

const hsv = Phaser.Display.Color.HSVColorWheel();
let i = 0;

Переменная `i` будет служить индексом для выбора текущего цвета из этого массива.

Обработка ввода и рисование на текстуре

Логика рисования привязана к событию перемещения указателя (мыши или касания). Код внутри обработчика срабатывает каждый раз, когда курсор двигается.

this.input.on('pointermove', pointer => {
    if (pointer.isDown) {
        rt.draw(brush, pointer.x - 32, pointer.y - 32, 1, hsv[i].color).render();
        i++;
        if (i === 360) {
            i = 0;
        }
    }
}, this);

Ключевой момент — проверка pointer.isDown. Рисование происходит только если кнопка мыши зажата или палец прижат к экрану.

Метод rt.draw() наносит отпечаток кисти (brush) на нашу Render Texture. Аргументы: 1. brush: Источник для рисования (кадр текстуры). 2. pointer.x - 32, pointer.y - 32: Позиция для отрисовки. Вычитание 32 пикселя (половина от размера кисти 64x64) центрирует кисть относительно курсора. 3. `1`: Масштаб отрисовки (1 = оригинальный размер). 4. hsv[i].color: Цветовой тон (tint), который применяется к кисти.

Сразу после draw() вызывается .render(), чтобы изменения сразу отобразились на экране.

После каждого отпечатка индекс `i` увеличивается, и цвет плавно меняется по кругу. Когда индекс достигает 360, он сбрасывается на 0.

Почему именно Render Texture, а не множество спрайтов?

Можно было бы создать новый спрайт кисти при каждом движении мыши. Однако это быстро привело бы к созданию сотен или тысяч игровых объектов, что негативно скажется на производительности.

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

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

Render Texture — это ваш динамический холст внутри игры Phaser. В рассмотренном примере мы реализовали интерактивную кисть с меняющимся цветом. Этот механизм лежит в основе множества других техник. Для экспериментов попробуйте: 1. Изменить кисть на другую текстуру (например, частицу или геометрическую фигуру). 2. Реализовать "ластик", используя метод rt.erase(). 3. Сохранять получившееся изображение с помощью rt.snapshot(). 4. Использовать rt.drawFrame() для рисования кадров из атласа анимации, создавая анимированные следы.