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

Визуальные эффекты (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(). Вместо этого инициализируйте эффекты при создании объектов и управляйте их параметрами или активностью. Для экспериментов попробуйте: анимировать цвет тени в зависимости от времени дня в игре, комбинировать несколько эффектов (тень + свечение) на одном объекте или привязывать включение эффекта к игровым событиям, например, получению усиления персонажем.