О чем этот пример
При разработке игр часто возникает необходимость работать с группой объектов как с единым целым: вращать, перемещать или менять их порядок отрисовки. Phaser предоставляет для этого мощный инструмент — `Container`. Эта статья на практическом примере покажет, как создавать контейнеры, добавлять в них спрайты и управлять их отображением, используя свойство `depth`. Вы научитесь группировать объекты и контролировать их визуальную иерархию, что критически важно для сложных сцен и интерфейсов.
Версия 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('logo', 'assets/sprites/phaser3-logo-x2.png');
this.load.image('lemming', 'assets/sprites/lemming.png');
}
create ()
{
this.add.image(400, 300, 'logo');
const image0 = this.add.image(0, 0, 'lemming');
const image1 = this.add.image(-100, -100, 'lemming');
const image2 = this.add.image(100, -100, 'lemming');
const image3 = this.add.image(100, 100, 'lemming');
const image4 = this.add.image(-100, 100, 'lemming');
const container = this.add.container(400, 300, [ image0, image1, image2, image3, image4 ]);
this.tweens.add({
targets: container,
angle: 360,
duration: 6000,
yoyo: true,
repeat: -1
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#010101',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что такое Container и зачем он нужен
Container — это специальный игровой объект в Phaser, который выступает в роли родительского контейнера для других объектов. Все дочерние объекты наследуют трансформации (позицию, вращение, масштаб) своего контейнера. Это позволяет вам, например, вращать или перемещать всю группу объектов одной операцией, а не управлять каждым объектом по отдельности.
Кроме того, контейнеры имеют собственное свойство depth, которое определяет их порядок отрисовки относительно других объектов на сцене. Это ключевой момент для управления тем, какие объекты будут перекрывать другие.
Создание контейнера и добавление спрайтов
В примере мы сначала создаем несколько спрайтов lemming в разных позициях относительно точки (0, 0). Обратите внимание, что их координаты заданы относительно будущего контейнера.
const image0 = this.add.image(0, 0, 'lemming');
const image1 = this.add.image(-100, -100, 'lemming');
const image2 = this.add.image(100, -100, 'lemming');
const image3 = this.add.image(100, 100, 'lemming');
const image4 = this.add.image(-100, 100, 'lemming');
Затем эти спрайты передаются массивом в конструктор контейнера. Второй и третий аргументы (400, 300) — это мировые координаты, куда будет помещена точка (0, 0) самого контейнера.
const container = this.add.container(400, 300, [ image0, image1, image2, image3, image4 ]);
Теперь все спрайты стали дочерними объектами контейнера. Их локальные координаты (например, -100, -100) отсчитываются от позиции контейнера в мире (400, 300).
Анимация контейнера как единого целого
Основное преимущество контейнера демонстрируется в следующем шаге. Мы создаем твин, который анимирует не каждый спрайт по отдельности, а весь контейнер.
this.tweens.add({
targets: container,
angle: 360,
duration: 6000,
yoyo: true,
repeat: -1
});
Здесь targets указывает на объект контейнера, а свойство angle отвечает за вращение. Все дочерние спрайты будут вращаться вместе с контейнером вокруг его точки происхождения (400, 300), сохраняя свои относительные позиции внутри группы. Параметры yoyo: true и repeat: -1 заставляют анимацию играть вперед-назад бесконечно.
Управление глубиной (depth) контейнера
Порядок отрисовки (z-index) в Phaser 3 управляется свойством depth. Объект с большим значением depth рисуется поверх объектов с меньшим значением. В нашем примере есть фоновая картинка logo и контейнер со спрайтами.
По умолчанию объекты рисуются в порядке их создания. Чтобы контейнер (и все его дети) отрисовался позже фона, нужно явно увеличить его depth.
// Создаем фон первым (он получит depth = 0)
this.add.image(400, 300, 'logo');
// ... создаем контейнер и спрайты
// Поднимаем контейнер и всех его детей на передний план
container.setDepth(10);
Важно: установка depth для контейнера влияет на всю группу. Установка depth для отдельного дочернего спрайта будет работать, но может привести к неожиданным результатам, если другие дети контейнера имеют другое значение. Для сложного управления глубиной внутри группы лучше использовать вложенные контейнеры.
Практическое применение и паттерны
Контейнеры идеально подходят для:
1. **Группировки элементов интерфейса (HUD)**: панель здоровья, состоящая из иконки, полоски и текста, может быть единым контейнером, который легко показывать/скрывать или анимировать.
2. **Создания сложных игровых объектов**: танк (корпус + башня + гусеницы) или персонаж с экипировкой.
3. **Управления слоями сцены**: разделение фона, игрового слоя и эффектов на разные контейнеры с разным depth.
// Пример: создание сложного объекта
const body = this.add.sprite(0, 0, 'tankBody');
const turret = this.add.sprite(30, -10, 'tankTurret');
const playerTank = this.add.container(x, y, [ body, turret ]);
// Вращаем всю сборку
playerTank.setAngle(45);
// Вращаем только башню относительно корпуса
turret.setAngle(-15);
Что попробовать дальше
Контейнеры в Phaser — это фундаментальный инструмент для организации кода и визуальной структуры игры. Они позволяют управлять группами объектов эффективно и предсказуемо. Для экспериментов попробуйте
- Изменять
depthконтейнера и дочерних объектов, наблюдая за порядком отрисовки - Создавать иерархию из вложенных контейнеров
- Анимировать не только
angle, но иscaleилиalphaконтейнера, чтобы увидеть, как эффект применяется ко всем детям
