Skip to main content

groups-and-containers

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.

Informations de source

Dépôt
phaserjs/phaser
Dernière activité de la source
10 avril 2026 à 12:38
Langue détectée de SKILL.md
anglais
Étoiles
40 564
Forks
7 183

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
groups-and-containers
description
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.
# 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 ```js // 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 ```js // 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. ```js // 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. ```js // 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 ```js // 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 ```js 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 ```js // 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 ```js 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)
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub