О чем этот пример
Создание погружающей атмосферы — ключ к увлекательной игре. Статичный фон и плоские спрайты давно в прошлом. Этот пример демонстрирует, как использовать фильтр Image Light в Phaser для добавления динамического освещения, которое реагирует на окружение и анимацию. Вы узнаете, как заставить объект отражать движущееся небо, лес и летающие частицы, создавая иллюзию объёма и реального света без сложных 3D-расчётов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
background;
backgroundLight;
environmentSky;
environmentTrees;
environmentCompositeTexture;
environmentCompositeImage;
redbubble;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/textures/alien-metal.jpg');
this.load.image('bg_n', 'assets/textures/alien-metal-n.jpg');
this.load.image('spider', 'assets/normal-maps/spider.png');
this.load.image('spider_n', 'assets/normal-maps/spider_n.png');
this.load.image('ms3-sky', 'assets/skies/ms3-sky.png');
this.load.image('ms3-trees', 'assets/skies/ms3-trees.png');
this.load.image('redbubble', 'assets/particles/redbubble.png');
}
create ()
{
this.environmentSky = this.add.tileSprite(0, 0, 1280, 444, 'ms3-sky').setTileScale(1.5).setOrigin(0).setVisible(false);
this.environmentTrees = this.add.tileSprite(0, 400, 1280, 320, 'ms3-trees').setOrigin(0).setVisible(false);
this.redbubble = this.add.image(640, 128, 'redbubble').setScale(2);
this.environmentCompositeTexture = this.textures.addDynamicTexture('environmentComposite', 1280, 720);
this.environmentCompositeImage = this.add.image(0, 0, 'environmentComposite').setOrigin(0);
this.spider = this.add.image(640, 360, 'spider').enableFilters();
this.spiderLight = this.spider.filters.internal.addImageLight({
environmentMap: 'environmentComposite',
normalMap: 'spider_n',
colorFactor: [ 2, 2, 2 ],
modelRotationSource: this.spider
});
}
update (time, delta)
{
this.environmentSky.tilePositionX = time / 60;
this.environmentTrees.tilePositionX = time / 20;
this.redbubble.setPosition(512 * Math.cos(time / 1000) + 640, 256 * Math.sin(time / 1000) + 360);
this.environmentCompositeTexture.draw(this.environmentSky).draw(this.environmentTrees).draw(this.redbubble).render();
this.spider.setRotation(0.2 * Math.sin(time/3000));
}
}
const config = {
type: Phaser.WEBGL,
width: 1280,
height: 720,
backgroundColor: '#2d3440',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов: текстуры и карты нормалей
Для работы фильтра ImageLight необходимы две группы ресурсов: обычные текстуры и их карты нормалей (normal maps). Карта нормалей — это специальное изображение, где цвет каждого пикселя кодирует информацию о направлении поверхности (её «выпуклостях» и «впадинах»). Фильтр использует эти данные для расчёта отражения света.
this.load.image('bg', 'assets/textures/alien-metal.jpg');
this.load.image('bg_n', 'assets/textures/alien-metal-n.jpg'); // _n суффикс для карты нормалей
this.load.image('spider', 'assets/normal-maps/spider.png');
this.load.image('spider_n', 'assets/normal-maps/spider_n.png');
Также загружаются фоновые слои (ms3-sky, ms3-trees) и текстура частицы (redbubble), которые позже станут источником света в нашей сцене.
Создание динамического окружения
Окружение, которое будет отражаться на объекте, собирается из нескольких слоёв в одну динамическую текстуру. Это позволяет объединить независимо анимированные элементы (небо, деревья, частицы) в единую "карту окружения" (environment map).
// Создаём невидимые плиточные спрайты для фона
this.environmentSky = this.add.tileSprite(0, 0, 1280, 444, 'ms3-sky').setTileScale(1.5).setOrigin(0).setVisible(false);
this.environmentTrees = this.add.tileSprite(0, 400, 1280, 320, 'ms3-trees').setOrigin(0).setVisible(false);
// Создаём видимую частицу
this.redbubble = this.add.image(640, 128, 'redbubble').setScale(2);
// Создаём пустую динамическую текстуру
this.environmentCompositeTexture = this.textures.addDynamicTexture('environmentComposite', 1280, 720);
// Создаём изображение, которое будет отображать эту текстуру (для отладки)
this.environmentCompositeImage = this.add.image(0, 0, 'environmentComposite').setOrigin(0);
Ключевой объект — environmentCompositeTexture. Это холст в памяти, на который мы будем рисовать все слои окружения каждый кадр.
Применение фильтра Image Light к объекту
Фильтр добавляется к игровому объекту, который должен отражать окружение. В данном случае это спрайт паука. Важно сначала вызвать .enableFilters() на целевом изображении.
this.spider = this.add.image(640, 360, 'spider').enableFilters();
this.spiderLight = this.spider.filters.internal.addImageLight({
environmentMap: 'environmentComposite', // Имя нашей динамической текстуры
normalMap: 'spider_n', // Карта нормалей для паука
colorFactor: [ 2, 2, 2 ], // Множитель интенсивности отражения (RGB)
modelRotationSource: this.spider // Объект, чьё вращение влияет на расчёт
});
Параметр modelRotationSource связывает вращение модели (паука) с расчётом отражения. Когда паук поворачивается, "свет" скользит по его поверхности естественным образом, создавая иллюзию объёма.
Анимация в методе Update
Вся магия оживает в методе update. Здесь анимируются фоновые слои, частица и обновляется составная текстура окружения, которая, в свою очередь, влияет на отражение на пауке.
// Анимация прокрутки фона для иллюзии движения
this.environmentSky.tilePositionX = time / 60;
this.environmentTrees.tilePositionX = time / 20;
// Круговое движение частицы по синусоиде и косинусоиде
this.redbubble.setPosition(512 * Math.cos(time / 1000) + 640, 256 * Math.sin(time / 1000) + 360);
// Отрисовка всех слоёв в динамическую текстуру и её принудительный рендер
this.environmentCompositeTexture.draw(this.environmentSky).draw(this.environmentTrees).draw(this.redbubble).render();
// Плавное вращение паука
this.spider.setRotation(0.2 * Math.sin(time/3000));
Цепочка вызовов .draw().draw().render() перерисовывает динамическую текстуру каждый кадр. Поскольку фильтр ImageLight использует эту текстуру как environmentMap, отражение на пауке мгновенно обновляется, реагируя на движение пузыря и прокрутку неба.
Что попробовать дальше
Фильтр ImageLight — мощный инструмент для добавления динамического освещения на основе окружения (Image-Based Lighting). Он превращает статичные 2D-спрайты в визуально сложные объекты, взаимодействующие с миром. Для экспериментов попробуйте: изменить colorFactor на [0, 4, 0] для зловещего зелёного свечения; использовать другую карту нормалей для грубой или гладкой поверхности; добавить больше светящихся частиц в составную текстуру или сделать источником света интерфейс игры.
