О чем этот пример
В Phaser контейнеры (`Container`) — это мощный инструмент для группировки игровых объектов и управления ими как единым целым. По умолчанию объект может принадлежать только одному контейнеру. Однако, установив флаг `exclusive` в `false`, вы можете добавить один и тот же спрайт или текст в несколько контейнеров одновременно. Эта статья покажет, как это работает и какие нестандартные эффекты анимации можно создать с помощью этого подхода.
Версия 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('lemming', 'assets/sprites/lemming.png');
}
create ()
{
// Here we've got 1 of each game object:
const image = this.add.image(0, 0, 'lemming');
const text = this.add.text(60, 0, 'Oh No!', { font: '16px Courier', fill: '#00ff00' });
const container1 = this.add.container(200, 300).setExclusive(true);
container1.add([ image, text ]);
const container2 = this.add.container(400, 300).setExclusive(false);
container2.add([ image, text ]);
const container3 = this.add.container(600, 300).setExclusive(false);
container3.add([ image, text ]);
this.tweens.add({
targets: [ container1, container2, container3 ],
angle: 360,
duration: 6000,
repeat: -1
});
this.tweens.add({
targets: container2,
scaleX: 2,
scaleY: 2,
duration: 3000,
yoyo: true,
repeat: -1
});
this.tweens.add({
targets: container3,
alpha: 0,
duration: 3000,
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);
В чем суть флага `exclusive`?
При создании контейнера с помощью this.add.container() вы можете вызвать метод .setExclusive(). Этот метод принимает булево значение.
- **setExclusive(true) (значение по умолчанию)**: Объект может находиться только в этом контейнере. Попытка добавить его в другой контейнер удалит его из предыдущего.
- **setExclusive(false)**: Объект может быть добавлен в несколько контейнеров одновременно. Его трансформации (положение, масштаб, поворот, прозрачность) будут комбинироваться из всех контейнеров, в которых он состоит.
В примере создается один спрайт image и один текстовый объект text, которые затем используются повторно.
Разбор кода: создание контейнеров
Давайте посмотрим, как в сцене создаются три контейнера с разными настройками эксклюзивности и анимации.
const image = this.add.image(0, 0, 'lemming');
const text = this.add.text(60, 0, 'Oh No!', { font: '16px Courier', fill: '#00ff00' });
Сначала создаются два независимых игровых объекта. Обратите внимание, их начальные координаты (0,0) и (60,0) заданы относительно будущих контейнеров.
const container1 = this.add.container(200, 300).setExclusive(true);
container1.add([ image, text ]);
const container2 = this.add.container(400, 300).setExclusive(false);
container2.add([ image, text ]);
const container3 = this.add.container(600, 300).setExclusive(false);
container3.add([ image, text ]);
container1 — эксклюзивный. Добавив в него объекты, мы «забираем» их себе. container2 и container3 — неэксклюзивные. Один и тот же спрайт и текст успешно добавляются во все три контейнера. Без setExclusive(false) на втором и третьем контейнере это вызвало бы ошибку или неожиданное поведение.
Наложение анимаций: магия неэксклюзивности
Самое интересное начинается, когда мы применяем анимации к контейнерам, содержащим одни и те же объекты. Tween-анимации в Phaser могут управлять свойствами контейнеров.
this.tweens.add({
targets: [ container1, container2, container3 ],
angle: 360,
duration: 6000,
repeat: -1
});
Эта анимация заставляет все три контейнера непрерывно вращаться. Объекты внутри container1 вращаются только с ним. Объекты внутри container2 и container3 вращаются дважды — по разным осям и с разными центрами (400,300 и 600,300), создавая сложную траекторию.
this.tweens.add({
targets: container2,
scaleX: 2,
scaleY: 2,
duration: 3000,
yoyo: true,
repeat: -1
});
Здесь только container2 пульсирует, изменяя масштаб. Поскольку он неэксклюзивен, объекты получают это масштабирование поверх вращения.
this.tweens.add({
targets: container3,
alpha: 0,
duration: 3000,
yoyo: true,
repeat: -1
});
Аналогично, только container3 управляет прозрачностью своих объектов. Итоговый визуальный эффект — это сумма всех примененных трансформаций.
Практические применения и подводные камни
**Где это может пригодиться?** - **Сложные составные анимации**: когда объект должен одновременно вращаться вокруг одной точки, масштабироваться от другой и менять прозрачность. - **Эффекты наложения**: создание «призрачных» копий или следов без дублирования ресурсов в памяти. - **Интерфейсы**: один элемент UI (например, иконка), который должен появляться в разных местах экрана с разными эффектами.
**На что обратить внимание:**
- Управлять свойствами самого игрового объекта (например, image.x) теперь бессмысленно, так как его конечная позиция на экране вычисляется из всех контейнеров.
- Порядок отрисовки определяется порядком добавления контейнеров в сцену, а не порядком объектов внутри них.
- Производительность: хотя объект в памяти один, его трансформации вычисляются несколько раз — по разу для каждого контейнера.
Что попробовать дальше
Неэксклюзивные контейнеры в Phaser — это нишевый, но чрезвычайно мощный инструмент для создания сложных, составных анимаций без дублирования ресурсов. Они позволяют применять несколько независимых пространственных преобразований к одному объекту.
**Идеи для экспериментов:**
1. Добавьте один спрайт в 4-5 контейнеров, каждый из которых движется по своей круговой траектории с разной скоростью. Получится ли орбитальная система?
2. Используйте неэксклюзивный контейнер для создания эффекта «дрожания» (через быстрые случайные смещения), который можно наложить на любую другую анимацию объекта.
3. Попробуйте менять depth у разных контейнеров с одним объектом. Как это повлияет на отрисовку?
