Arcade Physics
Setting up and using Arcade Physics in Phaser 4 -- enabling physics in GameConfig, creating physics-enabled sprites/images/groups, velocity, acceleration, gravity, collisions (collide/overlap), world bounds, body properties, and collision categories.
Key source paths: 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/
Related skills: ../game-setup-and-config/SKILL.md, ../sprites-and-images/SKILL.md, ../tilemaps/SKILL.md, ../groups-and-containers/SKILL.md
Quick Start
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);
Core Concepts
World (this.physics.world)
The Phaser.Physics.Arcade.World manages all bodies in the simulation. Accessed via this.physics.world in a Scene.
- gravity --
Vector2 applied to all dynamic bodies each step. Set via config or this.physics.world.gravity.y = 300.
- bounds --
Rectangle defining the world boundary. Defaults to game canvas size.
- bodies --
Set of all dynamic Body instances.
- staticBodies --
Set of all StaticBody instances.
- colliders --
ProcessQueue of registered Collider objects (from this.physics.add.collider / this.physics.add.overlap).
- fps -- Physics steps per second (default 60). Read-only; change via
world.setFPS(120).
- fixedStep --
true (default) uses fixed timestep; false syncs to render fps.
- timeScale -- Scaling factor: 1.0 = normal, 2.0 = half speed, 0.5 = double speed.
- isPaused -- When true, no bodies update and no colliders run. Toggle via
world.pause() / world.resume().
- useTree --
true (default) uses RTree spatial index for dynamic bodies. Disable for 5000+ bodies.
- drawDebug -- Enables debug rendering when
true. Set via config debug: true.
Bodies (Body)
A dynamic body is created automatically when you use this.physics.add.sprite() or this.physics.add.image(). Access it via gameObject.body.
Key properties (all on Body):
- velocity --
Vector2, pixels/sec
- acceleration --
Vector2, pixels/sec^2
- drag --
Vector2, deceleration (applied only when acceleration is zero)
- gravity --
Vector2, per-body gravity added to world gravity
- bounce --
Vector2, rebound factor relative to 1
- friction --
Vector2, default (1, 0) -- velocity imparted by immovable body to riding body
- maxVelocity --
Vector2, default (10000, 10000) | maxSpeed -- scalar cap, default -1 (none)
- mass -- default
1, affects collision momentum exchange
- immovable --
false; if true, never moved by collisions
- pushable --
true; if false, reflects velocity to colliding body
- moves --
true; if false, position/rotation not updated by physics
- enable --
true; false removes from simulation
- useDamping --
false; when true, drag is a multiplier (0-1) instead of linear
- collideWorldBounds --
false; onWorldBounds/onCollide/onOverlap -- false, set true to emit events
Shape: isCircle (set via setCircle), width/height, offset (Vector2), center (read-only midpoint).
Collision state (read-only, reset each step): touching / blocked / wasTouching -- { none, up, down, left, right }. embedded -- both overlapping with zero velocity.
Angular: angularVelocity (deg/sec), angularAcceleration, angularDrag, maxAngular (default 1000), allowRotation (default true).
Static Bodies (StaticBody)
A static body never moves and is not affected by gravity or velocity. It uses an optimized RTree for fast spatial lookups.
- Created via
this.physics.add.staticSprite(), this.physics.add.staticImage(), or this.physics.add.staticGroup().
- After changing the parent Game Object's position or scale, call
body.reset() or gameObject.refreshBody() to sync.
- Has
collisionCategory and collisionMask like dynamic bodies.
- Does not have velocity, acceleration, drag, or gravity properties.
Common Patterns
Enabling Physics on Existing Game Objects
// 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);
Creating Physics Sprites and Images
// 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
Velocity and Gravity
// 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
Collide and Overlap
Two approaches: persistent Colliders (checked every frame automatically) or one-shot checks (called in 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);
Groups
// 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 config keys (applied as defaults to new members): 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.
World Bounds
// 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 });
});
Body Properties
// 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
Collision Categories
Collision categories let you filter which bodies can collide with each other using bitmask values. Maximum 32 categories.
// 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]);
Default: all bodies have category 0x0001 and collisionMask 1. Everything still collides with everything because (1 & 1) !== 0. PhysicsGroup defaults to mask 2147483647 (all bits). Call resetCollisionCategory() to set the mask to all bits after changing categories.
Configuration Reference
ArcadeWorldConfig -- passed via physics.arcade in GameConfig or SceneConfig:
| Property |
Default |
Description |
fps |
60 |
Physics steps per second |
fixedStep |
true |
Use fixed timestep vs render sync |
timeScale |
1 |
Simulation speed multiplier |
gravity |
{ x: 0, y: 0 } |
World gravity in px/sec |
x, y |
0 |
World bounds origin |
width, height |
game size |
World bounds dimensions |
checkCollision |
all true |
{ up, down, left, right } edge collision |
overlapBias |
4 |
Overlap threshold for separation |
tileBias |
16 |
Tile overlap threshold |
forceX |
false |
Separate horizontally first |
isPaused |
false |
Start simulation paused |
debug |
false |
Enable debug rendering |
debugShowBody / debugShowStaticBody / debugShowVelocity |
true |
Debug display toggles |
debugBodyColor / debugStaticBodyColor / debugVelocityColor |
0xff00ff / 0x0000ff / 0x00ff00 |
Debug colors |
maxEntries |
16 |
RTree items per node |
useTree |
true |
Use RTree for dynamic bodies |
customUpdate |
false |
If true, call world.update() yourself |
Scene-level config overrides game-level config (merged).
Events
All events are emitted on this.physics.world:
| Event |
String |
Condition |
Callback Args |
COLLIDE |
'collide' |
Two bodies collide and at least one has onCollide = true |
(gameObject1, gameObject2, body1, body2) |
OVERLAP |
'overlap' |
Two bodies overlap and at least one has onOverlap = true |
(gameObject1, gameObject2, body1, body2) |
WORLD_BOUNDS |
'worldbounds' |
Body hits world edge and has onWorldBounds = true |
(body, up, down, left, right) |
TILE_COLLIDE |
'tilecollide' |
Body collides with a tile |
(gameObject, tile, body) |
TILE_OVERLAP |
'tileoverlap' |
Body overlaps a tile |
(gameObject, tile, body) |
WORLD_STEP |
'worldstep' |
After each physics step |
(delta) |
PAUSE |
'pause' |
World paused |
none |
RESUME |
'resume' |
World resumed |
none |
Events require opt-in per body: set body.onCollide, body.onOverlap, or body.onWorldBounds to true.
API Quick Reference
Scene Plugin (this.physics)
| Method |
Description |
this.physics.add.sprite(x, y, key, frame) |
Create sprite with dynamic body |
this.physics.add.image(x, y, key, frame) |
Create image with dynamic body |
this.physics.add.staticSprite(x, y, key, frame) |
Sprite with static body |
this.physics.add.staticImage(x, y, key, frame) |
Image with static body |
this.physics.add.group(config) |
Dynamic physics group |
this.physics.add.staticGroup(config) |
Static physics group |
this.physics.add.existing(go, isStatic?) |
Add body to existing Game Object |
this.physics.add.body(x, y, w?, h?) |
Standalone dynamic Body (no GO) |
this.physics.add.staticBody(x, y, w?, h?) |
Standalone static Body (no GO) |
this.physics.add.collider(a, b, cb?, proc?, ctx?) |
Persistent collider |
this.physics.add.overlap(a, b, cb?, proc?, ctx?) |
Persistent overlap |
this.physics.collide(a, b, cb?, proc?, ctx?) |
One-shot collision check |
this.physics.overlap(a, b, cb?, proc?, ctx?) |
One-shot overlap check |
this.physics.nextCategory() |
Next collision category bitmask |
this.physics.pause() / resume() |
Pause/resume simulation |
this.physics.accelerateTo(go, x, y, spd?, maxX?, maxY?) |
Accelerate toward point |
this.physics.moveTo(go, x, y, spd?, maxTime?) |
Move toward point at speed |
this.physics.velocityFromAngle(angle, speed?, vec2?) |
Angle (deg) to velocity |
this.physics.velocityFromRotation(rot, speed?, vec2?) |
Rotation (rad) to velocity |
this.physics.closest(source, targets?) |
Find nearest body |
this.physics.furthest(source, targets?) |
Find farthest body |
this.physics.overlapRect(x, y, w, h, dyn?, static?) |
Query bodies in rectangle |
this.physics.overlapCirc(x, y, r, dyn?, static?) |
Query bodies in circle |
Also: accelerateToObject, moveToObject, collideTiles, overlapTiles, enableUpdate, disableUpdate.
World (this.physics.world)
| Method |
Description |
setBounds(x, y, w, h, left?, right?, up?, down?) |
Set boundary + edge checks |
setBoundsCollision(left?, right?, up?, down?) |
Set which edges collide |
setFPS(framerate) |
Change physics step rate |
enable(object, bodyType?) |
Enable physics on object/group/array |
disable(object) |
Disable physics on object/group/array |
add(body) / remove(body) |
Add/remove Body from simulation |
addCollider / addOverlap |
Create Collider (same args as factory) |
removeCollider(collider) |
Remove Collider |
pause() / resume() |
Pause/resume simulation |
createDebugGraphic() |
Create debug rendering graphic |
Body Methods
| Method |
Description |
setVelocity(x, y) / setVelocityX / setVelocityY |
Set velocity |
setAcceleration(x, y) / setAccelerationX / Y |
Set acceleration |
setMaxVelocity(x, y) / setMaxSpeed(speed) |
Velocity caps |
setBounce(x, y) / setDrag(x, y) |
Bounce and drag |
setDamping(bool) |
Enable damping mode |
setFriction(x, y) / setGravity(x, y) |
Friction and per-body gravity |
setMass(val) / setImmovable(bool) / setPushable(bool) |
Mass and collision behavior |
setSize(w, h, center?) / setOffset(x, y) |
Resize/offset body |
setCircle(radius, offX?, offY?) |
Switch to circular body |
setCollideWorldBounds(val, bX?, bY?) |
World bounds collision |
setBoundsRectangle(rect) |
Custom bounds rectangle |
setAllowGravity / setAllowDrag / setAllowRotation |
Toggle physics features |
setEnable(val) |
Enable/disable body |
setCollisionCategory / setCollidesWith / addCollidesWith / removeCollidesWith |
Category filtering |
resetCollisionCategory() |
Reset to default (all) |
reset(x, y) / stop() |
Reset position or zero velocity |
Gotchas
debug: true in production -- Debug rendering is expensive. Always disable for release builds.
Static body transform changes -- After changing position, scale, or origin of a static body's Game Object, you must call body.reset() or gameObject.refreshBody(). Static bodies do not auto-sync.
collide vs overlap -- collide performs separation (pushes bodies apart). overlap only detects intersection without moving anything. Use overlap for triggers like pickups.
Persistent Collider vs one-shot -- this.physics.add.collider() creates a Collider checked every frame automatically. this.physics.collide() is a one-shot check that must be called each frame in update(). Prefer persistent Colliders.
Events require opt-in -- World events ('collide', 'overlap', 'worldbounds') only fire if the relevant body property (onCollide, onOverlap, onWorldBounds) is set to true.
Collision categories default -- By default all bodies have category 0x0001 and mask 1 (PhysicsGroup defaults to mask 2147483647). Call this.physics.nextCategory() to get new category values; max 32 categories total. After changing categories, use resetCollisionCategory() to set the mask to all bits.
Group defaults only apply at creation -- PhysicsGroup defaults (like velocityX, bounceY) are applied when a member is added/created. Changing defaults later does not retroactively update existing members.
customUpdate config -- Setting customUpdate: true in the arcade config stops the world from auto-updating on the Scene UPDATE event. You must call this.physics.world.update(time, delta) manually.
useDamping drag values -- When useDamping is true, drag values should be small (e.g., 0.05) as they act as a multiplier. A value of 0.05 means the body keeps 5% of its velocity per second.
Immovable vs pushable -- immovable = true means the body is never moved by collisions at all. pushable = false means the body reflects velocity back to the colliding body but can still be separated.
Overlap with TilemapLayer -- When using overlapOnly with a TilemapLayer, every tile is checked regardless of collision settings on individual tiles.
RTree threshold -- The RTree spatial index (enabled by default) becomes expensive to rebuild with very large numbers of dynamic bodies. Consider setting useTree: false for 5000+ dynamic bodies.
Source File Map
| File |
Purpose |
src/physics/arcade/ArcadePhysics.js |
Scene plugin (this.physics) -- collide, overlap, moveTo, accelerateTo, nextCategory |
src/physics/arcade/World.js |
Physics world -- bodies, gravity, bounds, colliders, step loop, setBounds, addCollider |
src/physics/arcade/Body.js |
Dynamic body -- velocity, acceleration, bounce, drag, gravity, mass, immovable, pushable |
src/physics/arcade/StaticBody.js |
Static body -- immovable, no velocity, optimized RTree lookup |
src/physics/arcade/Factory.js |
this.physics.add -- sprite, image, group, staticGroup, body, existing, collider, overlap |
src/physics/arcade/Collider.js |
Collider object -- persistent collision/overlap check with callbacks |
src/physics/arcade/PhysicsGroup.js |
Dynamic physics group with per-member defaults |
src/physics/arcade/StaticPhysicsGroup.js |
Static physics group with auto-refresh |
src/physics/arcade/components/index.js |
Body component mixins -- Acceleration, Angular, Bounce, Collision, Debug, Drag, Enable, Friction, Gravity, Immovable, Mass, Pushable, Size, Velocity |
src/physics/arcade/components/Collision.js |
Collision category/mask methods -- setCollisionCategory, setCollidesWith, addCollidesWith, removeCollidesWith |
src/physics/arcade/events/ |
Event constants -- COLLIDE, OVERLAP, WORLD_BOUNDS, TILE_COLLIDE, TILE_OVERLAP, WORLD_STEP, PAUSE, RESUME |
src/physics/arcade/typedefs/ArcadeWorldConfig.js |
TypeDef for arcade physics config options |
1---2name: physics-arcade3description: Use this skill when using Arcade Physics in Phaser 4. Covers enabling physics, velocity, acceleration, gravity, collisions, overlap, world bounds, physics groups, static bodies, and collision categories. Triggers on: physics, arcade, velocity, gravity, collide, overlap, bounce, physics body.4---56# Arcade Physics7> Setting up and using Arcade Physics in Phaser 4 -- enabling physics in GameConfig, creating physics-enabled sprites/images/groups, velocity, acceleration, gravity, collisions (collide/overlap), world bounds, body properties, and collision categories.89**Key source paths:** `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/`10**Related skills:** ../game-setup-and-config/SKILL.md, ../sprites-and-images/SKILL.md, ../tilemaps/SKILL.md, ../groups-and-containers/SKILL.md1112## Quick Start1314```js15class GameScene extends Phaser.Scene {16 create() {17 // Physics sprite (dynamic body, affected by gravity)18 this.player = this.physics.add.sprite(100, 300, 'player');19 this.player.setCollideWorldBounds(true);20 this.player.setBounce(0.2);2122 // Static group (immovable platforms)23 this.platforms = this.physics.add.staticGroup();24 this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();2526 // Register a persistent collider (checked every frame automatically)27 this.physics.add.collider(this.player, this.platforms);2829 this.cursors = this.input.keyboard.createCursorKeys();30 }3132 update() {33 if (this.cursors.left.isDown) {34 this.player.setVelocityX(-160);35 } else if (this.cursors.right.isDown) {36 this.player.setVelocityX(160);37 } else {38 this.player.setVelocityX(0);39 }40 }41}4243// Enable Arcade Physics in game config44const config = {45 type: Phaser.AUTO,46 width: 800,47 height: 600,48 physics: {49 default: 'arcade',50 arcade: {51 gravity: { y: 300 },52 debug: false53 }54 },55 scene: GameScene56};5758const game = new Phaser.Game(config);59```6061## Core Concepts6263### World (`this.physics.world`)6465The `Phaser.Physics.Arcade.World` manages all bodies in the simulation. Accessed via `this.physics.world` in a Scene.6667- **gravity** -- `Vector2` applied to all dynamic bodies each step. Set via config or `this.physics.world.gravity.y = 300`.68- **bounds** -- `Rectangle` defining the world boundary. Defaults to game canvas size.69- **bodies** -- `Set` of all dynamic `Body` instances.70- **staticBodies** -- `Set` of all `StaticBody` instances.71- **colliders** -- `ProcessQueue` of registered `Collider` objects (from `this.physics.add.collider` / `this.physics.add.overlap`).72- **fps** -- Physics steps per second (default 60). Read-only; change via `world.setFPS(120)`.73- **fixedStep** -- `true` (default) uses fixed timestep; `false` syncs to render fps.74- **timeScale** -- Scaling factor: 1.0 = normal, 2.0 = half speed, 0.5 = double speed.75- **isPaused** -- When true, no bodies update and no colliders run. Toggle via `world.pause()` / `world.resume()`.76- **useTree** -- `true` (default) uses RTree spatial index for dynamic bodies. Disable for 5000+ bodies.77- **drawDebug** -- Enables debug rendering when `true`. Set via config `debug: true`.7879### Bodies (`Body`)8081A dynamic body is created automatically when you use `this.physics.add.sprite()` or `this.physics.add.image()`. Access it via `gameObject.body`.8283Key properties (all on `Body`):84- **velocity** -- `Vector2`, pixels/sec85- **acceleration** -- `Vector2`, pixels/sec^286- **drag** -- `Vector2`, deceleration (applied only when acceleration is zero)87- **gravity** -- `Vector2`, per-body gravity added to world gravity88- **bounce** -- `Vector2`, rebound factor relative to 189- **friction** -- `Vector2`, default `(1, 0)` -- velocity imparted by immovable body to riding body90- **maxVelocity** -- `Vector2`, default `(10000, 10000)` | **maxSpeed** -- scalar cap, default `-1` (none)91- **mass** -- default `1`, affects collision momentum exchange92- **immovable** -- `false`; if `true`, never moved by collisions93- **pushable** -- `true`; if `false`, reflects velocity to colliding body94- **moves** -- `true`; if `false`, position/rotation not updated by physics95- **enable** -- `true`; `false` removes from simulation96- **useDamping** -- `false`; when `true`, drag is a multiplier (0-1) instead of linear97- **collideWorldBounds** -- `false`; **onWorldBounds/onCollide/onOverlap** -- `false`, set `true` to emit events9899Shape: **isCircle** (set via `setCircle`), **width/height**, **offset** (`Vector2`), **center** (read-only midpoint).100101Collision state (read-only, reset each step): **touching** / **blocked** / **wasTouching** -- `{ none, up, down, left, right }`. **embedded** -- both overlapping with zero velocity.102103Angular: **angularVelocity** (deg/sec), **angularAcceleration**, **angularDrag**, **maxAngular** (default 1000), **allowRotation** (default true).104105### Static Bodies (`StaticBody`)106107A static body never moves and is not affected by gravity or velocity. It uses an optimized RTree for fast spatial lookups.108109- Created via `this.physics.add.staticSprite()`, `this.physics.add.staticImage()`, or `this.physics.add.staticGroup()`.110- After changing the parent Game Object's position or scale, call `body.reset()` or `gameObject.refreshBody()` to sync.111- Has `collisionCategory` and `collisionMask` like dynamic bodies.112- Does not have velocity, acceleration, drag, or gravity properties.113114## Common Patterns115116### Enabling Physics on Existing Game Objects117118```js119// Add a physics body to any existing Game Object120this.physics.add.existing(mySprite); // dynamic body121this.physics.add.existing(mySprite, true); // static body122123// Or enable via the world directly124this.physics.world.enable(mySprite); // dynamic125this.physics.world.enable(mySprite, Phaser.Physics.Arcade.STATIC_BODY);126```127128### Creating Physics Sprites and Images129130```js131// Via the physics factory (this.physics.add)132const player = this.physics.add.sprite(100, 200, 'player'); // dynamic body133const coin = this.physics.add.image(300, 100, 'coin'); // dynamic body134const wall = this.physics.add.staticImage(400, 300, 'wall'); // static body135const platform = this.physics.add.staticSprite(400, 500, 'plat'); // static body136137// Standalone bodies (no Game Object)138const sensor = this.physics.add.body(200, 200, 32, 32); // dynamic Body139const zone = this.physics.add.staticBody(100, 100, 64, 64); // static Body140```141142### Velocity and Gravity143144```js145// Direct velocity146player.setVelocity(200, -300); // x=200 px/s, y=-300 px/s (upward)147player.setVelocityX(200);148player.body.velocity.set(200, -300); // equivalent via Vector2149150// Acceleration + max velocity151player.setAcceleration(100, 0);152player.setMaxVelocity(300, 600);153154// Per-body gravity (added to world gravity)155player.body.gravity.y = 200;156player.body.allowGravity = false; // exempt from world gravity157158// Drag (applied only when acceleration is zero)159player.setDrag(300); // linear deceleration160player.body.useDamping = true;161player.setDrag(0.05); // damping mode: keeps 5% velocity/sec162163// Bounce164player.setBounce(0.5); // both axes165player.setBounceY(1); // full vertical bounce166```167168### Collide and Overlap169170Two approaches: **persistent Colliders** (checked every frame automatically) or **one-shot checks** (called in `update()`).171172```js173// --- Persistent Colliders (preferred) ---174// Created via this.physics.add.collider / this.physics.add.overlap175// Automatically checked every physics step176177const collider = this.physics.add.collider(player, platforms);178const overlap = this.physics.add.overlap(player, coins, collectCoin, null, this);179180function collectCoin (player, coin) {181 coin.disableBody(true, true); // disable physics and hide182}183184// Collider management185collider.active = false; // temporarily disable186collider.destroy(); // remove permanently187188// With processCallback (must return boolean to allow collision)189this.physics.add.collider(player, enemies, onHit, canCollide, this);190function canCollide (player, enemy) {191 return !player.getData('invincible');192}193194// --- One-shot checks (in update) ---195// Called manually each frame; no Collider object created196197this.physics.collide(player, platforms);198this.physics.overlap(player, coins, collectCoin, null, this);199200// Self-collision within a single group201this.physics.add.collider(enemies, enemies);202```203204### Groups205206```js207// Dynamic physics group -- members get dynamic bodies automatically208const bullets = this.physics.add.group({209 classType: Phaser.Physics.Arcade.Sprite, // default: ArcadeSprite210 maxSize: 20,211 collideWorldBounds: true,212 bounceX: 1,213 bounceY: 1,214 velocityX: 200,215 velocityY: 0,216 allowGravity: false,217 immovable: false218});219220// Static physics group -- members get static bodies221const platforms = this.physics.add.staticGroup();222platforms.create(400, 568, 'ground');223platforms.create(600, 400, 'ground');224225// After modifying a static group member's transform, refresh:226platforms.create(400, 568, 'ground').setScale(2).refreshBody();227228// Group velocity helpers229bullets.setVelocity(200, 0); // all members230bullets.setVelocityX(200);231bullets.setVelocityY(0, 10); // with step increment per member232```233234PhysicsGroup config keys (applied as defaults to new members): `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`.235236### World Bounds237238```js239// Set bounds size and which edges collide (left, right, up, down)240this.physics.world.setBounds(0, 0, 1600, 1200, true, true, false, true);241this.physics.world.setBoundsCollision(true, true, false, true); // edges only242243// Per-body world bounds244player.setCollideWorldBounds(true);245player.body.setBoundsRectangle(new Phaser.Geom.Rectangle(100, 100, 600, 400));246player.body.worldBounce = new Phaser.Math.Vector2(0.5, 0.5);247248// Detect world bounds hit via event (requires opt-in)249player.body.onWorldBounds = true;250this.physics.world.on('worldbounds', (body, up, down, left, right) => {251 console.log('Hit edge:', { up, down, left, right });252});253```254255### Body Properties256257```js258// Size and shape259player.body.setSize(24, 32, true); // width, height, re-center on GO260player.body.setOffset(4, 0); // offset from GO position261player.body.setCircle(16); // circular body, radius 16262player.body.setCircle(16, 4, 4); // circular with offset263264// Collision behavior265player.body.setImmovable(true); // not moved by collisions266player.body.setPushable(false); // reflects velocity to collider267player.body.slideFactor.set(0, 0); // Sokoban-style: stops after push268player.body.setMass(2); // affects momentum exchange269270// Enable / disable271player.disableBody(true, true); // disable body + hide Game Object272player.enableBody(true, x, y, true, true); // re-enable at position + show273274// Per-direction collision check275player.body.checkCollision.up = false; // don't collide from above276player.body.syncBounds = true; // auto-sync size to texture277```278279### Collision Categories280281Collision categories let you filter which bodies can collide with each other using bitmask values. Maximum 32 categories.282283```js284// Get unique category values from the physics plugin285const CAT_PLAYER = this.physics.nextCategory(); // 0x0002286const CAT_ENEMY = this.physics.nextCategory(); // 0x0004287const CAT_BULLET = this.physics.nextCategory(); // 0x0008288289// Assign categories to bodies or groups290player.setCollisionCategory(CAT_PLAYER);291enemy.setCollisionCategory(CAT_ENEMY);292bullet.setCollisionCategory(CAT_BULLET);293294// Set what each body collides with295player.setCollidesWith([CAT_ENEMY]); // player hits enemies only296enemy.setCollidesWith([CAT_PLAYER, CAT_BULLET]); // enemies hit player + bullets297bullet.setCollidesWith([CAT_ENEMY]); // bullets hit enemies only298299// Add/remove individual categories from existing mask300player.addCollidesWith(CAT_BULLET);301player.removeCollidesWith(CAT_ENEMY);302303// Check if a body will collide with a category304player.willCollideWith(CAT_ENEMY); // returns boolean305306// Reset to default (collides with everything)307player.resetCollisionCategory();308309// Works on Groups too310enemies.setCollisionCategory(CAT_ENEMY);311enemies.setCollidesWith([CAT_PLAYER, CAT_BULLET]);312```313314Default: all bodies have category `0x0001` and collisionMask `1`. Everything still collides with everything because `(1 & 1) !== 0`. PhysicsGroup defaults to mask `2147483647` (all bits). Call `resetCollisionCategory()` to set the mask to all bits after changing categories.315316## Configuration Reference317318`ArcadeWorldConfig` -- passed via `physics.arcade` in GameConfig or SceneConfig:319320| Property | Default | Description |321|---|---|---|322| `fps` | `60` | Physics steps per second |323| `fixedStep` | `true` | Use fixed timestep vs render sync |324| `timeScale` | `1` | Simulation speed multiplier |325| `gravity` | `{ x: 0, y: 0 }` | World gravity in px/sec |326| `x`, `y` | `0` | World bounds origin |327| `width`, `height` | game size | World bounds dimensions |328| `checkCollision` | all `true` | `{ up, down, left, right }` edge collision |329| `overlapBias` | `4` | Overlap threshold for separation |330| `tileBias` | `16` | Tile overlap threshold |331| `forceX` | `false` | Separate horizontally first |332| `isPaused` | `false` | Start simulation paused |333| `debug` | `false` | Enable debug rendering |334| `debugShowBody` / `debugShowStaticBody` / `debugShowVelocity` | `true` | Debug display toggles |335| `debugBodyColor` / `debugStaticBodyColor` / `debugVelocityColor` | `0xff00ff` / `0x0000ff` / `0x00ff00` | Debug colors |336| `maxEntries` | `16` | RTree items per node |337| `useTree` | `true` | Use RTree for dynamic bodies |338| `customUpdate` | `false` | If true, call `world.update()` yourself |339340Scene-level config overrides game-level config (merged).341342## Events343344All events are emitted on `this.physics.world`:345346| Event | String | Condition | Callback Args |347|---|---|---|---|348| `COLLIDE` | `'collide'` | Two bodies collide and at least one has `onCollide = true` | `(gameObject1, gameObject2, body1, body2)` |349| `OVERLAP` | `'overlap'` | Two bodies overlap and at least one has `onOverlap = true` | `(gameObject1, gameObject2, body1, body2)` |350| `WORLD_BOUNDS` | `'worldbounds'` | Body hits world edge and has `onWorldBounds = true` | `(body, up, down, left, right)` |351| `TILE_COLLIDE` | `'tilecollide'` | Body collides with a tile | `(gameObject, tile, body)` |352| `TILE_OVERLAP` | `'tileoverlap'` | Body overlaps a tile | `(gameObject, tile, body)` |353| `WORLD_STEP` | `'worldstep'` | After each physics step | `(delta)` |354| `PAUSE` | `'pause'` | World paused | none |355| `RESUME` | `'resume'` | World resumed | none |356357Events require opt-in per body: set `body.onCollide`, `body.onOverlap`, or `body.onWorldBounds` to `true`.358359## API Quick Reference360361### Scene Plugin (`this.physics`)362363| Method | Description |364|---|---|365| `this.physics.add.sprite(x, y, key, frame)` | Create sprite with dynamic body |366| `this.physics.add.image(x, y, key, frame)` | Create image with dynamic body |367| `this.physics.add.staticSprite(x, y, key, frame)` | Sprite with static body |368| `this.physics.add.staticImage(x, y, key, frame)` | Image with static body |369| `this.physics.add.group(config)` | Dynamic physics group |370| `this.physics.add.staticGroup(config)` | Static physics group |371| `this.physics.add.existing(go, isStatic?)` | Add body to existing Game Object |372| `this.physics.add.body(x, y, w?, h?)` | Standalone dynamic Body (no GO) |373| `this.physics.add.staticBody(x, y, w?, h?)` | Standalone static Body (no GO) |374| `this.physics.add.collider(a, b, cb?, proc?, ctx?)` | Persistent collider |375| `this.physics.add.overlap(a, b, cb?, proc?, ctx?)` | Persistent overlap |376| `this.physics.collide(a, b, cb?, proc?, ctx?)` | One-shot collision check |377| `this.physics.overlap(a, b, cb?, proc?, ctx?)` | One-shot overlap check |378| `this.physics.nextCategory()` | Next collision category bitmask |379| `this.physics.pause()` / `resume()` | Pause/resume simulation |380| `this.physics.accelerateTo(go, x, y, spd?, maxX?, maxY?)` | Accelerate toward point |381| `this.physics.moveTo(go, x, y, spd?, maxTime?)` | Move toward point at speed |382| `this.physics.velocityFromAngle(angle, speed?, vec2?)` | Angle (deg) to velocity |383| `this.physics.velocityFromRotation(rot, speed?, vec2?)` | Rotation (rad) to velocity |384| `this.physics.closest(source, targets?)` | Find nearest body |385| `this.physics.furthest(source, targets?)` | Find farthest body |386| `this.physics.overlapRect(x, y, w, h, dyn?, static?)` | Query bodies in rectangle |387| `this.physics.overlapCirc(x, y, r, dyn?, static?)` | Query bodies in circle |388389Also: `accelerateToObject`, `moveToObject`, `collideTiles`, `overlapTiles`, `enableUpdate`, `disableUpdate`.390391### World (`this.physics.world`)392393| Method | Description |394|---|---|395| `setBounds(x, y, w, h, left?, right?, up?, down?)` | Set boundary + edge checks |396| `setBoundsCollision(left?, right?, up?, down?)` | Set which edges collide |397| `setFPS(framerate)` | Change physics step rate |398| `enable(object, bodyType?)` | Enable physics on object/group/array |399| `disable(object)` | Disable physics on object/group/array |400| `add(body)` / `remove(body)` | Add/remove Body from simulation |401| `addCollider` / `addOverlap` | Create Collider (same args as factory) |402| `removeCollider(collider)` | Remove Collider |403| `pause()` / `resume()` | Pause/resume simulation |404| `createDebugGraphic()` | Create debug rendering graphic |405406### Body Methods407408| Method | Description |409|---|---|410| `setVelocity(x, y)` / `setVelocityX` / `setVelocityY` | Set velocity |411| `setAcceleration(x, y)` / `setAccelerationX` / `Y` | Set acceleration |412| `setMaxVelocity(x, y)` / `setMaxSpeed(speed)` | Velocity caps |413| `setBounce(x, y)` / `setDrag(x, y)` | Bounce and drag |414| `setDamping(bool)` | Enable damping mode |415| `setFriction(x, y)` / `setGravity(x, y)` | Friction and per-body gravity |416| `setMass(val)` / `setImmovable(bool)` / `setPushable(bool)` | Mass and collision behavior |417| `setSize(w, h, center?)` / `setOffset(x, y)` | Resize/offset body |418| `setCircle(radius, offX?, offY?)` | Switch to circular body |419| `setCollideWorldBounds(val, bX?, bY?)` | World bounds collision |420| `setBoundsRectangle(rect)` | Custom bounds rectangle |421| `setAllowGravity` / `setAllowDrag` / `setAllowRotation` | Toggle physics features |422| `setEnable(val)` | Enable/disable body |423| `setCollisionCategory` / `setCollidesWith` / `addCollidesWith` / `removeCollidesWith` | Category filtering |424| `resetCollisionCategory()` | Reset to default (all) |425| `reset(x, y)` / `stop()` | Reset position or zero velocity |426427## Gotchas4284291. **`debug: true` in production** -- Debug rendering is expensive. Always disable for release builds.4304312. **Static body transform changes** -- After changing position, scale, or origin of a static body's Game Object, you must call `body.reset()` or `gameObject.refreshBody()`. Static bodies do not auto-sync.4324333. **`collide` vs `overlap`** -- `collide` performs separation (pushes bodies apart). `overlap` only detects intersection without moving anything. Use `overlap` for triggers like pickups.4344354. **Persistent Collider vs one-shot** -- `this.physics.add.collider()` creates a Collider checked every frame automatically. `this.physics.collide()` is a one-shot check that must be called each frame in `update()`. Prefer persistent Colliders.4364375. **Events require opt-in** -- World events (`'collide'`, `'overlap'`, `'worldbounds'`) only fire if the relevant body property (`onCollide`, `onOverlap`, `onWorldBounds`) is set to `true`.4384396. **Collision categories default** -- By default all bodies have category `0x0001` and mask `1` (PhysicsGroup defaults to mask `2147483647`). Call `this.physics.nextCategory()` to get new category values; max 32 categories total. After changing categories, use `resetCollisionCategory()` to set the mask to all bits.4404417. **Group defaults only apply at creation** -- PhysicsGroup `defaults` (like `velocityX`, `bounceY`) are applied when a member is added/created. Changing `defaults` later does not retroactively update existing members.4424438. **`customUpdate` config** -- Setting `customUpdate: true` in the arcade config stops the world from auto-updating on the Scene UPDATE event. You must call `this.physics.world.update(time, delta)` manually.4444459. **`useDamping` drag values** -- When `useDamping` is `true`, drag values should be small (e.g., `0.05`) as they act as a multiplier. A value of `0.05` means the body keeps 5% of its velocity per second.44644710. **Immovable vs pushable** -- `immovable = true` means the body is never moved by collisions at all. `pushable = false` means the body reflects velocity back to the colliding body but can still be separated.44844911. **Overlap with TilemapLayer** -- When using `overlapOnly` with a TilemapLayer, every tile is checked regardless of collision settings on individual tiles.45045112. **RTree threshold** -- The RTree spatial index (enabled by default) becomes expensive to rebuild with very large numbers of dynamic bodies. Consider setting `useTree: false` for 5000+ dynamic bodies.452453## Source File Map454455| File | Purpose |456|---|---|457| `src/physics/arcade/ArcadePhysics.js` | Scene plugin (`this.physics`) -- collide, overlap, moveTo, accelerateTo, nextCategory |458| `src/physics/arcade/World.js` | Physics world -- bodies, gravity, bounds, colliders, step loop, setBounds, addCollider |459| `src/physics/arcade/Body.js` | Dynamic body -- velocity, acceleration, bounce, drag, gravity, mass, immovable, pushable |460| `src/physics/arcade/StaticBody.js` | Static body -- immovable, no velocity, optimized RTree lookup |461| `src/physics/arcade/Factory.js` | `this.physics.add` -- sprite, image, group, staticGroup, body, existing, collider, overlap |462| `src/physics/arcade/Collider.js` | Collider object -- persistent collision/overlap check with callbacks |463| `src/physics/arcade/PhysicsGroup.js` | Dynamic physics group with per-member defaults |464| `src/physics/arcade/StaticPhysicsGroup.js` | Static physics group with auto-refresh |465| `src/physics/arcade/components/index.js` | Body component mixins -- Acceleration, Angular, Bounce, Collision, Debug, Drag, Enable, Friction, Gravity, Immovable, Mass, Pushable, Size, Velocity |466| `src/physics/arcade/components/Collision.js` | Collision category/mask methods -- setCollisionCategory, setCollidesWith, addCollidesWith, removeCollidesWith |467| `src/physics/arcade/events/` | Event constants -- COLLIDE, OVERLAP, WORLD_BOUNDS, TILE_COLLIDE, TILE_OVERLAP, WORLD_STEP, PAUSE, RESUME |468| `src/physics/arcade/typedefs/ArcadeWorldConfig.js` | TypeDef for arcade physics config options |