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

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

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

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

    create ()
    {
        const texture = this.textures.addDynamicTexture('wheelTexture', 800, 600)

        //  Draw the color wheel to our texture
        texture.stamp('wheel', null, 400, 300).render();

        //  Now add the finished texture to a Sprite
        this.add.sprite(400, 300, 'wheelTexture');

        //  Add a Rectangle so we can show the color we've grabbed
        const rect = this.add.rectangle(10, 10, 128, 128, 0xffffff).setOrigin(0, 0);

        const callback = (color) => {

            rect.setFillStyle(color.color);

        };

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

            texture.snapshotPixel(pointer.worldX, pointer.worldY, callback);

        });
    }
}

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

const game = new Phaser.Game(config);

Создание динамической текстуры и ее отрисовка

Динамическая текстура в Phaser — это текстура, создаваемая и управляемая во время выполнения программы, в отличие от загруженных из файлов. Она ведет себя как обычная текстура, но ее содержимое можно изменять.

В методе create сцены мы создаем такую текстуру с помощью метода this.textures.addDynamicTexture. Ей нужно указать уникальный ключ, ширину и высоту.

const texture = this.textures.addDynamicTexture('wheelTexture', 800, 600)

Затем мы используем метод stamp, чтобы отпечатать на этой динамической текстуре заранее загруженное изображение с ключом 'wheel'. Параметры метода позволяют указать источник, фрейм и координаты (x, y) для позиционирования. После выполнения операции stamp необходимо вызвать render(), чтобы изменения были применены к текстуре.

texture.stamp('wheel', null, 400, 300).render();

Теперь текстуру можно использовать как обычную. Мы создаем спрайт, который будет отображать наше сгенерированное изображение.

this.add.sprite(400, 300, 'wheelTexture');

Также мы заранее создаем белый прямоугольник, который позже будет менять цвет, чтобы визуализировать выбранный пиксель.

const rect = this.add.rectangle(10, 10, 128, 128, 0xffffff).setOrigin(0, 0);

Получение цвета пикселя по клику

Основная задача — получить цвет пикселя в текстуре по координатам указателя мыши. Для этого в Phaser у динамической текстуры есть метод snapshotPixel. Этот метод асинхронно считывает данные цвета.

Мы подписываемся на событие клика (pointerdown) у системного ввода this.input. В обработчике события мы получаем объект pointer, который содержит координаты клика в мире игры (pointer.worldX, pointer.worldY).

this.input.on('pointerdown', pointer => {
    texture.snapshotPixel(pointer.worldX, pointer.worldY, callback);
});

Метод snapshotPixel принимает три аргумента: 1. Координата X точки в текстуре. 2. Координата Y точки в текстуре. 3. Функция обратного вызова (callback), которая будет выполнена, когда цвет будет готов.

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

Обработка результата и визуализация

Функция обратного вызова получает объект color с информацией о цвете. В нашем примере мы заранее определили эту функцию как callback. Она принимает один параметр, который мы назвали color.

const callback = (color) => {
    rect.setFillStyle(color.color);
};

Объект color содержит несколько свойств, но для изменения заливки прямоугольника нам нужно свойство color.color. Это числовое значение цвета в формате, понятном для Phaser (например, 0xff00ff).

Метод setFillStyle у графического объекта Rectangle применяет этот цвет, мгновенно меняя внешний вид прямоугольника на выбранный цвет из текстуры. Таким образом, интерфейс дает игроку немедленную обратную связь.

rect.setFillStyle(color.color);

Конфигурация игры и запуск

Код примера завершается стандартной для Phaser 3 конфигурацией и созданием экземпляра игры. Обратите внимание на размеры сцены (800x600) и цвет фона (#2d2d6d).

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

const game = new Phaser.Game(config);

Размеры сцены совпадают с размерами создаваемой динамической текстуры (800x600), что гарантирует корректное соответствие координат.

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

Использование snapshotPixel для динамических текстур — мощный инструмент для создания интерактивных элементов, связанных с цветом. Вы можете расширить этот пример, чтобы создавать полноценные пипетки в играх-раскрасках, проверять, попал ли игрок в область определенного цвета в головоломках, или динамически генерировать цветовые схемы на основе игрового мира. Попробуйте применить этот метод к нескольким текстурам или сохранять массив выбранных цветов для создания пользовательской палитры.