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

Работа с графикой на уровне отдельных пикселей открывает огромные возможности для создания уникальных визуальных эффектов в играх: от динамического разрушения ландшафта и систем частиц до стилизации под пиксель-арт и генерации процедурных текстур. В этой статье мы разберем пример из официальной документации Phaser, который демонстрирует, как получить доступ к данным изображения, извлечь цвет каждого пикселя и использовать его для создания новой композиции. Этот метод — фундамент для реализации продвинутых графических механик.

Версия 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('piggy', 'assets/pics/pigchampagne.png');
    }

    create ()
    {
        const src = this.textures.get('piggy').getSourceImage();

        const canvas = this.textures.createCanvas('map', src.width, src.height).draw(0, 0, src);

        //  You can now access the CanvasTexture properties, such as canvas.imageData

        //  Here we'll just create a rectangle for each pixel, with a unique size

        const pixel = new Phaser.Display.Color();

        for (let y = 0; y < src.height; y++)
        {
            for (let x = 0; x < src.width; x++)
            {
                canvas.getPixel(x, y, pixel);

                if (pixel.a > 0)
                {
                    this.add.rectangle(x * 4, y * 8, 4, 8, pixel.color);
                }
            }
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка и загрузка текстуры

Первым делом в методе preload() мы загружаем изображение, с которым будем работать. Важно установить базовый URL для загрузчика, если ресурсы хранятся удаленно. В данном случае используется стандартное изображение из примеров Phaser.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('piggy', 'assets/pics/pigchampagne.png');
}

Получение источника изображения и создание холста

В методе create() начинается основная работа. Сначала мы получаем исходный DOM-элемент изображения (HTMLImageElement) через this.textures.get('piggy').getSourceImage(). Это дает доступ к "сырым" данным картинки.

Затем создается специальный CanvasTexture с именем 'map' и размерами, совпадающими с исходным изображением. Метод .draw(0, 0, src) сразу отрисовывает загруженную картинку на этот холст. Теперь у нас есть текстура Phaser, которая также является HTMLCanvasElement, что позволяет использовать контекст 2D или, как в нашем случае, работать с данными пикселей.

const src = this.textures.get('piggy').getSourceImage();
const canvas = this.textures.createCanvas('map', src.width, src.height).draw(0, 0, src);

Итерация по пикселям и извлечение цвета

Для эффективного получения цвета мы создаем один экземпляр Phaser.Display.Color. Это объект-утилита для работы с цветами, который будет переиспользоваться в цикле, что предотвращает создание тысяч одноразовых объектов.

Два вложенных цикла проходят по всем координатам изображения. Ключевой метод canvas.getPixel(x, y, pixel) записывает цвет пикселя с координатами (x, y) в переданный объект pixel. После вызова этого метода в объекте pixel доступны свойства `r,g,b,aи вычисленное целочисленное значениеcolor`.

const pixel = new Phaser.Display.Color();
for (let y = 0; y < src.height; y++)
{
    for (let x = 0; x < src.width; x++)
    {
        canvas.getPixel(x, y, pixel);
    }
}

Визуализация данных: от пикселей к прямоугольникам

Получив цвет, мы можем его использовать. В примере выполняется проверка if (pixel.a > 0), которая отсекает полностью прозрачные пиксели (альфа-канал равен 0).

Для каждого непрозрачного пикселя создается прямоугольник (this.add.rectangle). Его координаты умножены на разные коэффициенты (4 по X и 8 по Y), что растягивает итоговую композицию, создавая интересный визуальный эффект, отдаленно напоминающий исходное изображение. Цвет прямоугольника задается свойством pixel.color.

if (pixel.a > 0)
{
    this.add.rectangle(x * 4, y * 8, 4, 8, pixel.color);
}

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

Этот пример — отправная точка для экспериментов с пиксельной графикой в Phaser. Попробуйте изменить форму примитивов (используйте add.circle или add.triangle), примените математические функции к координатам и размерам для создания волн, шума или паттернов. Можно реализовать эффект "разлета" пикселей при клике, сохранять модифицированные данные холста обратно в текстуру через canvas.update() или даже загружать пользовательские изображения для их последующей обработки.