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

Работа с графикой в играх часто требует динамического изменения текстур. Phaser 3 предоставляет мощный инструмент — Canvas 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('grass', 'assets/textures/grass.png');
        this.load.image('bg', 'assets/pics/turkey-1985086.jpg');
    }

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

        const texture = this.textures.createCanvas('canvastexture', 800, 600);

        const grass = this.textures.get('grass').getSourceImage();
        const brush = this.textures.get('brush').getSourceImage();

        texture.draw(0, 0, grass);
        texture.draw(512, 0, grass);
        texture.draw(0, 512, grass);
        texture.draw(512, 512, grass);

        //  Set the global composite op:
        texture.context.globalCompositeOperation = 'destination-out';

        //  Now anything drawn to the canvas will use this op
        texture.draw(0, 0, brush);
        texture.draw(150, 90, brush);
        texture.draw(300, 140, brush);

        //  Finally, display the Canvas Texture by adding it to an Image
        this.add.image(0, 0, 'canvastexture').setOrigin(0);
    }
}

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

const game = new Phaser.Game(config);

Создание Canvas Texture и загрузка ресурсов

Перед началом работы с динамическими текстурами необходимо загрузить исходные изображения. В методе preload мы используем this.load.image для загрузки трех картинок: фоновой текстуры (bg), текстуры травы (grass) и кисти (brush), которая будет выступать в роли маски или инструмента.

Ключевой момент происходит в create. Здесь мы создаем пустую текстуру на основе холста с помощью метода this.textures.createCanvas. Этот метод принимает уникальный ключ для текстуры и ее размеры.

const texture = this.textures.createCanvas('canvastexture', 800, 600);

Затем, чтобы рисовать на этом холсте другими изображениями, мы получаем их DOM-представление (объекты HTMLImageElement) с помощью метода getSourceImage().

Рисование и композитные операции

После подготовки мы можем рисовать на нашей Canvas Texture. Метод texture.draw() работает аналогично context.drawImage() в нативном Canvas API. Мы размещаем текстуру травы в четырех углах нашего холста.

texture.draw(0, 0, grass);
texture.draw(512, 0, grass);
texture.draw(0, 512, grass);
texture.draw(512, 512, grass);

Самая интересная часть — использование композитных операций. Они определяют, как новое изображение будет взаимодействовать с уже нарисованным содержимым. Мы напрямую обращаемся к контексту холста текстуры (texture.context) и меняем его свойство globalCompositeOperation. В примере используется значение 'destination-out', которое делает рисуемую область прозрачной, словно стирая ее.

texture.context.globalCompositeOperation = 'destination-out';

После установки этой операции все последующие вызовы draw будут не добавлять, а "вырезать" пиксели. Таким образом, изображения кисти создают прозрачные дыры в текстуре травы.

Отображение результата и конфигурация сцены

После всех манипуляций текстура готова. Чтобы увидеть ее в игре, мы добавляем ее на сцену как обычное изображение с помощью this.add.image, используя тот же ключ, который был задан при создании ('canvastexture').

this.add.image(0, 0, 'canvastexture').setOrigin(0);

Важно помнить, что игра в этом примере использует рендерер Phaser.CANVAS. Canvas Texture также работает и в WebGL-режиме, но под капотом Phaser будет использовать Canvas API для создания текстуры, а затем загружать ее в видеопамять как обычное изображение.

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

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

Canvas Texture в Phaser — это мост между динамическим Canvas API и игровым рендерером. С его помощью можно создавать маски, повреждения поверхностей, интерактивные элементы, которые стираются, или генерировать уникальный визуальный контент. Для экспериментов попробуйте другие значения globalCompositeOperation (например, 'multiply', 'screen', 'overlay'), рисуйте не изображениями, а примитивами через texture.context.fillRect, или анимируйте процесс рисования, обновляя текстуру в методе update.