Groups and Containers
Logical grouping (Group), visual grouping with transform inheritance (Container), render-layer grouping (Layer), object pooling, and when to use each in Phaser 4.
Key source paths: src/gameobjects/group/, src/gameobjects/container/, src/gameobjects/layer/
Related skills: ../sprites-and-images/SKILL.md, ../physics-arcade/SKILL.md
Quick Start
// In a Scene's create() method:
// --- Group: logical collection, no transform, great for pooling ---
const enemies = this.add.group();
enemies.create(100, 200, 'enemy'); // creates Sprite at (100,200)
enemies.create(300, 200, 'enemy');
// --- Container: visual parent with inherited transform ---
const hud = this.add.container(10, 10);
const icon = this.add.image(0, 0, 'heart');
const label = this.add.text(20, 0, 'x3');
hud.add([icon, label]); // children move/scale/rotate with hud
// --- Layer: render-ordering bucket, no position/scale ---
const bgLayer = this.add.layer();
const fgLayer = this.add.layer();
bgLayer.add(this.add.image(400, 300, 'sky'));
fgLayer.add(this.add.sprite(400, 300, 'player'));
Core Concepts
Group vs Container vs Layer
| Feature |
Group |
Container |
Layer |
| Purpose |
Logical collection / pool |
Visual parent with transform |
Render-order bucket |
| On display list |
No (children are) |
Yes (renders children) |
Yes (renders children) |
| Position/rotation/scale |
No |
Yes (children inherit) |
No |
| Children storage |
children (Set) |
list (Array) |
List (Structs.List) |
| Physics |
Via physics.add.group() |
Limited (offsets if not at 0,0) |
No |
| Input |
No (children can) |
Yes (needs hit area shape) |
No |
| Object pooling |
Yes (getFirstDead, kill) |
No |
No |
| Masks |
No |
Yes (not per-child in Canvas) |
Yes |
| Alpha/blend/visible |
No (batch via setVisible) |
Yes |
Yes |
| Nesting |
N/A |
Container in Container |
Cannot go in Container |
| Extends |
EventEmitter |
GameObject |
List |
| Factory |
this.add.group() |
this.add.container(x, y) |
this.add.layer() |
When to Use Each
Group: Managing collections of similar objects (enemies, bullets, coins), object pooling with active/inactive lifecycle, physics group collisions. No shared visual transform. Members can belong to multiple Groups simultaneously.
Container: Children inherit position, rotation, scale, alpha. Composite UI elements (health bars, inventory slots), moving/rotating clusters as one unit, nested transforms. By default exclusive -- a child can only belong to one Container (use setExclusive(false) to override).
Layer: Controlling render order of object batches, applying shared alpha/blend/mask. No position/scale/rotation. Lightweight render bucketing.
Container vs Group at a Glance
- Container has position, rotation, scale, alpha -- Group does not. If you need children to move/rotate as a unit, use Container.
- Container is exclusive by default -- adding a child removes it from its previous Container. Group is non-exclusive; a game object can be in many Groups.
- Container is on the display list -- it renders its children. Group is not on the display list; its children render individually on the Scene.
- Group supports object pooling -- getFirstDead, kill, killAndHide. Container does not.
- Container has performance cost -- each child requires matrix math per frame. Deeper nesting = more cost. Prefer Group or Layer when transforms are not needed.
Common Patterns
Creating and Populating Groups
// Empty group, add existing objects
const gems = this.add.group();
gems.add(existingSprite);
gems.addMultiple([sprite1, sprite2, sprite3]);
// Group with config -- creates children automatically
const coins = this.add.group({
classType: Phaser.GameObjects.Sprite,
key: 'coin',
quantity: 10, // overrides frameQuantity
setXY: { x: 50, y: 300, stepX: 60 },
setScale: { x: 0.5, y: 0.5 }
});
// Custom class type with pool limit
const bullets = this.add.group({
classType: Bullet, // must accept (scene, x, y, key, frame)
maxSize: 30,
defaultKey: 'bullet',
runChildUpdate: true // calls child.update() each frame
});
Object Pooling with getFirstDead
The core pooling pattern: deactivate objects instead of destroying them, then reuse inactive ones.
// Setup pool
const bullets = this.add.group({
classType: Phaser.GameObjects.Sprite,
defaultKey: 'bullet',
maxSize: 30
});
// Fire a bullet -- get() finds first inactive member or creates one
function fireBullet(x, y) {
const bullet = bullets.get(x, y);
if (bullet) {
bullet.setActive(true);
bullet.setVisible(true);
bullet.body.velocity.y = -300; // if physics enabled
}
}
// Deactivate when off-screen or on hit
function killBullet(bullet) {
bullets.killAndHide(bullet); // sets active=false, visible=false
// If using physics, also reset the body:
// bullet.body.stop();
}
// Alternative: manual getFirst
const inactive = bullets.getFirst(false); // first where active===false
const active = bullets.getFirstAlive(); // first where active===true
const dead = bullets.getFirstDead(true, x, y); // first inactive, create if null
Pool helper methods on Group:
| Method |
Description |
get(x, y, key, frame) |
Shortcut: getFirst(false, true, ...) -- finds inactive or creates |
getFirst(state, createIfNull, x, y, key, frame) |
First member matching active state |
getFirstAlive(createIfNull, x, y, key, frame) |
First member where active===true |
getFirstDead(createIfNull, x, y, key, frame) |
First member where active===false |
getLast(state, createIfNull, x, y, key, frame) |
Like getFirst but searches back-to-front |
kill(gameObject) |
Sets active=false on a member |
killAndHide(gameObject) |
Sets active=false and visible=false |
countActive(value) |
Count members where active===value (default true) |
getTotalUsed() |
Count of active members |
getTotalFree() |
maxSize - active count (remaining pool capacity) |
isFull() |
True if children.size >= maxSize |
Physics Groups
Physics groups extend Group with automatic body assignment. See ../physics-arcade/SKILL.md for full details.
// Arcade Physics group -- every member gets a dynamic body
const enemies = this.physics.add.group({
key: 'enemy',
quantity: 5,
setXY: { x: 100, y: 100, stepX: 80 }
});
// Static physics group -- immovable bodies
const platforms = this.physics.add.staticGroup();
platforms.create(400, 568, 'ground');
// Collide physics groups with each other
this.physics.add.collider(player, platforms);
this.physics.add.overlap(bullets, enemies, onHit);
Containers with Nested Transforms
// HUD that follows camera
const hud = this.add.container(10, 10);
hud.setScrollFactor(0); // pinned to camera
const healthBar = this.add.rectangle(0, 0, 200, 20, 0x00ff00);
const healthText = this.add.text(210, -5, '100 HP');
hud.add([healthBar, healthText]);
// Move everything at once
hud.setPosition(50, 50);
// Scale and rotate propagate to children
hud.setScale(1.5);
hud.setRotation(0.1);
// Alpha affects all children
hud.setAlpha(0.8);
// Nested containers
const inventory = this.add.container(300, 500);
for (let i = 0; i < 5; i++) {
const slot = this.add.container(i * 55, 0);
slot.add([
this.add.rectangle(0, 0, 48, 48, 0x333333),
this.add.image(0, 0, `item-${i}`)
]);
inventory.add(slot); // Container inside Container
}
Key Container methods:
| Method |
Description |
add(child) / addAt(child, index) |
Add Game Object(s); removes from display list |
remove(child, destroyChild) |
Remove; optionally destroy |
getAt(index) / getIndex(child) |
Access by index |
getByName(name) / getFirst(prop, val) |
Query children |
getAll(prop, val) / count(prop, val) |
Filtered access and counting |
sort(property) / swap(a, b) / moveTo(child, idx) |
Ordering |
each(cb, ctx) / iterate(cb, ctx) |
Iteration (iterate passes index) |
setScrollFactor(x, y, updateChildren) |
Pass true to also apply to children |
getBounds(output) |
Bounding rect of all children |
pointToContainer(source, output) |
World point to local space |
setExclusive(value) |
When false, children can exist in multiple places |
replace(oldChild, newChild) |
Swap one child for another |
setSize(width, height) |
Set hit area size (required for input) |
length |
Read-only child count |
Layers for Render Ordering
const bgLayer = this.add.layer();
const entityLayer = this.add.layer();
const uiLayer = this.add.layer();
// Add objects -- they render in layer order, then by depth within layer
bgLayer.add(this.add.image(400, 300, 'sky'));
entityLayer.add(player);
entityLayer.add(enemy);
uiLayer.add(scoreText);
// Control depth of layers themselves
bgLayer.setDepth(0);
entityLayer.setDepth(1);
uiLayer.setDepth(2);
// Children set depth within their layer
enemy.setDepth(5); // relative to entityLayer, not the Scene
// Apply shared effects to entire layer
entityLayer.setAlpha(0.5);
entityLayer.setVisible(false);
entityLayer.setBlendMode(Phaser.BlendModes.ADD);
Bulk Creation with createMultiple
// createMultiple accepts a GroupCreateConfig or array of them
const coins = this.add.group();
coins.createMultiple({
key: 'coin',
quantity: 20,
setXY: { x: 50, y: 100, stepX: 40, stepY: 0 },
setScale: { x: 0.5, y: 0.5 },
setRotation: { value: 0, step: 0.1 }, // each rotated 0.1 more than previous
setAlpha: { value: 1 },
setOrigin: { x: 0.5, y: 0.5 },
setDepth: { value: 5 },
gridAlign: {
width: 5,
height: 4,
cellWidth: 48,
cellHeight: 48,
x: 100,
y: 200
}
});
// Multiple configs at once (creates two different sets)
coins.createMultiple([
{ key: 'gold-coin', quantity: 10, setXY: { x: 50, y: 100, stepX: 30 } },
{ key: 'silver-coin', quantity: 10, setXY: { x: 50, y: 200, stepX: 30 } }
]);
Iterating and Batch Operations on Groups
const enemies = this.add.group();
// Get all children as array
const all = enemies.getChildren();
const active = enemies.getMatching('active', true);
// Batch property operations
enemies.setXY(200, 300);
enemies.incX(5); // add 5 to each member's x
enemies.setVisible(false);
enemies.propertyValueSet('tintTopLeft', 0xff0000);
enemies.playAnimation('walk');
// Stepping: apply incremental values across members
enemies.setX(100, 50); // first at x=100, next at 150, then 200...
enemies.setY(200, 30); // first at y=200, next at 230, then 260...
enemies.setXY(100, 200, 50, 30); // combined X and Y stepping
enemies.incXY(10, 5); // add 10 to each x, 5 to each y
enemies.angle(0, 15); // first at 0 deg, next at 15, then 30...
enemies.setAlpha(1, -0.1); // first at 1.0, next at 0.9, then 0.8...
enemies.setScale(1, 0, 0.1, 0); // scaleX: 1.0, 1.1, 1.2... (stepX=0.1)
enemies.setDepth(0, 1); // depth 0, 1, 2, 3...
enemies.setOrigin(0.5);
enemies.setBlendMode(Phaser.BlendModes.ADD);
enemies.setTint(0xff0000);
enemies.shuffle(); // randomize order in the group
API Quick Reference
Group (Phaser.GameObjects.Group)
Factory: this.add.group(children?, config?)
Config types:
GroupConfig -- classType, name, active, maxSize, defaultKey, defaultFrame,
runChildUpdate, createCallback, removeCallback
GroupCreateConfig -- key (required), classType, frame, quantity, visible, active,
repeat, yoyo, frameQuantity, max, setXY, setRotation,
setScale, setOrigin, setAlpha, setDepth, setScrollFactor,
hitArea, gridAlign
Key members: children (Set), classType, maxSize, defaultKey, defaultFrame,
active, runChildUpdate
Lifecycle: create, createMultiple, add, addMultiple, remove, clear, destroy
Queries: getFirst, getFirstAlive, getFirstDead, getLast, get, getChildren,
getLength, getMatching, contains, countActive, getTotalUsed,
getTotalFree, isFull
Pool: get, getFirstDead, kill, killAndHide
Bulk ops: setX/Y/XY, incX/Y/XY, setAlpha, setVisible, toggleVisible,
playAnimation, propertyValueSet, propertyValueInc, setOrigin,
setDepth, shuffle, setBlendMode, setTint
Container (Phaser.GameObjects.Container)
Factory: this.add.container(x?, y?, children?)
Extends: GameObject
Mixins: AlphaSingle, BlendMode, ComputedSize, Depth, Mask, Transform, Visible
Key members: list (Array), exclusive, maxSize, scrollFactorX/Y
Children: add, addAt, remove, removeAt, removeBetween, removeAll
Queries: getAt, getIndex, getByName, getFirst, getAll, getRandom, count
Ordering: sort, swap, moveTo, moveUp, moveDown, sendToBack, bringToTop,
moveAbove, moveBelow, reverse
Transform: pointToContainer, getBounds, getBoundsTransformMatrix
Iteration: each(cb, ctx), iterate(cb, ctx, ...args)
Config: setExclusive, setScrollFactor(x, y, updateChildren)
Property: length (read-only child count)
Layer (Phaser.GameObjects.Layer)
Factory: this.add.layer(children?)
Extends: Phaser.Structs.List
Mixins: AlphaSingle, BlendMode, Depth, Filters, Mask, RenderSteps, Visible
Key members: scene, displayList, sortChildrenFlag
Children: add, remove (inherited from List)
Settings: setAlpha, setBlendMode, setDepth, setVisible, setMask, setName,
setActive, setState, setData, getData
No position, rotation, scale, scroll factor, input, or physics.
Cannot be added to a Container. Containers can be added to Layers.
Gotchas
Group is NOT on the display list. Its children appear on the Scene display list individually. Moving a Group does nothing visually -- use Container for that.
Container has performance overhead. Every child requires extra matrix math per frame. Deep nesting multiplies this. Avoid Containers when a Group or Layer suffices.
Container origin is always 0,0. The transform point cannot be changed. Position children relative to (0,0).
Container children lose Scene-level depth control. A child's depth only orders within the Container. The Container's own depth positions it in the Scene.
Physics + Container is problematic. If a Container is not at (0,0), physics bodies on children will be offset. Avoid physics bodies on Container children.
Container children cannot be individually masked in Canvas rendering. Only the Container itself can have a mask. Masks do not stack for nested Containers. Masks do stack in WebGL rendering.
Group.get() vs Group.getFirst() differ. get(x, y) is shorthand for getFirst(false, true, x, y) -- finds first inactive member and creates if none found. getFirst(state) defaults to active===false without auto-creating.
Layer cannot go inside a Container. Containers can be added to Layers, but not the reverse.
Group children Set is unordered. No index-based access. Use getChildren() to get an array snapshot.
killAndHide does not remove from the group. It only sets active=false and visible=false. The object stays in the group for reuse.
Container.setScrollFactor does not auto-propagate. Pass true as the third argument to also update children: container.setScrollFactor(0, 0, true).
Group.create() adds to the Scene display list. But group.add() does NOT unless you pass true as the second argument.
Container needs setSize() for input. Containers have no implicit size. You must call container.setSize(width, height) before setInteractive() will work with a hit area.
Source File Map
| File |
Description |
src/gameobjects/group/Group.js |
Group class -- pooling, create, getFirst*, kill, batch ops |
src/gameobjects/group/GroupFactory.js |
this.add.group() factory registration |
src/gameobjects/group/typedefs/GroupConfig.js |
GroupConfig typedef (classType, maxSize, callbacks) |
src/gameobjects/group/typedefs/GroupCreateConfig.js |
GroupCreateConfig typedef (key, quantity, setXY, etc.) |
src/gameobjects/container/Container.js |
Container class -- list management, nested transforms |
src/gameobjects/container/ContainerFactory.js |
this.add.container() factory registration |
src/gameobjects/container/ContainerRender.js |
Container WebGL/Canvas render functions |
src/gameobjects/layer/Layer.js |
Layer class -- display list bucket with alpha/blend/mask |
src/gameobjects/layer/LayerFactory.js |
this.add.layer() factory registration |
src/gameobjects/layer/LayerRender.js |
Layer WebGL/Canvas render functions |
src/physics/arcade/ArcadePhysics.js |
this.physics.add.group() / staticGroup() |
1---2name: groups-and-containers3description: Use this skill when using Groups or Containers in Phaser 4. Covers organizing game objects, object pooling, batch operations, and nested transforms with Containers. Triggers on: Group, Container, object pool, getFirstDead, children.4---56# Groups and Containers7> Logical grouping (Group), visual grouping with transform inheritance (Container), render-layer grouping (Layer), object pooling, and when to use each in Phaser 4.89**Key source paths:** `src/gameobjects/group/`, `src/gameobjects/container/`, `src/gameobjects/layer/`10**Related skills:** ../sprites-and-images/SKILL.md, ../physics-arcade/SKILL.md1112## Quick Start1314```js15// In a Scene's create() method:1617// --- Group: logical collection, no transform, great for pooling ---18const enemies = this.add.group();19enemies.create(100, 200, 'enemy'); // creates Sprite at (100,200)20enemies.create(300, 200, 'enemy');2122// --- Container: visual parent with inherited transform ---23const hud = this.add.container(10, 10);24const icon = this.add.image(0, 0, 'heart');25const label = this.add.text(20, 0, 'x3');26hud.add([icon, label]); // children move/scale/rotate with hud2728// --- Layer: render-ordering bucket, no position/scale ---29const bgLayer = this.add.layer();30const fgLayer = this.add.layer();31bgLayer.add(this.add.image(400, 300, 'sky'));32fgLayer.add(this.add.sprite(400, 300, 'player'));33```3435## Core Concepts3637### Group vs Container vs Layer3839| Feature | Group | Container | Layer |40|---|---|---|---|41| **Purpose** | Logical collection / pool | Visual parent with transform | Render-order bucket |42| **On display list** | No (children are) | Yes (renders children) | Yes (renders children) |43| **Position/rotation/scale** | No | Yes (children inherit) | No |44| **Children storage** | `children` (Set) | `list` (Array) | List (Structs.List) |45| **Physics** | Via physics.add.group() | Limited (offsets if not at 0,0) | No |46| **Input** | No (children can) | Yes (needs hit area shape) | No |47| **Object pooling** | Yes (getFirstDead, kill) | No | No |48| **Masks** | No | Yes (not per-child in Canvas) | Yes |49| **Alpha/blend/visible** | No (batch via setVisible) | Yes | Yes |50| **Nesting** | N/A | Container in Container | Cannot go in Container |51| **Extends** | EventEmitter | GameObject | List |52| **Factory** | `this.add.group()` | `this.add.container(x, y)` | `this.add.layer()` |5354### When to Use Each5556**Group:** Managing collections of similar objects (enemies, bullets, coins), object pooling with active/inactive lifecycle, physics group collisions. No shared visual transform. Members can belong to multiple Groups simultaneously.5758**Container:** Children inherit position, rotation, scale, alpha. Composite UI elements (health bars, inventory slots), moving/rotating clusters as one unit, nested transforms. By default exclusive -- a child can only belong to one Container (use `setExclusive(false)` to override).5960**Layer:** Controlling render order of object batches, applying shared alpha/blend/mask. No position/scale/rotation. Lightweight render bucketing.6162### Container vs Group at a Glance6364- **Container has position, rotation, scale, alpha** -- Group does not. If you need children to move/rotate as a unit, use Container.65- **Container is exclusive by default** -- adding a child removes it from its previous Container. Group is non-exclusive; a game object can be in many Groups.66- **Container is on the display list** -- it renders its children. Group is not on the display list; its children render individually on the Scene.67- **Group supports object pooling** -- getFirstDead, kill, killAndHide. Container does not.68- **Container has performance cost** -- each child requires matrix math per frame. Deeper nesting = more cost. Prefer Group or Layer when transforms are not needed.6970## Common Patterns7172### Creating and Populating Groups7374```js75// Empty group, add existing objects76const gems = this.add.group();77gems.add(existingSprite);78gems.addMultiple([sprite1, sprite2, sprite3]);7980// Group with config -- creates children automatically81const coins = this.add.group({82 classType: Phaser.GameObjects.Sprite,83 key: 'coin',84 quantity: 10, // overrides frameQuantity85 setXY: { x: 50, y: 300, stepX: 60 },86 setScale: { x: 0.5, y: 0.5 }87});8889// Custom class type with pool limit90const bullets = this.add.group({91 classType: Bullet, // must accept (scene, x, y, key, frame)92 maxSize: 30,93 defaultKey: 'bullet',94 runChildUpdate: true // calls child.update() each frame95});96```9798### Object Pooling with getFirstDead99100The core pooling pattern: deactivate objects instead of destroying them, then reuse inactive ones.101102```js103// Setup pool104const bullets = this.add.group({105 classType: Phaser.GameObjects.Sprite,106 defaultKey: 'bullet',107 maxSize: 30108});109110// Fire a bullet -- get() finds first inactive member or creates one111function fireBullet(x, y) {112 const bullet = bullets.get(x, y);113114 if (bullet) {115 bullet.setActive(true);116 bullet.setVisible(true);117 bullet.body.velocity.y = -300; // if physics enabled118 }119}120121// Deactivate when off-screen or on hit122function killBullet(bullet) {123 bullets.killAndHide(bullet); // sets active=false, visible=false124 // If using physics, also reset the body:125 // bullet.body.stop();126}127128// Alternative: manual getFirst129const inactive = bullets.getFirst(false); // first where active===false130const active = bullets.getFirstAlive(); // first where active===true131const dead = bullets.getFirstDead(true, x, y); // first inactive, create if null132```133134**Pool helper methods on Group:**135136| Method | Description |137|---|---|138| `get(x, y, key, frame)` | Shortcut: `getFirst(false, true, ...)` -- finds inactive or creates |139| `getFirst(state, createIfNull, x, y, key, frame)` | First member matching active `state` |140| `getFirstAlive(createIfNull, x, y, key, frame)` | First member where `active===true` |141| `getFirstDead(createIfNull, x, y, key, frame)` | First member where `active===false` |142| `getLast(state, createIfNull, x, y, key, frame)` | Like getFirst but searches back-to-front |143| `kill(gameObject)` | Sets `active=false` on a member |144| `killAndHide(gameObject)` | Sets `active=false` and `visible=false` |145| `countActive(value)` | Count members where `active===value` (default true) |146| `getTotalUsed()` | Count of active members |147| `getTotalFree()` | `maxSize - active count` (remaining pool capacity) |148| `isFull()` | True if `children.size >= maxSize` |149150### Physics Groups151152Physics groups extend Group with automatic body assignment. See ../physics-arcade/SKILL.md for full details.153154```js155// Arcade Physics group -- every member gets a dynamic body156const enemies = this.physics.add.group({157 key: 'enemy',158 quantity: 5,159 setXY: { x: 100, y: 100, stepX: 80 }160});161162// Static physics group -- immovable bodies163const platforms = this.physics.add.staticGroup();164platforms.create(400, 568, 'ground');165166// Collide physics groups with each other167this.physics.add.collider(player, platforms);168this.physics.add.overlap(bullets, enemies, onHit);169```170171### Containers with Nested Transforms172173```js174// HUD that follows camera175const hud = this.add.container(10, 10);176hud.setScrollFactor(0); // pinned to camera177178const healthBar = this.add.rectangle(0, 0, 200, 20, 0x00ff00);179const healthText = this.add.text(210, -5, '100 HP');180hud.add([healthBar, healthText]);181182// Move everything at once183hud.setPosition(50, 50);184185// Scale and rotate propagate to children186hud.setScale(1.5);187hud.setRotation(0.1);188189// Alpha affects all children190hud.setAlpha(0.8);191192// Nested containers193const inventory = this.add.container(300, 500);194for (let i = 0; i < 5; i++) {195 const slot = this.add.container(i * 55, 0);196 slot.add([197 this.add.rectangle(0, 0, 48, 48, 0x333333),198 this.add.image(0, 0, `item-${i}`)199 ]);200 inventory.add(slot); // Container inside Container201}202```203204**Key Container methods:**205206| Method | Description |207|---|---|208| `add(child)` / `addAt(child, index)` | Add Game Object(s); removes from display list |209| `remove(child, destroyChild)` | Remove; optionally destroy |210| `getAt(index)` / `getIndex(child)` | Access by index |211| `getByName(name)` / `getFirst(prop, val)` | Query children |212| `getAll(prop, val)` / `count(prop, val)` | Filtered access and counting |213| `sort(property)` / `swap(a, b)` / `moveTo(child, idx)` | Ordering |214| `each(cb, ctx)` / `iterate(cb, ctx)` | Iteration (iterate passes index) |215| `setScrollFactor(x, y, updateChildren)` | Pass true to also apply to children |216| `getBounds(output)` | Bounding rect of all children |217| `pointToContainer(source, output)` | World point to local space |218| `setExclusive(value)` | When false, children can exist in multiple places |219| `replace(oldChild, newChild)` | Swap one child for another |220| `setSize(width, height)` | Set hit area size (required for input) |221| `length` | Read-only child count |222223### Layers for Render Ordering224225```js226const bgLayer = this.add.layer();227const entityLayer = this.add.layer();228const uiLayer = this.add.layer();229230// Add objects -- they render in layer order, then by depth within layer231bgLayer.add(this.add.image(400, 300, 'sky'));232entityLayer.add(player);233entityLayer.add(enemy);234uiLayer.add(scoreText);235236// Control depth of layers themselves237bgLayer.setDepth(0);238entityLayer.setDepth(1);239uiLayer.setDepth(2);240241// Children set depth within their layer242enemy.setDepth(5); // relative to entityLayer, not the Scene243244// Apply shared effects to entire layer245entityLayer.setAlpha(0.5);246entityLayer.setVisible(false);247entityLayer.setBlendMode(Phaser.BlendModes.ADD);248```249250### Bulk Creation with createMultiple251252```js253// createMultiple accepts a GroupCreateConfig or array of them254const coins = this.add.group();255256coins.createMultiple({257 key: 'coin',258 quantity: 20,259 setXY: { x: 50, y: 100, stepX: 40, stepY: 0 },260 setScale: { x: 0.5, y: 0.5 },261 setRotation: { value: 0, step: 0.1 }, // each rotated 0.1 more than previous262 setAlpha: { value: 1 },263 setOrigin: { x: 0.5, y: 0.5 },264 setDepth: { value: 5 },265 gridAlign: {266 width: 5,267 height: 4,268 cellWidth: 48,269 cellHeight: 48,270 x: 100,271 y: 200272 }273});274275// Multiple configs at once (creates two different sets)276coins.createMultiple([277 { key: 'gold-coin', quantity: 10, setXY: { x: 50, y: 100, stepX: 30 } },278 { key: 'silver-coin', quantity: 10, setXY: { x: 50, y: 200, stepX: 30 } }279]);280```281282### Iterating and Batch Operations on Groups283284```js285const enemies = this.add.group();286287// Get all children as array288const all = enemies.getChildren();289const active = enemies.getMatching('active', true);290291// Batch property operations292enemies.setXY(200, 300);293enemies.incX(5); // add 5 to each member's x294enemies.setVisible(false);295enemies.propertyValueSet('tintTopLeft', 0xff0000);296enemies.playAnimation('walk');297298// Stepping: apply incremental values across members299enemies.setX(100, 50); // first at x=100, next at 150, then 200...300enemies.setY(200, 30); // first at y=200, next at 230, then 260...301enemies.setXY(100, 200, 50, 30); // combined X and Y stepping302enemies.incXY(10, 5); // add 10 to each x, 5 to each y303enemies.angle(0, 15); // first at 0 deg, next at 15, then 30...304enemies.setAlpha(1, -0.1); // first at 1.0, next at 0.9, then 0.8...305enemies.setScale(1, 0, 0.1, 0); // scaleX: 1.0, 1.1, 1.2... (stepX=0.1)306enemies.setDepth(0, 1); // depth 0, 1, 2, 3...307enemies.setOrigin(0.5);308enemies.setBlendMode(Phaser.BlendModes.ADD);309enemies.setTint(0xff0000);310enemies.shuffle(); // randomize order in the group311```312313## API Quick Reference314315### Group (Phaser.GameObjects.Group)316317```318Factory: this.add.group(children?, config?)319320Config types:321 GroupConfig -- classType, name, active, maxSize, defaultKey, defaultFrame,322 runChildUpdate, createCallback, removeCallback323 GroupCreateConfig -- key (required), classType, frame, quantity, visible, active,324 repeat, yoyo, frameQuantity, max, setXY, setRotation,325 setScale, setOrigin, setAlpha, setDepth, setScrollFactor,326 hitArea, gridAlign327328Key members: children (Set), classType, maxSize, defaultKey, defaultFrame,329 active, runChildUpdate330Lifecycle: create, createMultiple, add, addMultiple, remove, clear, destroy331Queries: getFirst, getFirstAlive, getFirstDead, getLast, get, getChildren,332 getLength, getMatching, contains, countActive, getTotalUsed,333 getTotalFree, isFull334Pool: get, getFirstDead, kill, killAndHide335Bulk ops: setX/Y/XY, incX/Y/XY, setAlpha, setVisible, toggleVisible,336 playAnimation, propertyValueSet, propertyValueInc, setOrigin,337 setDepth, shuffle, setBlendMode, setTint338```339340### Container (Phaser.GameObjects.Container)341342```343Factory: this.add.container(x?, y?, children?)344345Extends: GameObject346Mixins: AlphaSingle, BlendMode, ComputedSize, Depth, Mask, Transform, Visible347348Key members: list (Array), exclusive, maxSize, scrollFactorX/Y349Children: add, addAt, remove, removeAt, removeBetween, removeAll350Queries: getAt, getIndex, getByName, getFirst, getAll, getRandom, count351Ordering: sort, swap, moveTo, moveUp, moveDown, sendToBack, bringToTop,352 moveAbove, moveBelow, reverse353Transform: pointToContainer, getBounds, getBoundsTransformMatrix354Iteration: each(cb, ctx), iterate(cb, ctx, ...args)355Config: setExclusive, setScrollFactor(x, y, updateChildren)356Property: length (read-only child count)357```358359### Layer (Phaser.GameObjects.Layer)360361```362Factory: this.add.layer(children?)363364Extends: Phaser.Structs.List365Mixins: AlphaSingle, BlendMode, Depth, Filters, Mask, RenderSteps, Visible366367Key members: scene, displayList, sortChildrenFlag368Children: add, remove (inherited from List)369Settings: setAlpha, setBlendMode, setDepth, setVisible, setMask, setName,370 setActive, setState, setData, getData371372No position, rotation, scale, scroll factor, input, or physics.373Cannot be added to a Container. Containers can be added to Layers.374```375376## Gotchas3773781. **Group is NOT on the display list.** Its children appear on the Scene display list individually. Moving a Group does nothing visually -- use Container for that.3793802. **Container has performance overhead.** Every child requires extra matrix math per frame. Deep nesting multiplies this. Avoid Containers when a Group or Layer suffices.3813823. **Container origin is always 0,0.** The transform point cannot be changed. Position children relative to (0,0).3833844. **Container children lose Scene-level depth control.** A child's `depth` only orders within the Container. The Container's own depth positions it in the Scene.3853865. **Physics + Container is problematic.** If a Container is not at (0,0), physics bodies on children will be offset. Avoid physics bodies on Container children.3873886. **Container children cannot be individually masked in Canvas rendering.** Only the Container itself can have a mask. Masks do not stack for nested Containers. Masks do stack in WebGL rendering.3893907. **Group.get() vs Group.getFirst() differ.** `get(x, y)` is shorthand for `getFirst(false, true, x, y)` -- finds first *inactive* member and creates if none found. `getFirst(state)` defaults to `active===false` without auto-creating.3913928. **Layer cannot go inside a Container.** Containers can be added to Layers, but not the reverse.3933949. **Group children Set is unordered.** No index-based access. Use `getChildren()` to get an array snapshot.39539610. **killAndHide does not remove from the group.** It only sets `active=false` and `visible=false`. The object stays in the group for reuse.39739811. **Container.setScrollFactor does not auto-propagate.** Pass `true` as the third argument to also update children: `container.setScrollFactor(0, 0, true)`.39940012. **Group.create() adds to the Scene display list.** But `group.add()` does NOT unless you pass `true` as the second argument.40140213. **Container needs setSize() for input.** Containers have no implicit size. You must call `container.setSize(width, height)` before `setInteractive()` will work with a hit area.403404## Source File Map405406| File | Description |407|---|---|408| `src/gameobjects/group/Group.js` | Group class -- pooling, create, getFirst*, kill, batch ops |409| `src/gameobjects/group/GroupFactory.js` | `this.add.group()` factory registration |410| `src/gameobjects/group/typedefs/GroupConfig.js` | GroupConfig typedef (classType, maxSize, callbacks) |411| `src/gameobjects/group/typedefs/GroupCreateConfig.js` | GroupCreateConfig typedef (key, quantity, setXY, etc.) |412| `src/gameobjects/container/Container.js` | Container class -- list management, nested transforms |413| `src/gameobjects/container/ContainerFactory.js` | `this.add.container()` factory registration |414| `src/gameobjects/container/ContainerRender.js` | Container WebGL/Canvas render functions |415| `src/gameobjects/layer/Layer.js` | Layer class -- display list bucket with alpha/blend/mask |416| `src/gameobjects/layer/LayerFactory.js` | `this.add.layer()` factory registration |417| `src/gameobjects/layer/LayerRender.js` | Layer WebGL/Canvas render functions |418| `src/physics/arcade/ArcadePhysics.js` | `this.physics.add.group()` / `staticGroup()` |