О чем этот пример
При создании сложных сцен в Phaser 3 разработчики часто сталкиваются с необходимостью группировать игровые объекты для их совместного обновления, трансформации или отрисовки. Двумя основными инструментами для этого являются `Container` и `Layer`. Эта статья на практическом примере разберёт ключевые различия между ними, покажет, как они влияют на структуру дерева отображения (Display List), и объяснит, когда стоит выбрать один инструмент вместо другого. Понимание этой механики критически важно для эффективной организации кода и управления рендерингом в вашей игре.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example2 extends Phaser.Scene
{
constructor ()
{
super('example2');
}
create ()
{
this.add.text(10, 10, 'Layer 2', { font: '16px Courier', fill: '#00ff00' });
this.input.once('pointerdown', () => {
this.scene.start('example1');
});
}
}
class Example extends Phaser.Scene
{
constructor ()
{
super('example1');
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('spaceman', 'assets/sprites/exocet_spaceman.png');
}
create ()
{
this.add.text(10, 10, 'Scene 1', { font: '16px Courier', fill: '#00ff00' });
this.add.sprite(400, 300, 'spaceman');
const bob = this.add.container();
bob.add(this.add.sprite(500, 400, 'spaceman'));
bob.add(this.add.sprite(550, 500, 'spaceman'));
const spaceman = this.add.sprite(150, 300, 'spaceman');
const layer = this.add.layer();
layer.add([ spaceman ]);
this.input.once('pointerdown', () => {
this.scene.start('example2');
});
console.log(this.children);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d88',
parent: 'phaser-example',
scene: [ Example, Example2 ]
};
const game = new Phaser.Game(config);
Разбор примера: две сцены и структура объектов
В приведённом примере создаются две простые сцены: Example (ключ 'example1') и Example2. Основное действие происходит в первой сцене. Её задача — наглядно продемонстрировать, как добавление объектов в Container и Layer влияет на их положение в иерархии.
Давайте посмотрим на метод create() сцены Example. В нём создаются несколько спрайтов и группы.
Container: группа как единый объект
Контейнер (Container) — это объект-родитель, который группирует дочерние элементы. Все трансформации (позиция, масштаб, вращение), применённые к контейнеру, автоматически влияют на всех его детей. В примере создаётся контейнер bob, в который добавляются два спрайта.
const bob = this.add.container();
bob.add(this.add.sprite(500, 400, 'spaceman'));
bob.add(this.add.sprite(550, 500, 'spaceman'));
Важно понимать, что контейнер сам становится дочерним элементом сцены. Добавленные в него спрайты больше не являются прямыми детьми сцены, они — дети контейнера bob. Это меняет их глобальные координаты и порядок обработки.
Layer: менеджер отображения, а не родитель
Слой (Layer) работает иначе. Его основная задача — управлять глубиной (z-index) и состоянием рендеринга для группы объектов, но при этом он не становится их родителем в классическом понимании. Создадим слой и добавим в него отдельный спрайт spaceman.
const spaceman = this.add.sprite(150, 300, 'spaceman');
const layer = this.add.layer();
layer.add([ spaceman ]);
Ключевое отличие: спрайт spaceman, добавленный в слой, остаётся прямым дочерним объектом сцены. Слой лишь берёт на себя управление его рендерингом в рамках своей группы. Это сохраняет независимость координат объекта.
Дерево отображения и вывод в консоль
Чтобы увидеть разницу, в конце метода create() сцены Example выводится список детей сцены.
console.log(this.children);
Если открыть консоль браузера, можно наблюдать следующую структуру:
1. Текстовый объект 'Scene 1'.
2. Первый спрайт космонавта (созданный напрямую через this.add.sprite(400, 300, 'spaceman')).
3. Контейнер bob (внутри которого находятся два спрайта-ребёнка).
4. Спрайт spaceman, который был добавлен в слой.
5. Сам объект layer.
Это подтверждает: объект в слое (spaceman) и сам слой (layer) — это два отдельных, независимых ребенка сцены. В контейнере же виден только сам bob, а его содержимое скрыто на уровень ниже.
Когда использовать Container, а когда Layer?
Выбор зависит от задачи.
**Используйте Container, если:**
* Вам нужно перемещать, масштабировать или вращать группу объектов как единое целое.
* Логика требует чёткой родительско-дочерней иерархии (например, части составного объекта).
**Используйте Layer, если:**
* Вам нужно тонко управлять порядком отрисовки (глубиной) группы объектов относительно других элементов.
* Вы хотите применять общие эффекты рендеринга (например, blend mode) к группе, но при этом сохранять независимые координаты и трансформации для каждого объекта в группе.
* Важно, чтобы объекты оставались на верхнем уровне иерархии сцены для прямого доступа.
Что попробовать дальше
Container и Layer в Phaser 3 решают схожие, но структурно разные задачи по группировке. Контейнер создаёт вложенную иерархию, где группа ведёт себя как один составной объект. Слой же выступает в роли менеджера рендеринга для независимых объектов. Для экспериментов попробуйте
- Добавить вращение контейнеру
bobи увидеть, как вращаются оба его спрайта - Изменить глубину (
depth) объектаlayerи проследить, как это повлияет на отрисовку всех его элементов относительно других объектов сцены
