Phaser 4 Arcade Physics
Add movement and collision to a Phaser game with the lightweight Arcade
Physics engine (AABB rectangles and circles only). Targets Phaser 4.2 for
new projects; inspect the installed major before editing an existing project.
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.
Core workflow
- 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.
- 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).
- 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).
- 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.
- Group many objects. Use
this.physics.add.group() (dynamic) or
staticGroup() (platforms) so one collider call handles all members.
- Check ground contact with
body.onFloor() / body.blocked.down before
jumping. Run with debug: true and confirm bodies, contacts, and bounds.
Patterns
1. Enable Arcade Physics (game config)
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);
2. Top-down movement (velocity from input)
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
}
3. Platformer jump (gravity + ground check)
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 // 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);
}
4. Colliders vs overlaps (separate vs detect)
// 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);
});
5. A group of moving objects
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);
}
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.
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.
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.
1---
2name: phaser-arcade-physics
3description: >
4 Use Phaser 4 Arcade Physics: enable the world, give sprites bodies, set
5 velocity/acceleration/gravity, and resolve collisions with colliders, overlaps,
6 groups, and world bounds. Use when a Phaser game needs movement or collisions —
7 when the user mentions Arcade Physics, this.physics, setVelocity, collider,
8 overlap, gravity, onFloor, or a platformer/top-down controller. For game config,
9 scenes, and the loader use phaser-core.
10---
11
12# Phaser 4 Arcade Physics
13
14Add movement and collision to a Phaser game with the lightweight **Arcade
15Physics** engine (AABB rectangles and circles only). Targets **Phaser 4.2** for
16new projects; inspect the installed major before editing an existing project.
17
18## When to use
19
20- Use for top-down or platformer movement, velocity/acceleration/gravity, bouncing,
21 world bounds, and collision/overlap resolution between sprites, groups, and tiles.
22- Use when the scene enables `physics: { default: 'arcade' }` and code calls
23 `this.physics.add.*`, `body.setVelocity`, or `this.physics.add.collider`.
24
25**When *not* to use:** the `Game` config, scene structure, asset loading, or
26cameras → use `phaser-core`. Hinges, springs, complex polygons, or stacking
27rigid bodies → use Matter physics (a different engine; Arcade and Matter bodies
28do not interact). For engine-agnostic feel tuning see `physics-tuning`.
29
30## Core workflow
31
321. **Enable the world.** Set `physics: { default: 'arcade', arcade: { gravity:
33 {...}, debug: true } }` in the game or scene config. Turn `debug` on while
34 building to see body outlines and velocity vectors.
352. **Give a sprite a body.** Create it with `this.physics.add.sprite(...)` (dynamic)
36 or `this.physics.add.staticImage(...)` (static), or attach to an existing object
37 with `this.physics.add.existing(obj)`.
383. **Drive it through the body, not by setting `x`/`y`.** Use `setVelocity`,
39 `setAcceleration`, gravity, `setBounce`, and `setCollideWorldBounds`. The engine
40 integrates position from velocity each step (already frame-rate independent).
414. **Resolve interactions.** `this.physics.add.collider(a, b)` separates bodies;
42 `this.physics.add.overlap(a, b, cb)` detects without separating (pickups,
43 triggers). Pass a callback to react.
445. **Group many objects.** Use `this.physics.add.group()` (dynamic) or
45 `staticGroup()` (platforms) so one collider call handles all members.
466. **Check ground contact** with `body.onFloor()` / `body.blocked.down` before
47 jumping. Run with `debug: true` and confirm bodies, contacts, and bounds.
48
49## Patterns
50
51### 1. Enable Arcade Physics (game config)
52
53```js
54const config = {
55 type: Phaser.AUTO,
56 width: 800, height: 600,
57 physics: {
58 default: 'arcade',
59 arcade: {
60 gravity: { x: 0, y: 600 }, // top-down? use { x: 0, y: 0 }
61 debug: false // true to draw bodies + velocity while building
62 }
63 },
64 scene: [PlayScene]
65};
66new Phaser.Game(config);
67```
68
69### 2. Top-down movement (velocity from input)
70
71```js
72create() {
73 this.player = this.physics.add.sprite(400, 300, 'player');
74 this.player.setCollideWorldBounds(true);
75 this.cursors = this.input.keyboard.createCursorKeys();
76}
77
78update() {
79 const speed = 220;
80 const body = this.player.body;
81 body.setVelocity(0); // reset each frame
82 if (this.cursors.left.isDown) body.setVelocityX(-speed);
83 if (this.cursors.right.isDown) body.setVelocityX(speed);
84 if (this.cursors.up.isDown) body.setVelocityY(-speed);
85 if (this.cursors.down.isDown) body.setVelocityY(speed);
86 body.velocity.normalize().scale(speed); // keep diagonals same speed
87}
88```
89
90### 3. Platformer jump (gravity + ground check)
91
92```js
93create() {
94 this.player = this.physics.add.sprite(100, 450, 'player');
95 this.player.setCollideWorldBounds(true);
96
97 // Static platforms: one body each, never moved by collisions.
98 this.platforms = this.physics.add.staticGroup();
99 this.platforms.create(400, 568, 'ground');
100 this.physics.add.collider(this.player, this.platforms);
101
102 this.cursors = this.input.keyboard.createCursorKeys();
103}
104
105update() {
106 const onGround = this.player.body.blocked.down; // or this.player.body.onFloor()
107 if (this.cursors.left.isDown) this.player.setVelocityX(-160);
108 else if (this.cursors.right.isDown) this.player.setVelocityX(160);
109 else this.player.setVelocityX(0);
110
111 if (this.cursors.up.isDown && onGround) this.player.setVelocityY(-450);
112}
113```
114
115### 4. Colliders vs overlaps (separate vs detect)
116
117```js
118// Push apart and react: player vs enemies.
119this.physics.add.collider(this.player, this.enemies, (player, enemy) => {
120 this.handleHit(player, enemy);
121});
122
123// Detect without pushing: collect coins. The 4th arg is an optional
124// process callback returning a boolean to filter pairs before the main callback.
125this.physics.add.overlap(this.player, this.coins, (player, coin) => {
126 coin.disableBody(true, true); // deactivate + hide
127 this.registry.inc('score', 10);
128});
129```
130
131### 5. A group of moving objects
132
133```js
134this.bullets = this.physics.add.group({
135 defaultKey: 'bullet',
136 maxSize: 30 // pool size; reuse instead of allocating
137});
138
139fire(x, y) {
140 const bullet = this.bullets.get(x, y); // reuses a dead bullet if available
141 if (!bullet) return;
142 bullet.enableBody(true, x, y, true, true);
143 bullet.setVelocityY(-500);
144}
145```
146
147## Pitfalls
148
149- **Sprite ignores physics** → it was added with `this.add.sprite` instead of
150 `this.physics.add.sprite` (or `this.physics.add.existing(obj)`), so it has no body.
151- **Setting `sprite.x` directly fights the engine** → move dynamic bodies with
152 `setVelocity`/`setAcceleration`. Direct position writes can tunnel through colliders.
153- **Diagonal movement is faster** → independent X and Y velocities add up; normalise
154 the velocity vector and rescale to the intended speed.
155- **Platforms get pushed by the player** → use a `staticGroup`, or set
156 `body.setImmovable(true)` on a dynamic platform.
157- **`onFloor()` is always false** → the body needs something to collide with; add the
158 `collider` against the ground/platforms before checking, and ensure gravity is on.
159- **Moved a static body but collisions are stale** → static bodies don't auto-sync;
160 call `body.updateFromGameObject()` (or `refreshBody()` on the game object).
161- **Collider added every frame** → register `collider`/`overlap` once in `create`,
162 not in `update`.
163
164## References
165
166- For body anatomy and tuning (drag, bounce, max velocity, custom `setSize`/
167 `setCircle`/`setOffset` hitboxes, collision categories/masks, and `worldbounds`
168 events), read `references/bodies-and-collision.md`.
169
170## Related skills
171
172- `phaser-core` — game config, scenes, loader, cameras (the prerequisite setup).
173- `physics-tuning` — engine-agnostic feel (fixed timestep, tunneling, jitter).
174- `platformer` / `tower-defense` — genres that compose this skill.
175- `level-design` — laying out tile/platform geometry these bodies collide with.