О чем этот пример
Визуальная атмосфера — ключевой элемент игрового погружения. Phaser 3 предоставляет мощную, но простую в использовании систему освещения на основе WebGL. В этой статье мы разберём, как добавить в сцену один динамический источник света, который будет автоматически взаимодействовать с текстурой объекта, создавая эффект объёма и глубины. Это отличная отправная точка для создания мрачных подземелий, таинственных локаций или просто добавления визуального шарма вашим 2D-сценам.
Версия 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('brick', [ 'assets/normal-maps/brick.jpg', 'assets/normal-maps/brick_n.png' ]);
this.load.image('logo', 'assets/sprites/atari130xe.png');
}
create ()
{
this.add.image(400, 300, 'brick').setLighting(true);
this.add.image(400, 300, 'logo');
this.lights.enable().setAmbientColor(0x555555);
const hsv = Phaser.Display.Color.HSVColorWheel();
const radius = 80;
const intensity = 6;
const x = radius;
const y = 0;
const color = hsv[10].color;
const light = this.lights.addLight(400, y, radius, color, intensity);
this.tweens.add({
targets: light,
y: 600,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
duration: 2000
});
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
parent: 'phaser-example',
backgroundColor: '#000000',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
Прежде чем работать со светом, необходимо подготовить сцену и загрузить специальные ассеты. Ключевой момент — использование текстуры с нормальной картой (normal map). Нормальная карта — это дополнительное изображение, которое кодирует информацию о направлении поверхности (её «неровностях»). Именно эта информация позволяет плоскому 2D-спрайту корректно взаимодействовать с источником света, создавая иллюзию объёма.
В методе preload мы загружаем два изображения: основную текстуру и её нормальную карту.
this.load.image('brick', [ 'assets/normal-maps/brick.jpg', 'assets/normal-maps/brick_n.png' ]);
this.load.image('logo', 'assets/sprites/atari130xe.png');
Обратите внимание на синтаксис загрузки: массив передаётся как второй аргумент. Phaser автоматически связывает основное изображение (brick.jpg) и карту нормалей (brick_n.png) под одним ключом 'brick'. Второй спрайт ('logo') загружается без нормальной карты и не будет реагировать на свет.
Создание объектов и включение света
В методе create мы размещаем объекты в мире и активируем систему освещения.
this.add.image(400, 300, 'brick').setLighting(true);
this.add.image(400, 300, 'logo');
Для кирпичной текстуры мы сразу вызываем метод .setLighting(true). Это указывает рендереру, что данный объект должен учитывать освещение и использовать свою нормальную карту.
Далее мы включаем систему освещения для всей сцены и устанавливаем цвет окружающего (фонового) света.
this.lights.enable().setAmbientColor(0x555555);
Метод this.lights.enable() активирует систему. setAmbientColor(0x555555) задаёт серый цвет фоновой подсветки, которая равномерно освещает все объекты на сцене. Без неё области, не попавшие под прямой свет, были бы абсолютно чёрными.
Настройка и анимация источника света
Теперь создадим сам движущийся источник света. Сначала мы получаем палитру цветов в формате HSV (Hue, Saturation, Value), что удобно для плавных цветовых переходов.
const hsv = Phaser.Display.Color.HSVColorWheel();
const color = hsv[10].color;
В данном примере мы берём цвет под индексом 10 (оттенок оранжевого) из полученного массива.
Создаём свет с помощью метода this.lights.addLight().
const light = this.lights.addLight(400, y, radius, color, intensity);
Аргументы метода:
1. **x, y**: начальные координаты источника света.
2. **radius**: радиус действия света в пикселях.
3. **color**: цвет света в числовом формате (например, 0xff5500).
4. **intensity**: интенсивность (яркость) света. Значение 6 делает его очень ярким.
Чтобы свет ожил, мы добавляем к нему твин (анимацию) с помощью this.tweens.add().
this.tweens.add({
targets: light,
y: 600,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
duration: 2000
});
Эта анимация бесконечно (repeat: -1) двигает свет вниз до координаты y=600 и обратно (yoyo: true), создавая плавное движение по синусоиде (ease: 'Sine.easeInOut') за 2 секунды.
Конфигурация игры: важность WebGL
Система освещения Phaser работает исключительно в контексте WebGL. Это критически важно указать в конфигурации игры.
const config = {
type: Phaser.WEBGL, // Обязательно WEBGL, а не CANVAS
width: 800,
height: 600,
parent: 'phaser-example',
backgroundColor: '#000000',
scene: Example
};
Если установить type: Phaser.CANVAS, освещение работать не будет. Фоновый цвет сцены установлен в чёрный (#000000), чтобы контраст со светом был максимально заметным.
Что попробовать дальше
Вы только что реализовали динамическое освещение с одним источником света в Phaser 3. Это основа для создания сложных световых сцен. Для экспериментов попробуйте изменить параметры света (цвет, радиус, интенсивность), добавить несколько источников с разными траекториями движения или применить свет к анимированным спрайтам. Чтобы углубиться, изучите методы this.lights.addLight для создания цветовых градиентов и настройки аттенюации (затухания света с расстоянием).
