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

При работе с контейнерами в 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 взаимодействует с ними.