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

Эффекты освещения способны кардинально преобразить визуал 2D-игры, добавив ей атмосферу и глубину. Phaser 3 предлагает встроенную систему света, которая работает с WebGL-рендерингом. В этой статье мы разберём практический пример, где с помощью `TileSprite` и источника света создаётся интерактивная сцена с движущейся текстурой и прожектором, управляемым курсором мыши. Вы научитесь включать освещение, настраивать ambient-свет, создавать и перемещать динамические источники, а также анимировать текстуры — всё это базовые навыки для создания современных 2D-игр с богатой графикой.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    tilesprite;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('brick', [ 'assets/normal-maps/brick.jpg', 'assets/normal-maps/brick_n.png' ]);
        this.load.image('sonic', 'assets/sprites/sonic_havok_sanity.png');
    }

    create ()
    {
        this.tilesprite = this.add.tileSprite(400, 300, 800, 600, 'brick').setLighting(true);

        this.add.sprite(680, 600, 'sonic').setOrigin(0.5, 1);

        this.lights.enable();
        this.lights.setAmbientColor(0x808080);

        const spotlight = this.lights.addLight(400, 300, 280).setIntensity(3);

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

            spotlight.x = pointer.x;
            spotlight.y = pointer.y;

        });

        const colors = [
            0xffffff, 0xff0000, 0x00ff00, 0x00ffff, 0xff00ff, 0xffff00
        ];

        let currentColor = 0;

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

            currentColor++;

            if (currentColor === colors.length)
            {
                currentColor = 0;
            }

            spotlight.setColor(colors[currentColor]);

        });
    }

    update ()
    {
        this.tilesprite.tilePositionX += 0.3;
        this.tilesprite.tilePositionY += 0.6;
    }
}

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


const game = new Phaser.Game(config);

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

Класс Example наследуется от Phaser.Scene. В методе preload() загружаются необходимые изображения. Обратите внимание на загрузку изображения 'brick': передаётся массив из двух путей. Первый путь — это обычная текстурная карта (diffuse map), а второй — карта нормалей (normal map). Карта нормалей необходима для корректного расчёта освещения, чтобы свет "обтекал" неровности текстуры. Второй загружаемый спрайт ('sonic') использует только обычную текстуру.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('brick', [ 'assets/normal-maps/brick.jpg', 'assets/normal-maps/brick_n.png' ]);
this.load.image('sonic', 'assets/sprites/sonic_havok_sanity.png');

Создание объектов и включение освещения

В методе create() создаётся игровой мир. Сначала создаётся TileSprite — спрайт с тайловой (повторяющейся) текстурой. Ключевой метод .setLighting(true) активирует для этого объекта обработку освещения. Без этого вызова свет на спрайте отображаться не будет.

this.tilesprite = this.add.tileSprite(400, 300, 800, 600, 'brick').setLighting(true);

Затем добавляется обычный спрайт Соника. Далее активируется вся система освещения сцены с помощью this.lights.enable(). Метод this.lights.setAmbientColor(0x808080) устанавливает цвет окружающего (ambient) света. Это базовый серый свет, который равномерно освещает все объекты с включённым освещением, даже без дополнительных источников.

this.lights.enable();
this.lights.setAmbientColor(0x808080);

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

Основной источник света создаётся методом this.lights.addLight(). Ему передаются начальные координаты и радиус. Метод .setIntensity(3) увеличивает интенсивность свечения в три раза от стандартной. Полученный объект spotlight сохраняется в константу для дальнейшего управления.

const spotlight = this.lights.addLight(400, 300, 280).setIntensity(3);

Для создания интерактивности на событие перемещения указателя ('pointermove') вешается обработчик. В нём координаты прожектора (spotlight.x, spotlight.y) приравниваются к координатам курсора. Таким образом, источник света следует за движением мыши.

this.input.on('pointermove', pointer => {
    spotlight.x = pointer.x;
    spotlight.y = pointer.y;
});

Изменение цвета света по клику

В этом блоке кода добавляется возможность менять цвет прожектора по нажатию кнопки мыши. Создаётся массив colors с шестью вариантами цветов в HEX-формате. Переключение происходит по кругу: с каждым кликом индекс currentColor увеличивается, а при достижении конца массива — сбрасывается на ноль. Цвет источника света меняется с помощью метода .setColor().

const colors = [
    0xffffff, 0xff0000, 0x00ff00, 0x00ffff, 0xff00ff, 0xffff00
];
let currentColor = 0;

this.input.on('pointerdown', pointer => {
    currentColor++;
    if (currentColor === colors.length) {
        currentColor = 0;
    }
    spotlight.setColor(colors[currentColor]);
});

Анимация фоновой текстуры

Чтобы сцена не была статичной, в методе update(), который вызывается каждый кадр, происходит смещение внутренней позиции тайлов текстуры TileSprite. Изменяя свойства .tilePositionX и .tilePositionY, мы создаём иллюзию плавного движения фонового кирпича. Это простой и эффективный способ анимировать большие фоны без использования спрайт-листов или тяжёлой логики.

update ()
{
    this.tilesprite.tilePositionX += 0.3;
    this.tilesprite.tilePositionY += 0.6;
}

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

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

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};
const game = new Phaser.Game(config);

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

Вы изучили основы работы с системой динамического освещения в Phaser 3. Вы можете создавать статические и движущиеся источники света, менять их параметры в реальном времени и применять освещение к тайловым спрайтам и другим объектам. Для экспериментов попробуйте: добавить несколько независимых источников света, привязать свет к движущемуся игровому персонажу вместо курсора, изменить параметры ambient-освещения для создания разного времени суток или использовать другие типы текстур с картами нормалей для более сложных визуальных эффектов.