О чем этот пример
Визуальные эффекты (FX) в Phaser 3 позволяют легко оживить игровые объекты, но их динамическое добавление и удаление может привести к неочевидным ошибкам. В этой статье мы разберем пример из официального репозитория, где создается и удаляется тень у спрайта каждый кадр. Мы объясним, как работает система пост-эффектов `postFX`, почему код в примере может быть проблемным и как правильно управлять эффектами, чтобы избежать утечек памяти и артефактов рендеринга.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Demo extends Phaser.Scene {
preload() {
this.load.image('mushroom', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/assets/images/mushroom.png');
this.load.image('classroom', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/assets/images/backgrounds/classroom.png');
}
create() {
this.add.image(400, 300, 'classroom')
var gameObject = this.add.image(400, 200, 'mushroom')
this.gameObject = gameObject;
}
update() {
if (this.effect) {
this.gameObject.postFX.remove(this.effect);
this.effect = undefined;
} else {
this.effect = this.gameObject.postFX.addShadow(10, -10, 0.006, 1, 0xff0000, 10)
}
}
}
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scale: {
// mode: Phaser.Scale.FIT,
// autoCenter: Phaser.Scale.CENTER_BOTH,
},
scene: Demo
};
var game = new Phaser.Game(config);
Разбор примера: что происходит в коде
В представленном примере создается простая сцена с двумя изображениями: фоном (classroom) и спрайтом (mushroom). Основная логика заключена в методе update(), который выполняется каждый кадр.
update() {
if (this.effect) {
this.gameObject.postFX.remove(this.effect);
this.effect = undefined;
} else {
this.effect = this.gameObject.postFX.addShadow(10, -10, 0.006, 1, 0xff0000, 10)
}
}
Каждый кадр проверяется, существует ли уже эффект this.effect. Если эффект есть — он удаляется с объекта с помощью this.gameObject.postFX.remove(). Если эффекта нет — создается новая тень с помощью this.gameObject.postFX.addShadow(). Это приводит к тому, что тень мигает: появляется и исчезает каждый кадр.
Ключевой момент: метод update() вызывается на каждом тике игрового цикла (обычно 60 раз в секунду). Динамическое создание и удаление объектов в таком цикле — ресурсоемкая операция.
Как работает система PostFX в Phaser 3
Система postFX (пост-эффектов) в Phaser 3 позволяет добавлять к игровым объектам визуальные модификации, такие как тени, свечение, размытие и другие шейдерные эффекты. Эти эффекты применяются после отрисовки основного спрайта.
// Создание эффекта тени и сохранение ссылки на него
this.effect = this.gameObject.postFX.addShadow(x, y, decay, power, color, samples);
Метод addShadow принимает несколько параметров:
* `x,y`: смещение тени относительно спрайта.
* decay: скорость затухания тени (чем меньше, тем длиннее "хвост").
* power: интенсивность эффекта.
* color: цвет тени в числовом формате (0xff0000 — красный).
* samples: качество рендеринга (больше — качественнее, но тяжелее).
Эффект, возвращаемый addShadow(), это объект, который можно позже удалить или модифицировать. Удаление происходит через метод remove() того же контейнера postFX, куда эффект был добавлен.
// Удаление конкретного эффекта
this.gameObject.postFX.remove(this.effect);
Проблема в примере и как её избежать
Исходный пример, вероятно, создан для демонстрации конкретного бага (bugs/6681) или артефакта, возникающего при таком частом добавлении/удалении. В реальном проекте такой подход неэффективен и может привести к: 1. **Утечке памяти или ресурсов:** Хотя эффект удаляется, постоянное создание новых объектов в цикле нагружает сборщик мусора. 2. **Нестабильности рендеринга:** Система может не успевать корректно очищать или применять эффекты каждый кадр.
**Правильный подход:** Создайте эффект один раз (например, в create()), а в update() лишь изменяйте его свойства, если это необходимо для анимации.
create() {
this.add.image(400, 300, 'classroom');
this.gameObject = this.add.image(400, 200, 'mushroom');
// Создаем эффект один раз при создании сцены
this.shadowEffect = this.gameObject.postFX.addShadow(10, -10, 0.006, 1, 0xff0000, 10);
}
update() {
// Теперь мы можем анимировать свойства эффекта, не пересоздавая его
// Например, меняем смещение тени по синусоиде
this.shadowEffect.x = 10 + Math.sin(this.time.now * 0.001) * 5;
}
Если эффект нужно действительно включать и выключать, делайте это по какому-то событию (клик, таймер), а не каждый кадр.
Практическое применение: управление видимостью эффекта
Часто нужно не удалять эффект, а временно его скрывать или делать неактивным. Прямого свойства visible у объектов эффектов postFX нет, но можно управлять этим через родительский объект или сам эффект.
**Способ 1: Отключаем рендеринг FX для объекта целиком.**
// Временно отключаем все пост-эффекты на объекте
this.gameObject.postFX.setActive(false);
// Включаем обратно
this.gameObject.postFX.setActive(true);
**Способ 2: Управляем через флаги в логике игры.** Создайте логическую переменную-флаг и применяйте эффект только когда это нужно.
create() {
// ... загрузка и создание объекта ...
this.hasShadow = true;
this.shadowEffect = this.gameObject.postFX.addShadow(5, 5, 0.005, 0.8, 0x000000, 8);
}
// В методе, вызываемом по событию (например, по клику)
toggleShadow() {
this.hasShadow = !this.hasShadow;
if (this.hasShadow) {
// Если тени не было, добавляем (если удаляли ранее)
this.shadowEffect = this.gameObject.postFX.addShadow(5, 5, 0.005, 0.8, 0x000000, 8);
} else {
// Если тень есть, удаляем её
this.gameObject.postFX.remove(this.shadowEffect);
this.shadowEffect = null;
}
}
Второй способ более ресурсоемкий, чем первый, но дает полный контроль.
Что попробовать дальше
Система PostFX в Phaser 3 — мощный инструмент для визуального улучшения игр. Ключевой вывод: избегайте создания и удаления эффектов внутри игрового цикла update(). Вместо этого инициализируйте эффекты при создании объектов и управляйте их параметрами или активностью. Для экспериментов попробуйте: анимировать цвет тени в зависимости от времени дня в игре, комбинировать несколько эффектов (тень + свечение) на одном объекте или привязывать включение эффекта к игровым событиям, например, получению усиления персонажем.
