О чем этот пример
В Phaser прозрачность контейнера — это не всегда то, что вы видите. Обычный `setAlpha()` делает каждый дочерний объект полупрозрачным, а эффект наложения этих объектов может выглядеть неожиданно. Но есть и другой подход: рендерить всю группу объектов как единое целое, а потом применять прозрачность к получившемуся изображению. Эта статья покажет, как управлять этим поведением с помощью флага `filtersForceComposite`, открывая путь к более контролируемым визуальным эффектам для ваших UI-элементов, всплывающих окон или сложных спрайтовых сборок.
Версия 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('ball-pink', 'assets/sprites/ball-pink.png');
}
create ()
{
// Create overlapping balls in a transparent container.
const container1 = this.add.container(420, 360).setAlpha(0.5);
const ball1 = this.add.image(0, -140, 'ball-pink');
const ball2 = this.add.image(0, 0, 'ball-pink');
const ball3 = this.add.image(0, 140, 'ball-pink');
container1.add(ball1);
container1.add(ball2);
container1.add(ball3);
// Create overlapping balls in a container.
const container2 = this.add.container(840, 360);
const ball4 = this.add.image(0, -140, 'ball-pink');
const ball5 = this.add.image(0, 0, 'ball-pink');
const ball6 = this.add.image(0, 140, 'ball-pink');
container2.add(ball4);
container2.add(ball5);
container2.add(ball6);
container2.enableFilters();
container2.filtersForceComposite = true;
container2.filterCamera.alpha = 0.5;
// By setting `filtersForceComposite` to true,
// the container renders to a framebuffer even though there are no filters.
// The filter camera's alpha is thus applied to the container.
}
}
const config = {
type: Phaser.AUTO,
width: 1280,
height: 720,
backgroundColor: '#446688',
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Проблема наложения альфа-каналов
Когда вы устанавливаете прозрачность контейнеру или объекту через setAlpha(), Phaser применяет это значение альфа-канала к каждому дочернему элементу по отдельности во время рендеринга. Это стандартное и эффективное поведение. Однако, когда полупрозрачные объекты перекрываются, их цвета смешиваются (blend) на экране. Это может привести к нежелательному визуальному эффекту — область перекрытия становится более насыщенной и менее прозрачной, чем хотелось бы, потому что альфа-каналы накладываются друг на друга.
Пример с первой группой шаров в исходном коде демонстрирует именно это:
Классический подход: прозрачность на каждом объекте
В первой части примера создается контейнер с альфа-каналом 0.5, и в него добавляются три спрайта. Каждый шар наследует эту прозрачность от родителя.
const container1 = this.add.container(420, 360).setAlpha(0.5);
const ball1 = this.add.image(0, -140, 'ball-pink');
const ball2 = this.add.image(0, 0, 'ball-pink');
const ball3 = this.add.image(0, 140, 'ball-pink');
container1.add(ball1);
container1.add(ball2);
container1.add(ball3);
При таком подходе каждый шар отрисовывается на холст с прозрачностью 50%. В местах, где шары перекрываются, мы видим не цельный полупрозрачный "столбик" из трех шаров, а три отдельных полупрозрачных слоя, наложенных друг на друга. Визуально это выглядит как три отдельных полупрозрачных изображения.
Композитный рендеринг: контейнер как единое целое
Phaser предоставляет механизм, позволяющий отрендерить все содержимое контейнера в отдельный буфер (framebuffer), как если бы это было одно изображение, и только потом применить к этому результату визуальные эффекты, включая прозрачность. Ключ к этому — свойство filtersForceComposite.
container2.enableFilters();
container2.filtersForceComposite = true;
container2.filterCamera.alpha = 0.5;
Давайте разберем по порядку:
1. enableFilters() — этот вызов подготавливает контейнер к использованию фильтров (например, размытия или цветокоррекции). По умолчанию, если фильтры не добавлены, композитный рендеринг не активируется.
2. filtersForceComposite = true — это самый важный флаг. Он принудительно заставляет Phaser рендерить весь контейнер в отдельный буфер, даже если к нему не применено ни одного фильтра.
3. filterCamera.alpha = 0.5 — теперь мы устанавливаем прозрачность не для самого контейнера или его детей, а для виртуальной "камеры", которая смотрит на этот отдельно отрендеренный буфер. Альфа-канал применяется ко всему изображению буфера сразу после его подготовки.
Сравнение результатов и практический смысл
Запустив пример, вы увидите две группы из трех одинаковых шаров. Левая группа (container1) будет выглядеть как три отдельных полупрозрачных слоя. Правая группа (container2) будет выглядеть как одно цельное изображение из трех шаров с равномерной прозрачностью 50% по всей площади.
Этот подход полезен, когда вам нужно:
* Создать полупрозрачную панель интерфейса со множеством элементов (текст, иконки), которые должны восприниматься как единое целое.
* Реализовать эффект затемнения или выделения группы объектов.
* Подготовить контейнер для последующего применения сложных фильтров (например, Blur), которые должны воздействовать на всю группу, а не на каждый объект по отдельности.
Важное замечание: композитный рендеринг требует дополнительных вычислительных ресурсов (создание буфера, дополнительный проход рендеринга), поэтому используйте его осознанно, особенно для часто обновляемых элементов.
Что попробовать дальше
Использование filtersForceComposite — это мощный прием для тонкого контроля над рендерингом групп объектов в Phaser. Он позволяет работать с контейнером как с единым визуальным блоком, что открывает двери для более сложных и целостных эффектов. Для экспериментов попробуйте добавить к container2 настоящий фильтр, например, размытие, и увидите, как он равномерно применится ко всем шарам. Или анимируйте свойство filterCamera.alpha для создания плавного эффекта появления/исчезания целой группы спрайтов.
