О чем этот пример
Визуальная атмосфера — ключевой элемент погружения в игру. Phaser 3 предлагает мощную систему освещения, позволяющую легко создавать динамические световые эффекты. В этой статье мы разберем, как работать с точечными источниками света (`PointLight`), изменять их параметры в реальном времени и синхронизировать с движением камеры. Вы научитесь создавать интерактивные световые сцены, где игрок может управлять цветом, интенсивностью и положением источника, что идеально подходит для хорроров, приключений или головоломок с механикой света и тени.
Версия 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('bg', 'assets/textures/gold.png');
}
create ()
{
this.add.image(400, 300, 'bg');
this.add.text(10, 10, 'Wheel: Hue\nW + S: Attenuation');
let colorIndex = 0;
const spectrum = Phaser.Display.Color.ColorSpectrum(128);
this.spotlight = this.add.pointlight(400, 300, 0, 128, 1);
let color = spectrum[colorIndex];
this.spotlight.color.setTo(color.r, color.g, color.b);
colorIndex++;
this.input.on('pointermove', pointer => {
this.spotlight.x = pointer.worldX;
this.spotlight.y = pointer.worldY;
});
this.input.on('wheel', (pointer, over, deltaX, deltaY, deltaZ) => {
if (deltaY < 0)
{
colorIndex--;
}
else if (deltaY > 0)
{
colorIndex++;
}
if (colorIndex === spectrum.length)
{
colorIndex = 0;
}
else if (colorIndex < 0)
{
colorIndex = spectrum.length - 1;
}
color = spectrum[colorIndex];
this.spotlight.color.setTo(color.r, color.g, color.b);
});
this.input.keyboard.on('keydown-W', () => {
this.spotlight.attenuation += 0.01;
});
this.input.keyboard.on('keydown-S', () => {
this.spotlight.attenuation -= 0.01;
});
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
}
update (time, delta)
{
this.controls.update(delta);
// 128 is the size we wish to keep the light at, no matter what the camera zoom level is
this.spotlight.radius = (1 / this.cameras.main.zoom) * 128;
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Создание и базовая настройка света
В Phaser 3 источник точечного света создается с помощью метода this.add.pointlight(). Этот метод возвращает объект типа PointLight, который можно настраивать.
this.spotlight = this.add.pointlight(400, 300, 0, 128, 1);
Первые два аргумента (400, 300) — это начальные координаты X и Y источника света на сцене. Третий аргумент (0) — это цвет в формате целого числа, но в нашем примере он будет переопределен позже. Четвертый аргумент (128) — начальный радиус освещения. Пятый аргумент (1) — начальная интенсивность (attenuation).
Цвет задается отдельно через свойство .color объекта света. В примере используется цветовой спектр, сгенерированный вспомогательной функцией Phaser.Display.Color.ColorSpectrum(128), которая создает массив из 128 цветовых значений.
const spectrum = Phaser.Display.Color.ColorSpectrum(128);
let color = spectrum[colorIndex];
this.spotlight.color.setTo(color.r, color.g, color.b);
Интерактивное управление: мышь и клавиатура
Пример связывает управление светом с действиями пользователя. Положение источника света привязано к курсору мыши с помощью события 'pointermove'.
this.input.on('pointermove', pointer => {
this.spotlight.x = pointer.worldX;
this.spotlight.y = pointer.worldY;
});
Свойства pointer.worldX и pointer.worldY предоставляют координаты курсора в мировой системе координат сцены, что гарантирует корректную работу при движении камеры.
Цикл изменения цвета (Hue) завязан на колесико мыши ('wheel'). Событие передает deltaY, указывающую направление прокрутки. В зависимости от этого значения индекс цвета в массиве spectrum увеличивается или уменьшается, обеспечивая плавный переход по всему спектру.
if (deltaY < 0) {
colorIndex--;
} else if (deltaY > 0) {
colorIndex++;
}
// ... обработка границ массива
color = spectrum[colorIndex];
this.spotlight.color.setTo(color.r, color.g, color.b);
Интенсивность света (затухание, attenuation) изменяется клавишами W и S. Чем выше значение attenuation, тем быстрее свет затухает с расстоянием, создавая более резкую границу светового пятна.
this.input.keyboard.on('keydown-W', () => {
this.spotlight.attenuation += 0.01;
});
this.input.keyboard.on('keydown-S', () => {
this.spotlight.attenuation -= 0.01;
});
Управление камерой и синхронизация света
Для навигации по большой сцене в примере используется Phaser.Cameras.Controls.SmoothedKeyControl. Это готовый контроллер, который обеспечивает плавное перемещение и зум камеры с ускорением и инерцией.
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
Контроллер обновляется в каждом кадре в методе update().
Самая важная часть синхронизации — поддержание визуального размера светового радиуса независимо от зума камеры. Без этого при приближении камеры (zoom > 1) свет будет занимать все меньше экранного пространства, а при отдалении (zoom < 1) — неограниченно расти.
update (time, delta) {
this.controls.update(delta);
this.spotlight.radius = (1 / this.cameras.main.zoom) * 128;
}
Формула (1 / this.cameras.main.zoom) * 128 компенсирует зум. Если камера приближена в 2 раза (zoom = 2), радиус света будет установлен в (1/2)*128 = 64, чтобы видимый размер остался примерно равным исходным 128 пикселям.
Конфигурация игры и настройка сцены
Классическая конфигурация игры (config) определяет базовые параметры, такие как размер холста, цвет фона и корневой сценой является наш класс Example.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Обратите внимание на черный цвет фона ('#000000'). На темном фоне световые эффекты выглядят наиболее контрастно и эффектно. В методе preload() загружается текстурный фон ('bg'), на котором будут видны световые блики. Загрузка настроена на использование публичного репозитория с примерами Phaser.
preload () {
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/textures/gold.png');
}
create () {
this.add.image(400, 300, 'bg');
// ... остальной код создания
}
Что попробовать дальше
Точечные источники света в Phaser 3 — это гибкий инструмент для создания атмосферы и интерактивных механик. Вы можете использовать их для подсветки персонажа, создания фонарика, реализации "зрения" для врагов или просто для украшения интерфейса. Для экспериментов попробуйте: изменить формулу расчета радиуса для другого визуального эффекта; добавить несколько источников света с разными цветами; привязать свет не к курсору, а к спрайту игрока; использовать маску (LightLayer имеет метод setMask) для создания сложных форм освещения, например, через щель в двери.
