О чем этот пример
При работе с контейнерами в Phaser важно понимать, как свойство `alpha` применяется к дочерним объектам. Этот пример демонстрирует тонкость, связанную с наложением графики (`Graphics`) внутри контейнера. Мы разберем, почему при установке общей прозрачности контейнера визуальный результат может отличаться от ожидаемого, и как это знание поможет избежать графических артефактов в ваших играх.
Версия 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('pic', 'assets/pics/ra-einstein.png');
}
create ()
{
this.add.image(200, 200, 'pic');
const a = this.add.graphics();
a.fillStyle(0xff0000, 1);
a.fillRect(0, 0, 100, 100);
const b = this.add.graphics();
b.fillStyle(0x00ff00, 1);
b.fillRect(70, 0, 100, 100);
const c = this.add.container();
c.add(this.add.image(0, 0, 'pic'));
c.add(a);
c.add(b);
c.setAlpha(0.5);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Сцена и загрузка ресурсов
Класс Example расширяет Phaser.Scene. В методе preload устанавливается базовый URL для загрузки и загружается одно изображение pic.
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/ra-einstein.png');
}
Создание базовых объектов
В методе create сначала добавляется обычное изображение в координаты (200, 200). Оно служит лишь фоном для сравнения.
Затем создаются два графических объекта (Graphics) `aиb`. Каждый из них получает свой цвет заливки и рисует прямоугольник. Обратите внимание, что прямоугольники частично перекрываются.
create ()
{
this.add.image(200, 200, 'pic');
const a = this.add.graphics();
a.fillStyle(0xff0000, 1);
a.fillRect(0, 0, 100, 100);
const b = this.add.graphics();
b.fillStyle(0x00ff00, 1);
b.fillRect(70, 0, 100, 100);
Группировка в контейнер
Далее создается контейнер `cс помощьюthis.add.container(). В него добавляются ранее созданные объекты: изображениеpic(уже второе, но с координатами (0,0 относительно контейнера) и два графических объектаaиb`.
const c = this.add.container();
c.add(this.add.image(0, 0, 'pic'));
c.add(a);
c.add(b);
Ключевой момент: применение прозрачности
Вот где начинается самое интересное. Для контейнера `c` устанавливается общая прозрачность (альфа-канал) в 0.5. Важно понимать, что это свойство применяется ко всему контейнеру как к единому целому *после* того, как его дочерние объекты были отрендерены в своем обычном виде (включая их взаимное наложение).
c.setAlpha(0.5);
}
Почему это важно?
Если бы мы установили прозрачность каждому объекту `aиbпо отдельности, область их перекрытия смешала бы цвета с учетом их собственных значений alpha. Однако при установкеalpha` на контейнер, прозрачность применяется к уже готовому, совмещенному изображению всех его детей. Это может привести к неожиданным визуальным эффектам, особенно когда объекты внутри контейнера перекрываются и имеют разные цвета или текстуры. В данном примере область пересечения красного и зеленого прямоугольников будет показана как полупрозрачное наложение, а не как результат смешения двух полупрозрачных цветов.
Конфигурация игры и ее запуск стандартны.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Управление прозрачностью через контейнер — мощный инструмент для групповых эффектов, но он работает на уровне итогового рендера группы. Для сложного смешения цветов и alpha-композитинга внутри группы лучше управлять прозрачностью каждого объекта индивидуально. Поэкспериментируйте: попробуйте установить a.setAlpha(0.5) и b.setAlpha(0.5) вместо прозрачности контейнера и сравните результат. Или добавьте в контейнер спрайты с blend mode и посмотрите, как container.alpha взаимодействует с ними.
