phaser-arcade-physics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Phaser 3 Arcade Physics

Phaser 3 Arcade Physics

Add movement and collision to a Phaser 3 game with the lightweight Arcade Physics engine (AABB rectangles and circles only). Targets Phaser 3.90.
借助轻量级的Arcade Physics引擎(仅支持AABB矩形和圆形碰撞),为Phaser 3游戏添加移动和碰撞功能。本文针对Phaser 3.90版本。

When to use

使用场景

  • Use for top-down or platformer movement, velocity/acceleration/gravity, bouncing, world bounds, and collision/overlap resolution between sprites, groups, and tiles.
  • Use when the scene enables
    physics: { default: 'arcade' }
    and code calls
    this.physics.add.*
    ,
    body.setVelocity
    , or
    this.physics.add.collider
    .
When not to use: the
Game
config, scene structure, asset loading, or cameras → use
phaser-core
. Hinges, springs, complex polygons, or stacking rigid bodies → use Matter physics (a different engine; Arcade and Matter bodies do not interact). For engine-agnostic feel tuning see
physics-tuning
.
  • 适用于俯视角或平台跳跃类游戏的移动、速度/加速度/重力设置、弹跳效果、世界边界限制,以及精灵、组和瓦片之间的碰撞/重叠检测处理。
  • 当场景配置中启用
    physics: { default: 'arcade' }
    ,且代码中调用
    this.physics.add.*
    body.setVelocity
    this.physics.add.collider
    时使用。
不适用于以下场景:游戏配置、场景结构、资源加载或相机相关操作 → 请使用
phaser-core
。铰链、弹簧、复杂多边形或堆叠刚体 → 请使用Matter物理引擎(这是另一个不同的引擎;Arcade和Matter的物理体无法交互)。若需进行引擎无关的手感调优,请参考
physics-tuning

Core workflow

核心工作流程

  1. Enable the world. Set
    physics: { default: 'arcade', arcade: { gravity: {...}, debug: true } }
    in the game or scene config. Turn
    debug
    on while building to see body outlines and velocity vectors.
  2. Give a sprite a body. Create it with
    this.physics.add.sprite(...)
    (dynamic) or
    this.physics.add.staticImage(...)
    (static), or attach to an existing object with
    this.physics.add.existing(obj)
    .
  3. Drive it through the body, not by setting
    x
    /
    y
    .
    Use
    setVelocity
    ,
    setAcceleration
    , gravity,
    setBounce
    , and
    setCollideWorldBounds
    . The engine integrates position from velocity each step (already frame-rate independent).
  4. Resolve interactions.
    this.physics.add.collider(a, b)
    separates bodies;
    this.physics.add.overlap(a, b, cb)
    detects without separating (pickups, triggers). Pass a callback to react.
  5. Group many objects. Use
    this.physics.add.group()
    (dynamic) or
    staticGroup()
    (platforms) so one collider call handles all members.
  6. Check ground contact with
    body.onFloor()
    /
    body.blocked.down
    before jumping. Run with
    debug: true
    and confirm bodies, contacts, and bounds.
  1. 启用物理世界。在游戏或场景配置中设置
    physics: { default: 'arcade', arcade: { gravity: {...}, debug: true } }
    。开发阶段开启
    debug
    模式,可查看物理体轮廓和速度向量。
  2. 为精灵添加物理体。使用
    this.physics.add.sprite(...)
    创建动态物理体,或使用
    this.physics.add.staticImage(...)
    创建静态物理体;也可通过
    this.physics.add.existing(obj)
    为已有对象附加物理体。
  3. 通过物理体控制移动,而非直接设置
    x
    /
    y
    。使用
    setVelocity
    setAcceleration
    、重力、
    setBounce
    setCollideWorldBounds
    。引擎会在每一步根据速度计算位置(已实现帧率无关)。
  4. 处理交互
    this.physics.add.collider(a, b)
    会分离发生碰撞的物理体;
    this.physics.add.overlap(a, b, cb)
    仅检测重叠而不分离(适用于拾取物、触发器等场景)。可传入回调函数来响应交互。
  5. 批量管理对象。使用
    this.physics.add.group()
    (动态组)或
    staticGroup()
    (平台组),这样一次碰撞器调用即可处理组内所有成员。
  6. 检测地面接触。跳跃前使用
    body.onFloor()
    /
    body.blocked.down
    检查是否接触地面。开启
    debug: true
    模式,确认物理体、接触点和边界是否正常。

Patterns

实现模式

1. Enable Arcade Physics (game config)

1. 启用Arcade Physics(游戏配置)

js
const config = {
  type: Phaser.AUTO,
  width: 800, height: 600,
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { x: 0, y: 600 },  // top-down? use { x: 0, y: 0 }
      debug: false                // true to draw bodies + velocity while building
    }
  },
  scene: [PlayScene]
};
new Phaser.Game(config);
js
const config = {
  type: Phaser.AUTO,
  width: 800, height: 600,
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { x: 0, y: 600 },  // 俯视角游戏?请设置为{ x: 0, y: 0 }
      debug: false                // 开发阶段设为true可绘制物理体和速度向量
    }
  },
  scene: [PlayScene]
};
new Phaser.Game(config);

2. Top-down movement (velocity from input)

2. 俯视角移动(基于输入设置速度)

js
create() {
  this.player = this.physics.add.sprite(400, 300, 'player');
  this.player.setCollideWorldBounds(true);
  this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
  const speed = 220;
  const body = this.player.body;
  body.setVelocity(0);                              // reset each frame
  if (this.cursors.left.isDown)  body.setVelocityX(-speed);
  if (this.cursors.right.isDown) body.setVelocityX(speed);
  if (this.cursors.up.isDown)    body.setVelocityY(-speed);
  if (this.cursors.down.isDown)  body.setVelocityY(speed);
  body.velocity.normalize().scale(speed);           // keep diagonals same speed
}
js
create() {
  this.player = this.physics.add.sprite(400, 300, 'player');
  this.player.setCollideWorldBounds(true);
  this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
  const speed = 220;
  const body = this.player.body;
  body.setVelocity(0);                              // 每帧重置速度
  if (this.cursors.left.isDown)  body.setVelocityX(-speed);
  if (this.cursors.right.isDown) body.setVelocityX(speed);
  if (this.cursors.up.isDown)    body.setVelocityY(-speed);
  if (this.cursors.down.isDown)  body.setVelocityY(speed);
  body.velocity.normalize().scale(speed);           // 保持斜向移动速度一致
}

3. Platformer jump (gravity + ground check)

3. 平台跳跃(重力+地面检测)

js
create() {
  this.player = this.physics.add.sprite(100, 450, 'player');
  this.player.setCollideWorldBounds(true);

  // Static platforms: one body each, never moved by collisions.
  this.platforms = this.physics.add.staticGroup();
  this.platforms.create(400, 568, 'ground');
  this.physics.add.collider(this.player, this.platforms);

  this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
  const onGround = this.player.body.blocked.down; // or this.player.body.onFloor()
  if (this.cursors.left.isDown)  this.player.setVelocityX(-160);
  else if (this.cursors.right.isDown) this.player.setVelocityX(160);
  else this.player.setVelocityX(0);

  if (this.cursors.up.isDown && onGround) this.player.setVelocityY(-450);
}
js
create() {
  this.player = this.physics.add.sprite(100, 450, 'player');
  this.player.setCollideWorldBounds(true);

  // 静态平台:每个平台对应一个物理体,碰撞时不会被推动
  this.platforms = this.physics.add.staticGroup();
  this.platforms.create(400, 568, 'ground');
  this.physics.add.collider(this.player, this.platforms);

  this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
  const onGround = this.player.body.blocked.down; // 或使用this.player.body.onFloor()
  if (this.cursors.left.isDown)  this.player.setVelocityX(-160);
  else if (this.cursors.right.isDown) this.player.setVelocityX(160);
  else this.player.setVelocityX(0);

  if (this.cursors.up.isDown && onGround) this.player.setVelocityY(-450);
}

4. Colliders vs overlaps (separate vs detect)

4. 碰撞器与重叠检测(分离vs检测)

js
// Push apart and react: player vs enemies.
this.physics.add.collider(this.player, this.enemies, (player, enemy) => {
  this.handleHit(player, enemy);
});

// Detect without pushing: collect coins. The 4th arg is an optional
// process callback returning a boolean to filter pairs before the main callback.
this.physics.add.overlap(this.player, this.coins, (player, coin) => {
  coin.disableBody(true, true);              // deactivate + hide
  this.registry.inc('score', 10);
});
js
// 分离物理体并响应:玩家vs敌人
this.physics.add.collider(this.player, this.enemies, (player, enemy) => {
  this.handleHit(player, enemy);
});

// 仅检测重叠不分离:收集金币。第四个参数是可选的处理回调函数,返回布尔值用于在主回调前过滤配对
this.physics.add.overlap(this.player, this.coins, (player, coin) => {
  coin.disableBody(true, true);              // 禁用并隐藏金币
  this.registry.inc('score', 10);
});

5. A group of moving objects

5. 移动对象组

js
this.bullets = this.physics.add.group({
  defaultKey: 'bullet',
  maxSize: 30                  // pool size; reuse instead of allocating
});

fire(x, y) {
  const bullet = this.bullets.get(x, y);     // reuses a dead bullet if available
  if (!bullet) return;
  bullet.enableBody(true, x, y, true, true);
  bullet.setVelocityY(-500);
}
js
this.bullets = this.physics.add.group({
  defaultKey: 'bullet',
  maxSize: 30                  // 对象池大小;复用对象而非重新创建
});

fire(x, y) {
  const bullet = this.bullets.get(x, y);     // 若有闲置子弹则复用
  if (!bullet) return;
  bullet.enableBody(true, x, y, true, true);
  bullet.setVelocityY(-500);
}

Pitfalls

常见陷阱

  • Sprite ignores physics → it was added with
    this.add.sprite
    instead of
    this.physics.add.sprite
    (or
    this.physics.add.existing(obj)
    ), so it has no body.
  • Setting
    sprite.x
    directly fights the engine
    → move dynamic bodies with
    setVelocity
    /
    setAcceleration
    . Direct position writes can tunnel through colliders.
  • Diagonal movement is faster → independent X and Y velocities add up; normalise the velocity vector and rescale to the intended speed.
  • Platforms get pushed by the player → use a
    staticGroup
    , or set
    body.setImmovable(true)
    on a dynamic platform.
  • onFloor()
    is always false
    → the body needs something to collide with; add the
    collider
    against the ground/platforms before checking, and ensure gravity is on.
  • Moved a static body but collisions are stale → static bodies don't auto-sync; call
    body.updateFromGameObject()
    (or
    refreshBody()
    on the game object).
  • Collider added every frame → register
    collider
    /
    overlap
    once in
    create
    , not in
    update
    .
  • 精灵无视物理规则 → 你使用了
    this.add.sprite
    而非
    this.physics.add.sprite
    (或
    this.physics.add.existing(obj)
    ),因此精灵没有物理体。
  • 直接设置
    sprite.x
    会与引擎冲突
    → 使用
    setVelocity
    /
    setAcceleration
    控制动态物理体移动。直接修改位置可能导致穿透碰撞体。
  • 斜向移动速度更快 → 独立设置X和Y速度会导致速度叠加;需归一化速度向量并重新缩放至预期速度。
  • 平台被玩家推动 → 使用
    staticGroup
    ,或为动态平台设置
    body.setImmovable(true)
  • onFloor()
    始终返回false
    → 物理体需要有可碰撞的对象;在检测前添加与地面/平台的碰撞器,并确保已启用重力。
  • 移动静态物理体后碰撞状态未更新 → 静态物理体不会自动同步;调用
    body.updateFromGameObject()
    (或在游戏对象上调用
    refreshBody()
    )。
  • 每帧都添加碰撞器 → 在
    create
    中注册一次
    collider
    /
    overlap
    ,不要在
    update
    中重复注册。

References

参考资料

  • For body anatomy and tuning (drag, bounce, max velocity, custom
    setSize
    /
    setCircle
    /
    setOffset
    hitboxes, collision categories/masks, and
    worldbounds
    events), read
    references/bodies-and-collision.md
    .
  • 关于物理体结构和调优(阻力、弹跳、最大速度、自定义
    setSize
    /
    setCircle
    /
    setOffset
    碰撞盒、碰撞分类/掩码、
    worldbounds
    事件),请阅读
    references/bodies-and-collision.md

Related skills

相关技能

  • phaser-core
    — game config, scenes, loader, cameras (the prerequisite setup).
  • physics-tuning
    — engine-agnostic feel (fixed timestep, tunneling, jitter).
  • platformer
    /
    tower-defense
    — genres that compose this skill.
  • level-design
    — laying out tile/platform geometry these bodies collide with.
  • phaser-core
    — 游戏配置、场景、资源加载器、相机(必备基础设置)。
  • physics-tuning
    — 引擎无关的手感调优(固定时间步长、穿透问题、抖动)。
  • platformer
    /
    tower-defense
    — 需结合此技能的游戏类型。
  • level-design
    — 物理体碰撞的瓦片/平台布局设计。