О чем этот пример
Контейнеры (`Container`) в Phaser — это мощный инструмент для группировки игровых объектов. Они позволяют обрабатывать множество элементов как единое целое: применять трансформации, управлять видимостью или позицией всей группы сразу. Эта статья на практическом примере покажет, как создавать вложенные контейнеры и правильно размещать в них графику, чтобы избежать частой ошибки — неожиданного смещения координат. Вы научитесь строить предсказуемые иерархии объектов, что критически важно для сложных интерфейсов или составных игровых сущностей.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
create ()
{
const container1 = this.add.container(400, 300);
// Because the rect is drawn at 0x0 it will start from the top-left of Container1
// If you want bg1 to be _centered_ on Container1 then its xy should be
// negative half width by negative half height (i.e. -150, -150 in this case)
const bg1 = this.add.graphics().fillStyle(0xff0000).fillRect(0, 0, 300, 300);
container1.add(bg1);
// This Container is positioned _relative_ to Container1 (at 400x300)
// Which is why we use 0x0 here - if you put a different value, see how they adjust
const container2 = this.add.container(0, 0);
// Because the rect is drawn at 0x0 it will start from the top-left of Container2
const bg2 = this.add.graphics().fillStyle(0x00ff00).fillRect(0, 0, 200, 200);
container2.add(bg2);
container1.add(container2);
const container3 = this.add.container(0, 0);
// Because the rect is drawn at 0x0 it will start from the top-left of Container3
const bg3 = this.add.graphics().fillStyle(0x0000ff).fillRect(0, 0, 100, 100);
container3.add(bg3);
container1.add(container3);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Создание корневого контейнера
Вся иерархия начинается с корневого контейнера. Он позиционируется относительно сцены с помощью координат `xиy, переданных в методеthis.add.container()`.
const container1 = this.add.container(400, 300);
Здесь container1 будет размещён в центре сцены размером 800x600. Важно понимать, что точка (0, 0) этого контейнера теперь находится на мировых координатах (400, 300).
Добавление графики в контейнер
Внутрь контейнера можно помещать любой игровой объект, например, графический примитив. Создадим красный квадрат.
const bg1 = this.add.graphics().fillStyle(0xff0000).fillRect(0, 0, 300, 300);
container1.add(bg1);
Ключевой момент: метод fillRect(0, 0, 300, 300) рисует квадрат, начиная от точки (0, 0) самой графики bg1. Эта точка (0, 0) объекта bg1 привязывается к точке (0, 0) его родителя — контейнера container1. Поэтому левый верхний угол красного квадрата окажется в мировых координатах (400, 300). Если нужно, чтобы квадрат был отцентрирован относительно контейнера, его следует рисовать со смещением: fillRect(-150, -150, 300, 300).
Вложенные контейнеры и относительные координаты
Контейнеры могут содержать другие контейнеры, образуя древо объектов. Позиция дочернего контейнера задаётся относительно родительского.
const container2 = this.add.container(0, 0);
const bg2 = this.add.graphics().fillStyle(0x00ff00).fillRect(0, 0, 200, 200);
container2.add(bg2);
container1.add(container2);
Создаётся container2 с координатами (0, 0). Это значит, что его начало будет в точке (0, 0) родителя, то есть в мировых координатах (400, 300). Зелёный квадрат, добавленный в container2, нарисуется от этой точки. Таким образом, изменение позиции container1 автоматически сдвинет всю его внутреннюю иерархию, включая container2 и bg2.
Построение многоуровневой иерархии
Добавим третий, синий контейнер, также как дочерний к корневому container1. Это демонстрирует, как несколько независимых ветвей объектов могут быть сгруппированы под одним родителем.
const container3 = this.add.container(0, 0);
const bg3 = this.add.graphics().fillStyle(0x0000ff).fillRect(0, 0, 100, 100);
container3.add(bg3);
container1.add(container3);
И container2, и container3 являются дочерними для container1. Их координаты (0, 0) означают, что они будут нарисованы в одной и той же точке — начале container1. На экране мы увидим, что красный, зелёный и синий квадраты наложатся друг на друга своими верхними левыми углами в точке (400, 300).
Конфигурация игры и запуск сцены
Весь пример выполняется внутри одной сцены. Базовая конфигурация игры определяет её размеры, цвет фона и класс сцены.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
let game = new Phaser.Game(config);
Эта конфигурация создаёт игровое поле 800x600 пикселей с чёрным фоном. Экземпляр сцены Example будет автоматически создан и запущен движком Phaser.
Что попробовать дальше
Вложенные контейнеры — это основа для организации сложных игровых миров и интерфейсов в Phaser. Главный принцип: координаты дочернего объекта всегда относительны к локальному началу координат его непосредственного родителя. Для экспериментов попробуйте
- Изменить координаты
container2на(50, 50)и увидите, как зелёный квадрат сместится относительно красного - Сделать
container3дочерним дляcontainer2, чтобы построить трёхуровневую иерархию - Применить к
container1методsetRotationи наблюдать, как вся группа объектов повернётся как единое целое вокруг точки(400, 300)
