关于 physics-arcade
面向 Phaser 4 游戏开发者的 Arcade Physics 技能,涵盖物理体、运动、重力、碰撞、世界边界和碰撞筛选。
主要功能
- 01创建动态和静态物理体
- 02设置速度、加速度和重力
- 03注册碰撞与重叠检测
- 04配置世界边界和物理体形状
- 05用类别和掩码筛选碰撞
- 06暂停并配置物理世界
适用场景
- 01为玩家角色添加物理运动
- 02设置平台及可收集物的重叠检测
- 03将物理体限制在世界边界内
- 04选择允许相互碰撞的物理体类别
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill physics-arcade安装后在 Claude Code 中输入/physics-arcade确认已加载。
详细介绍
Arcade Physics
在 Phaser 4 中设置和使用 Arcade Physics —— 在 GameConfig 中启用物理、创建启用物理的 sprite/image/group、速度、加速度、重力、碰撞(collide/overlap)、世界边界、body 属性以及碰撞分类。
关键源路径: src/physics/arcade/ArcadePhysics.js、src/physics/arcade/World.js、src/physics/arcade/Body.js、src/physics/arcade/StaticBody.js、src/physics/arcade/Factory.js、src/physics/arcade/components/、src/physics/arcade/events/
相关技能: ../game-setup-and-config/SKILL.md、../sprites-and-images/SKILL.md、../tilemaps/SKILL.md、../groups-and-containers/SKILL.md
快速开始
class GameScene extends Phaser.Scene {
create() {
// Physics sprite (dynamic body, affected by gravity)
this.player = this.physics.add.sprite(100, 300, 'player');
this.player.setCollideWorldBounds(true);
this.player.setBounce(0.2);
// Static group (immovable platforms)
this.platforms = this.physics.add.staticGroup();
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();
// Register a persistent collider (checked every frame automatically)
this.physics.add.collider(this.player, this.platforms);
this.cursors = this.input.keyboard.createCursorKeys();
}
update() {
if (this.cursors.left.isDown) {
this.player.setVelocityX(-160);
} else if (this.cursors.right.isDown) {
this.player.setVelocityX(160);
} else {
this.player.setVelocityX(0);
}
}
}
// Enable Arcade Physics in game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: GameScene
};
const game = new Phaser.Game(config);
核心概念
World(this.physics.world)
Phaser.Physics.Arcade.World 管理模拟中的所有 body。在 Scene 中通过 this.physics.world 访问。
- gravity -- 每个步进应用于所有动态 body 的
Vector2。可通过配置或this.physics.world.gravity.y = 300设置。 - bounds -- 定义世界边界的
Rectangle。默认值为游戏画布尺寸。 - bodies -- 所有动态
Body实例的Set。 - staticBodies -- 所有
StaticBody实例的Set。 - colliders -- 已注册的
Collider对象的ProcessQueue(来自this.physics.add.collider/this.physics.add.overlap)。 - fps -- 每秒物理步进数(默认 60)。只读;可通过
world.setFPS(120)更改。 - fixedStep --
true(默认)使用固定时间步长;false则同步到渲染 fps。 - timeScale -- 缩放因子:1.0 = 正常,2.0 = 半速,0.5 = 双倍速度。
- isPaused -- 为 true 时,不更新任何 body,也不运行任何 collider。可通过
world.pause()/world.resume()切换。 - useTree --
true(默认)对动态 body 使用 RTree 空间索引。对于 5000+ 个 body,请禁用。 - drawDebug -- 为
true时启用调试渲染。可通过配置debug: true设置。
Body(Body)
当你使用 this.physics.add.sprite() 或 this.physics.add.image() 时,会自动创建一个动态 body。可通过 gameObject.body 访问它。
关键属性(全部位于 Body 上):
- velocity --
Vector2,像素/秒 - acceleration --
Vector2,像素/秒^2 - drag --
Vector2,减速度(仅在加速度为零时应用) - gravity --
Vector2,每个 body 的重力,会叠加到世界重力上 - bounce --
Vector2,相对于 1 的回弹系数 - friction --
Vector2,默认(1, 0)-- 不可移动 body 传递给其上承载 body 的速度 - maxVelocity --
Vector2,默认(10000, 10000)| maxSpeed -- 标量上限,默认-1(无) - mass -- 默认
1,影响碰撞动量交换 - immovable --
false;若为true,则永远不会被碰撞移动 - pushable --
true;若为false,则将速度反射给碰撞的 body - moves --
true;若为false,则位置/旋转不会由物理更新 - enable --
true;false会将其从模拟中移除 - useDamping --
false;为true时,drag 是乘数(0-1)而非线性值 - collideWorldBounds --
false;onWorldBounds/onCollide/onOverlap --false,设为true以发出事件
形状:isCircle(通过 setCircle 设置)、width/height、offset(Vector2)、center(只读中点)。
碰撞状态(只读,每个步进重置):touching / blocked / wasTouching -- { none, up, down, left, right }。embedded -- 两者重叠且速度为零。
角度:angularVelocity(度/秒)、angularAcceleration、angularDrag、maxAngular(默认 1000)、allowRotation(默认 true)。
静态刚体(StaticBody)
静态刚体永不移动,也不受重力或速度影响。它使用优化的 RTree 实现快速空间查询。
- 通过
this.physics.add.staticSprite()、this.physics.add.staticImage()或this.physics.add.staticGroup()创建。 - 在更改父游戏对象的位置或缩放后,调用
body.reset()或gameObject.refreshBody()进行同步。 - 与动态刚体一样具有
collisionCategory和collisionMask。 - 没有速度、加速度、阻力或重力属性。
常见模式
在现有游戏对象上启用物理
// Add a physics body to any existing Game Object
this.physics.add.existing(mySprite); // dynamic body
this.physics.add.existing(mySprite, true); // static body
// Or enable via the world directly
this.physics.world.enable(mySprite); // dynamic
this.physics.world.enable(mySprite, Phaser.Physics.Arcade.STATIC_BODY);
创建物理精灵和图像
// Via the physics factory (this.physics.add)
const player = this.physics.add.sprite(100, 200, 'player'); // dynamic body
const coin = this.physics.add.image(300, 100, 'coin'); // dynamic body
const wall = this.physics.add.staticImage(400, 300, 'wall'); // static body
const platform = this.physics.add.staticSprite(400, 500, 'plat'); // static body
// Standalone bodies (no Game Object)
const sensor = this.physics.add.body(200, 200, 32, 32); // dynamic Body
const zone = this.physics.add.staticBody(100, 100, 64, 64); // static Body
速度和重力
// Direct velocity
player.setVelocity(200, -300); // x=200 px/s, y=-300 px/s (upward)
player.setVelocityX(200);
player.body.velocity.set(200, -300); // equivalent via Vector2
// Acceleration + max velocity
player.setAcceleration(100, 0);
player.setMaxVelocity(300, 600);
// Per-body gravity (added to world gravity)
player.body.gravity.y = 200;
player.body.allowGravity = false; // exempt from world gravity
// Drag (applied only when acceleration is zero)
player.setDrag(300); // linear deceleration
player.body.useDamping = true;
player.setDrag(0.05); // damping mode: keeps 5% velocity/sec
// Bounce
player.setBounce(0.5); // both axes
player.setBounceY(1); // full vertical bounce
碰撞和重叠
两种方式:持久碰撞器(每帧自动检查)或一次性检查(在 update() 中调用)。
// --- Persistent Colliders (preferred) ---
// Created via this.physics.add.collider / this.physics.add.overlap
// Automatically checked every physics step
const collider = this.physics.add.collider(player, platforms);
const overlap = this.physics.add.overlap(player, coins, collectCoin, null, this);
function collectCoin (player, coin) {
coin.disableBody(true, true); // disable physics and hide
}
// Collider management
collider.active = false; // temporarily disable
collider.destroy(); // remove permanently
// With processCallback (must return boolean to allow collision)
this.physics.add.collider(player, enemies, onHit, canCollide, this);
function canCollide (player, enemy) {
return !player.getData('invincible');
}
// --- One-shot checks (in update) ---
// Called manually each frame; no Collider object created
this.physics.collide(player, platforms);
this.physics.overlap(player, coins, collectCoin, null, this);
// Self-collision within a single group
this.physics.add.collider(enemies, enemies);
组
// Dynamic physics group -- members get dynamic bodies automatically
const bullets = this.physics.add.group({
classType: Phaser.Physics.Arcade.Sprite, // default: ArcadeSprite
maxSize: 20,
collideWorldBounds: true,
bounceX: 1,
bounceY: 1,
velocityX: 200,
velocityY: 0,
allowGravity: false,
immovable: false
});
// Static physics group -- members get static bodies
const platforms = this.physics.add.staticGroup();
platforms.create(400, 568, 'ground');
platforms.create(600, 400, 'ground');
// After modifying a static group member's transform, refresh:
platforms.create(400, 568, 'ground').setScale(2).refreshBody();
// Group velocity helpers
bullets.setVelocity(200, 0); // all members
bullets.setVelocityX(200);
bullets.setVelocityY(0, 10); // with step increment per member
PhysicsGroup 配置键(作为新成员的默认值应用):collideWorldBounds、bounceX/Y、accelerationX/Y、dragX/Y、gravityX/Y、frictionX/Y、velocityX/Y、angularVelocity、angularAcceleration、angularDrag、maxVelocityX/Y、maxSpeed、mass、immovable、allowDrag、allowGravity、allowRotation、useDamping、enable。
世界边界
// Set bounds size and which edges collide (left, right, up, down)
this.physics.world.setBounds(0, 0, 1600, 1200, true, true, false, true);
this.physics.world.setBoundsCollision(true, true, false, true); // edges only
// Per-body world bounds
player.setCollideWorldBounds(true);
player.body.setBoundsRectangle(new Phaser.Geom.Rectangle(100, 100, 600, 400));
player.body.worldBounce = new Phaser.Math.Vector2(0.5, 0.5);
// Detect world bounds hit via event (requires opt-in)
player.body.onWorldBounds = true;
this.physics.world.on('worldbounds', (body, up, down, left, right) => {
console.log('Hit edge:', { up, down, left, right });
});
刚体属性
// Size and shape
player.body.setSize(24, 32, true); // width, height, re-center on GO
player.body.setOffset(4, 0); // offset from GO position
player.body.setCircle(16); // circular body, radius 16
player.body.setCircle(16, 4, 4); // circular with offset
// Collision behavior
player.body.setImmovable(true); // not moved by collisions
player.body.setPushable(false); // reflects velocity to collider
player.body.slideFactor.set(0, 0); // Sokoban-style: stops after push
player.body.setMass(2); // affects momentum exchange
// Enable / disable
player.disableBody(true, true); // disable body + hide Game Object
player.enableBody(true, x, y, true, true); // re-enable at position + show
// Per-direction collision check
player.body.checkCollision.up = false; // don't collide from above
player.body.syncBounds = true; // auto-sync size to texture
碰撞类别
碰撞类别让你可以使用位掩码值过滤哪些刚体可以相互碰撞。最多 32 个类别。
// Get unique category values from the physics plugin
const CAT_PLAYER = this.physics.nextCategory(); // 0x0002
const CAT_ENEMY = this.physics.nextCategory(); // 0x0004
const CAT_BULLET = this.physics.nextCategory(); // 0x0008
// Assign categories to bodies or groups
player.setCollisionCategory(CAT_PLAYER);
enemy.setCollisionCategory(CAT_ENEMY);
bullet.setCollisionCategory(CAT_BULLET);
// Set what each body collides with
player.setCollidesWith([CAT_ENEMY]); // player hits enemies only
enemy.setCollidesWith([CAT_PLAYER, CAT_BULLET]); // enemies hit player + bullets
bullet.setCollidesWith([CAT_ENEMY]); // bullets hit enemies only
// Add/remove individual categories from existing mask
player.addCollidesWith(CAT_BULLET);
player.removeCollidesWith(CAT_ENEMY);
// Check if a body will collide with a category
player.willCollideWith(CAT_ENEMY); // returns boolean
// Reset to default (collides with everything)
player.resetCollisionCategory();
// Works on Groups too
enemies.setCollisionCategory(CAT_ENEMY);
enemies.setCollidesWith([CAT_PLAYER, CAT_BULLET]);
默认:所有刚体的类别为 0x0001,collisionMask 为 1。一切仍然与一切碰撞,因为 (1 & 1) !== 0。PhysicsGroup 默认掩码为 2147483647(所有位)。在更改类别后,调用 resetCollisionCategory() 将掩码设置为所有位。
配置参考
ArcadeWorldConfig -- 通过 GameConfig 或 SceneConfig 中的 physics.arcade 传入:
| 属性 | 默认值 | 描述 |
|---|---|---|
fps | 60 | 每秒物理步数 |
fixedStep | true | 使用固定时间步长 vs 渲染同步 |
timeScale | 1 | 模拟速度倍数 |
gravity | { x: 0, y: 0 } | 世界重力,单位为 px/sec |
x, y | 0 | 世界边界原点 |
width, height | 游戏尺寸 | 世界边界尺寸 |
checkCollision | 全部 true | { up, down, left, right } 边缘碰撞 |
overlapBias | 4 | 分离的重叠阈值 |
tileBias | 16 | 瓦片重叠阈值 |
forceX | false | 先水平分离 |
isPaused | false | 启动时暂停模拟 |
debug | false | 启用调试渲染 |
debugShowBody / debugShowStaticBody / debugShowVelocity | true | 调试显示开关 |
debugBodyColor / debugStaticBodyColor / debugVelocityColor | 0xff00ff / 0x0000ff / 0x00ff00 | 调试颜色 |
maxEntries | 16 | 每个节点的 RTree 项数 |
useTree | true | 对动态刚体使用 RTree |
customUpdate | false | 如果为 true,自行调用 world.update() |
场景级配置会覆盖游戏级配置(合并)。
事件
所有事件都在 this.physics.world 上触发:
| 事件 | 字符串 | 条件 | 回调参数 |
|---|---|---|---|
COLLIDE | 'collide' | 两个刚体碰撞且至少一个具有 onCollide = true | (gameObject1, gameObject2, body1, body2) |
OVERLAP | 'overlap' | 两个刚体重叠且至少一个具有 onOverlap = true | (gameObject1, gameObject2, body1, body2) |
WORLD_BOUNDS | 'worldbounds' | 刚体碰到世界边缘且具有 onWorldBounds = true | (body, up, down, left, right) |
TILE_COLLIDE | 'tilecollide' | 刚体与瓦片碰撞 | (gameObject, tile, body) |
TILE_OVERLAP | 'tileoverlap' | 刚体与瓦片重叠 | (gameObject, tile, body) |
WORLD_STEP | 'worldstep' | 每次物理步之后 | (delta) |
PAUSE | 'pause' | 世界暂停 | 无 |
RESUME | 'resume' | 世界恢复 | 无 |
事件需要按刚体选择性启用:将 body.onCollide、body.onOverlap 或 body.onWorldBounds 设置为 true。
API 快速参考
场景插件(this.physics)
| 方法 | 描述 |
|---|---|
this.physics.add.sprite(x, y, key, frame) | 创建带动态刚体的 sprite |
this.physics.add.image(x, y, key, frame) | 创建带动态刚体的 image |
this.physics.add.staticSprite(x, y, key, frame) | 带静态刚体的 Sprite |
this.physics.add.staticImage(x, y, key, frame) | 带静态刚体的 Image |
this.physics.add.group(config) | 动态物理组 |
this.physics.add.staticGroup(config) | 静态物理组 |
this.physics.add.existing(go, isStatic?) | 为现有 Game Object 添加刚体 |
this.physics.add.body(x, y, w?, h?) | 独立的动态 Body(无 GO) |
this.physics.add.staticBody(x, y, w?, h?) | 独立的静态 Body(无 GO) |
this.physics.add.collider(a, b, cb?, proc?, ctx?) | 持久碰撞器 |
this.physics.add.overlap(a, b, cb?, proc?, ctx?) | 持久重叠检测 |
this.physics.collide(a, b, cb?, proc?, ctx?) | 一次性碰撞检测 |
this.physics.overlap(a, b, cb?, proc?, ctx?) | 一次性重叠检测 |
this.physics.nextCategory() | 下一个碰撞类别位掩码 |
this.physics.pause() / resume() | 暂停/恢复模拟 |
this.physics.accelerateTo(go, x, y, spd?, maxX?, maxY?) | 向目标点加速 |
this.physics.moveTo(go, x, y, spd?, maxTime?) | 以指定速度向目标点移动 |
this.physics.velocityFromAngle(angle, speed?, vec2?) | 角度(度)转速度 |
this.physics.velocityFromRotation(rot, speed?, vec2?) | 旋转(弧度)转速度 |
this.physics.closest(source, targets?) | 查找最近的刚体 |
this.physics.furthest(source, targets?) | 查找最远的刚体 |
this.physics.overlapRect(x, y, w, h, dyn?, static?) | 查询矩形范围内的刚体 |
this.physics.overlapCirc(x, y, r, dyn?, static?) | 查询圆形范围内的刚体 |
此外还有:accelerateToObject、moveToObject、collideTiles、overlapTiles、enableUpdate、disableUpdate。
世界(this.physics.world)
| 方法 | 描述 |
|---|---|
setBounds(x, y, w, h, left?, right?, up?, down?) | 设置边界 + 边缘检测 |
setBoundsCollision(left?, right?, up?, down?) | 设置哪些边缘参与碰撞 |
setFPS(framerate) | 更改物理步进速率 |
enable(object, bodyType?) | 为对象/组/数组启用物理 |
disable(object) | 为对象/组/数组禁用物理 |
add(body) / remove(body) | 从模拟中添加/移除 Body |
addCollider / addOverlap | 创建 Collider(参数与工厂方法相同) |
removeCollider(collider) | 移除 Collider |
pause() / resume() | 暂停/恢复模拟 |
createDebugGraphic() | 创建调试渲染图形 |
Body 方法
| 方法 | 描述 |
|---|---|
setVelocity(x, y) / setVelocityX / setVelocityY | 设置速度 |
setAcceleration(x, y) / setAccelerationX / Y | 设置加速度 |
setMaxVelocity(x, y) / setMaxSpeed(speed) | 速度上限 |
setBounce(x, y) / setDrag(x, y) | 弹性和阻力 |
setDamping(bool) | 启用阻尼模式 |
setFriction(x, y) / setGravity(x, y) | 摩擦力和每个刚体的重力 |
setMass(val) / setImmovable(bool) / setPushable(bool) | 质量和碰撞行为 |
setSize(w, h, center?) / setOffset(x, y) | 调整刚体尺寸/偏移 |
setCircle(radius, offX?, offY?) | 切换为圆形刚体 |
setCollideWorldBounds(val, bX?, bY?) | 与世界边界碰撞 |
setBoundsRectangle(rect) | 自定义边界矩形 |
setAllowGravity / setAllowDrag / setAllowRotation | 切换物理特性 |
setEnable(val) | 启用/禁用刚体 |
setCollisionCategory / setCollidesWith / addCollidesWith / removeCollidesWith | 类别过滤 |
resetCollisionCategory() | 重置为默认(全部) |
reset(x, y) / stop() | 重置位置或将速度归零 |
注意事项
-
在生产环境中使用
debug: true—— 调试渲染开销很大。发布构建时务必禁用。 -
静态刚体变换的变更 —— 更改静态刚体的 Game Object 的位置、缩放或原点后,必须调用
body.reset()或gameObject.refreshBody()。静态刚体不会自动同步。 -
collide与overlap——collide会执行分离(将刚体推开)。overlap只检测相交,不会移动任何物体。对于拾取物之类的触发器,请使用overlap。 -
持久 Collider 与一次性检测 ——
this.physics.add.collider()会创建一个每帧自动检查的 Collider。this.physics.collide()是一次性检查,必须在update()中每帧调用。优先使用持久 Collider。 -
事件需要主动启用 —— 世界事件(
'collide'、'overlap'、'worldbounds')只有在相关刚体属性(onCollide、onOverlap、onWorldBounds)设置为true时才会触发。 -
碰撞类别的默认值 —— 默认情况下,所有刚体的类别为
0x0001,掩码为1(PhysicsGroup 默认掩码为2147483647)。调用this.physics.nextCategory()可获取新的类别值;总共最多 32 个类别。更改类别后,使用resetCollisionCategory()将掩码设置为所有位。 -
Group 默认值仅在创建时生效 —— PhysicsGroup 的
defaults(如velocityX、bounceY)会在成员被添加/创建时应用。之后更改defaults不会追溯更新已有成员。 -
customUpdate配置 —— 在 arcade 配置中设置customUpdate: true会阻止世界在 Scene UPDATE 事件上自动更新。你必须手动调用this.physics.world.update(time, delta)。 -
useDamping阻力值 —— 当useDamping为true时,阻力值应该很小(例如0.05),因为它们充当乘数。值为0.05意味着刚体每秒保留其速度的 5%。 -
Immovable 与 pushable ——
immovable = true意味着刚体完全不会被碰撞移动。pushable = false意味着刚体将速度反射回碰撞的刚体,但仍可被分离。 -
与 TilemapLayer 的 Overlap —— 当对 TilemapLayer 使用
overlapOnly时,无论单个瓦片上的碰撞设置如何,每个瓦片都会被检查。 -
RTree 阈值 —— RTree 空间索引(默认启用)在动态刚体数量非常多时重建开销会很大。对于 5000+ 动态刚体,考虑设置
useTree: false。
源文件映射
| 文件 | 用途 |
|---|---|
src/physics/arcade/ArcadePhysics.js | 场景插件(this.physics)——collide、overlap、moveTo、accelerateTo、nextCategory |
src/physics/arcade/World.js | 物理世界——body、重力、边界、碰撞器、步进循环、setBounds、addCollider |
src/physics/arcade/Body.js | 动态 body——速度、加速度、反弹、阻力、重力、质量、不可移动、可推动 |
src/physics/arcade/StaticBody.js | 静态 body——不可移动、无速度、优化的 RTree 查找 |
src/physics/arcade/Factory.js | this.physics.add——sprite、image、group、staticGroup、body、existing、collider、overlap |
src/physics/arcade/Collider.js | 碰撞器对象——带回调的持久碰撞/重叠检测 |
src/physics/arcade/PhysicsGroup.js | 动态物理组,支持按成员设置默认值 |
src/physics/arcade/StaticPhysicsGroup.js | 静态物理组,支持自动刷新 |
src/physics/arcade/components/index.js | Body 组件混入——Acceleration、Angular、Bounce、Collision、Debug、Drag、Enable、Friction、Gravity、Immovable、Mass、Pushable、Size、Velocity |
src/physics/arcade/components/Collision.js | 碰撞类别/掩码方法——setCollisionCategory、setCollidesWith、addCollidesWith、removeCollidesWith |
src/physics/arcade/events/ | 事件常量——COLLIDE、OVERLAP、WORLD_BOUNDS、TILE_COLLIDE、TILE_OVERLAP、WORLD_STEP、PAUSE、RESUME |
src/physics/arcade/typedefs/ArcadeWorldConfig.js | arcade 物理配置选项的 TypeDef |