Phaser 游戏组 #

游戏组概述 #

Phaser 提供了 Group 和 Container 两种方式来管理多个游戏对象。

text
┌─────────────────────────────────────────────────────────────┐
│                    游戏组对比                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Group                                                      │
│  ├── 轻量级                                                │
│  ├── 不影响子对象位置                                      │
│  ├── 适合批量操作                                          │
│  └── 性能更好                                              │
│                                                             │
│  Container                                                  │
│  ├── 有自己的位置和变换                                    │
│  ├── 子对象相对容器定位                                    │
│  ├── 适合 UI 组件                                          │
│  └── 支持嵌套                                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Group 基础 #

创建 Group #

javascript
create() {
  this.enemies = this.add.group();
  
  this.coins = this.physics.add.group();
  
  this.platforms = this.physics.add.staticGroup();
}

添加对象 #

javascript
const group = this.add.group();

const sprite = this.add.sprite(100, 100, 'enemy');
group.add(sprite);

group.addMultiple([sprite1, sprite2, sprite3]);

const enemy = group.create(200, 100, 'enemy');

const enemies = group.createMultiple({
  key: 'enemy',
  repeat: 10,
  setXY: { x: 100, y: 100, stepX: 70 }
});

const enemies = group.createMultiple([
  { key: 'enemy', x: 100, y: 100 },
  { key: 'enemy', x: 200, y: 100 },
  { key: 'enemy', x: 300, y: 100 }
]);

移除对象 #

javascript
group.remove(sprite);

group.remove(sprite, true);

group.remove(sprite, true, true);

group.clear(true, true);

group.removeAll();

group.destroy();

访问对象 #

javascript
const children = group.getChildren();

const first = group.getFirst();

const firstAlive = group.getFirstAlive();

const firstDead = group.getFirstDead();

const count = group.getLength();

const countAlive = group.countActive(true);

const countDead = group.countActive(false);

const size = group.getSize();

const random = group.getRandom();

遍历对象 #

javascript
group.children.iterate((child) => {
  child.setTint(0xff0000);
});

group.children.each((child) => {
  child.x += 10;
});

const visible = group.getChildren().filter(child => child.visible);

const found = group.getChildren().find(child => child.x > 400);

批量操作 #

javascript
group.setAlpha(0.5);

group.setVisible(true);

group.setTint(0xff0000);

group.setDepth(10);

group.setOrigin(0.5, 0.5);

group.setProperty('x', 100);

group.setXY(100, 100);

group.incXY(10, 10);

group.shiftPosition(100, 100);

group.rotate(45);

group.scale(2, 2);

物理组 #

javascript
const group = this.physics.add.group({
  key: 'enemy',
  repeat: 10,
  setXY: { x: 100, y: 100, stepX: 70 },
  bounceX: 1,
  bounceY: 1,
  collideWorldBounds: true,
  velocityX: 100,
  velocityY: 50
});

group.setVelocity(100, 50);
group.setVelocityX(100);
group.setVelocityY(50);

this.physics.add.collider(group, this.platforms);
this.physics.add.overlap(this.player, group, this.hitEnemy, null, this);

静态组 #

javascript
const group = this.physics.add.staticGroup();

group.create(400, 568, 'ground').setScale(2).refreshBody();

group.create(600, 400, 'ground');
group.create(50, 250, 'ground');
group.create(750, 220, 'ground');

const ground = group.create(400, 568, 'ground');
ground.setScale(2);
ground.refreshBody();

对象池 #

javascript
class Bullet extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y) {
    super(scene, x, y, 'bullet');
  }
  
  fire(x, y, velocityX, velocityY) {
    this.setPosition(x, y);
    this.setActive(true);
    this.setVisible(true);
    this.setVelocity(velocityX, velocityY);
  }
  
  deactivate() {
    this.setActive(false);
    this.setVisible(false);
    this.setVelocity(0, 0);
  }
}

create() {
  this.bullets = this.physics.add.group({
    classType: Bullet,
    maxSize: 30,
    runChildUpdate: true
  });
}

fireBullet() {
  const bullet = this.bullets.get();
  if (bullet) {
    bullet.fire(this.player.x, this.player.y, 0, -300);
  }
}

update() {
  this.bullets.children.iterate((bullet) => {
    if (bullet && bullet.active && bullet.y < 0) {
      bullet.deactivate();
    }
  });
}

Group 配置 #

创建配置 #

javascript
const group = this.add.group({
  classType: Phaser.GameObjects.Sprite,
  name: 'enemies',
  active: true,
  maxSize: -1,
  runChildUpdate: false,
  createCallback: (child) => {
    console.log('Child added:', child);
  },
  removeCallback: (child) => {
    console.log('Child removed:', child);
  }
});

创建多个对象配置 #

javascript
const group = this.add.group();

group.createMultiple({
  key: 'enemy',
  frame: [0, 1, 2, 3],
  repeat: 5,
  randomKey: false,
  randomFrame: true,
  yoyo: false,
  quantity: 2,
  setXY: {
    x: 100,
    y: 100,
    stepX: 70,
    stepY: 50
  },
  setRotation: {
    value: 0,
    step: 0.1
  },
  setScale: {
    x: 1,
    y: 1,
    stepX: 0.1,
    stepY: 0.1
  },
  setAlpha: {
    value: 1,
    step: -0.1
  },
  hitArea: new Phaser.Geom.Rectangle(0, 0, 32, 32),
  hitAreaCallback: Phaser.Geom.Rectangle.Contains
});

Container #

创建 Container #

javascript
const container = this.add.container(x, y);

const container = this.add.container(400, 300);

添加对象 #

javascript
const container = this.add.container(400, 300);

const image = this.add.image(0, 0, 'player');
const text = this.add.text(0, 50, 'Player 1', { fontSize: '16px' });

container.add(image);
container.add(text);

container.add([image, text]);

container.addAt(text, 0);

移除对象 #

javascript
container.remove(image);

container.remove(image, true);

container.removeAt(0);

container.removeBetween(0, 5);

container.removeAll();

container.destroy();

访问对象 #

javascript
const children = container.list;

const first = container.first;
const last = container.last;

const count = container.length;

const at = container.getAt(0);

const index = container.getIndex(image);

遍历对象 #

javascript
container.each((child) => {
  child.setAlpha(0.5);
});

container.iterate('sprite', (child) => {
  child.setTint(0xff0000);
});

container.contains(sprite);

Container 属性 #

javascript
container.x = 100;
container.y = 200;
container.setPosition(100, 200);

container.rotation = Math.PI / 4;
container.angle = 45;

container.scaleX = 2;
container.scaleY = 2;
container.setScale(2, 2);

container.alpha = 0.5;

container.visible = true;

container.depth = 10;
container.setDepth(10);

container.setSize(200, 150);

Container 嵌套 #

javascript
const outerContainer = this.add.container(400, 300);
const innerContainer = this.add.container(0, 0);

const image = this.add.image(0, 0, 'player');
innerContainer.add(image);

outerContainer.add(innerContainer);

Container 交互 #

javascript
const container = this.add.container(400, 300);

const image = this.add.image(0, 0, 'button');
const text = this.add.text(0, 0, 'Click Me', { fontSize: '24px' }).setOrigin(0.5);

container.add([image, text]);

container.setSize(image.width, image.height);
container.setInteractive();

container.on('pointerdown', () => {
  console.log('Container clicked');
});

container.on('pointerover', () => {
  container.setAlpha(0.8);
});

container.on('pointerout', () => {
  container.setAlpha(1);
});

Container 物理 #

javascript
const container = this.add.container(100, 100);

const image = this.add.image(0, 0, 'player');
container.add(image);

container.setSize(32, 48);

this.physics.world.enable(container);
container.body.setCollideWorldBounds(true);
container.body.setBounce(0.2);

this.physics.add.collider(container, this.platforms);

Group vs Container #

使用 Group 的场景 #

javascript
const enemies = this.add.group();

for (let i = 0; i < 10; i++) {
  enemies.create(Phaser.Math.Between(50, 750), Phaser.Math.Between(50, 550), 'enemy');
}

this.physics.add.collider(enemies, this.platforms);
this.physics.add.overlap(this.player, enemies, this.hitEnemy, null, this);

enemies.children.iterate((enemy) => {
  enemy.setTint(0xff0000);
});

使用 Container 的场景 #

javascript
const button = this.add.container(400, 300);

const background = this.add.image(0, 0, 'button-bg');
const text = this.add.text(0, 0, 'Click Me', { fontSize: '24px' }).setOrigin(0.5);
const icon = this.add.image(-50, 0, 'icon');

button.add([background, text, icon]);
button.setSize(background.width, background.height);
button.setInteractive();

this.tweens.add({
  targets: button,
  y: 250,
  duration: 1000,
  yoyo: true,
  repeat: -1
});

实用示例 #

敌人管理器 #

javascript
class EnemyManager {
  constructor(scene) {
    this.scene = scene;
    this.enemies = scene.physics.add.group();
  }
  
  spawn(x, y, type = 'basic') {
    const enemy = this.enemies.get(x, y, `enemy-${type}`);
    
    if (!enemy) return null;
    
    enemy.setActive(true);
    enemy.setVisible(true);
    enemy.setCollideWorldBounds(true);
    
    switch (type) {
      case 'fast':
        enemy.setVelocity(Phaser.Math.Between(-200, 200), 0);
        enemy.health = 50;
        break;
      case 'tank':
        enemy.setVelocity(Phaser.Math.Between(-50, 50), 0);
        enemy.health = 200;
        break;
      default:
        enemy.setVelocity(Phaser.Math.Between(-100, 100), 0);
        enemy.health = 100;
    }
    
    return enemy;
  }
  
  despawn(enemy) {
    enemy.setActive(false);
    enemy.setVisible(false);
    enemy.setVelocity(0, 0);
    enemy.setPosition(-100, -100);
  }
  
  update() {
    this.enemies.children.iterate((enemy) => {
      if (!enemy || !enemy.active) return;
      
      if (enemy.health <= 0) {
        this.despawn(enemy);
      }
    });
  }
  
  getGroup() {
    return this.enemies;
  }
}

UI 面板 #

javascript
class UIPanel extends Phaser.GameObjects.Container {
  constructor(scene, x, y, width, height) {
    super(scene, x, y);
    
    this.width = width;
    this.height = height;
    
    this.background = scene.add.rectangle(0, 0, width, height, 0x333333, 0.9);
    this.background.setStrokeStyle(2, 0xffffff);
    
    this.title = scene.add.text(0, -height / 2 + 20, 'Panel Title', {
      fontSize: '24px',
      fill: '#ffffff'
    }).setOrigin(0.5);
    
    this.closeButton = scene.add.text(width / 2 - 20, -height / 2 + 20, 'X', {
      fontSize: '20px',
      fill: '#ffffff'
    }).setOrigin(0.5).setInteractive();
    
    this.closeButton.on('pointerdown', () => this.hide());
    
    this.add([this.background, this.title, this.closeButton]);
    
    scene.add.existing(this);
    
    this.setAlpha(0);
    this.setVisible(false);
  }
  
  show() {
    this.setVisible(true);
    this.scene.tweens.add({
      targets: this,
      alpha: 1,
      duration: 200
    });
  }
  
  hide() {
    this.scene.tweens.add({
      targets: this,
      alpha: 0,
      duration: 200,
      onComplete: () => {
        this.setVisible(false);
      }
    });
  }
  
  setTitle(text) {
    this.title.setText(text);
  }
}

下一步 #

现在你已经掌握了游戏组,接下来学习 着色器,了解如何使用 WebGL 着色器创建高级视觉效果!

最后更新:2026-03-29