Arcade Physics
Fast AABB physics for platformers, shmups, and most arcade-style games — bodies, collisions, groups, and the pitfalls that trip new users.
Arcade is Phaser 4’s fast physics system. It models everything as axis-aligned bounding boxes (AABBs) or circles — no rotation, no rigid-body stacking, no joints. In exchange you get a system fast enough to step hundreds of bodies per frame on a phone.
When to use Arcade
Use Arcade when all of these are true:
- You don’t need rotational physics (“a tilted box rests on a slope”).
- You don’t need stacking (“boxes pile up correctly”).
- You don’t need joints, constraints, or compound bodies.
Most platformers, top-down adventures, shmups, brick-breakers, and casual puzzle games fit. If you find yourself fighting Arcade for rigid-body behavior, switch to Matter — don’t try to fake it.
Enabling Arcade
Set it in the game config:
new Phaser.Game({
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false,
},
},
scene: { /* ... */ },
});
Inside a scene, this.physics is the world; this.physics.add is the factory.
Creating bodies
Two kinds: dynamic (move, collide, respond to gravity) and static (immovable; they exist to be collided with).
// Dynamic — has velocity, acceleration, gravity.
const player = this.physics.add.sprite(100, 100, 'player');
// Static — fixed in place. Cheap to collide against.
const platform = this.physics.add.staticImage(400, 568, 'ground');
Promote a regular game object after the fact with this.physics.add.existing(obj, isStatic).
Movement
Three knobs control motion:
player.setVelocity(120, 0); // pixels per second
player.setAcceleration(0, 400); // pixels per second^2
player.setDrag(100); // velocity decay per second
player.setMaxVelocity(300, 800); // clamp
Prefer setVelocity over mutating x directly — direct position writes bypass collision resolution and you’ll tunnel through walls.
Collisions vs. overlaps
Two relationships, with different semantics:
- Collider — Phaser separates the bodies and stops them from interpenetrating. Use for walls, floors, enemies that should physically push the player.
- Overlap — Phaser detects the intersection but lets the bodies pass through. Use for pickups, triggers, hitboxes.
this.physics.add.collider(player, platforms);
this.physics.add.overlap(player, coins, (_p, coin) => coin.destroy());
Both take an optional process callback (return false to skip resolution for that pair) and a context.
Groups
A group is a typed pool of physics-enabled objects. Use one for any set of objects you’ll collide as a unit:
const enemies = this.physics.add.group({
defaultKey: 'enemy',
maxSize: 50,
collideWorldBounds: true,
});
enemies.get(400, 0).setVelocityY(80);
this.physics.add.collider(enemies, platforms);
this.physics.add.overlap(player, enemies, this.onHit, undefined, this);
group.get() reuses an inactive member if one exists, or creates a new one up to maxSize. Cheap.
A live example
Three bouncing balls colliding with each other and the world bounds.
Notice the setCircle call — Arcade defaults to a rectangular body even for round sprites. Calling setCircle after creation switches the body to a circle of the given radius. For circular sprites, this is almost always what you want.
Common pitfalls
Tunneling. A fast-moving body can skip past a thin wall in a single frame. Arcade physics has no continuous collision detection, so mitigations are geometric:
- Make walls thicker than the body’s maximum per-frame travel (
speed × delta). - Cap maximum velocity (
setMaxVelocity) for objects that don’t need to be arbitrarily fast. - For bullet-like objects, replace the body with an overlap-with-process callback that ray-casts the bullet’s path each frame.
Sub-pixel jitter. Mixing setVelocity with manual x writes causes flicker. Pick one and stick to it within a body’s lifetime.
Body offset vs. sprite offset. The body’s bounding box may not match the sprite’s visible bounds (think: a character with a long ponytail). Use body.setSize(w, h) and body.setOffset(x, y) to tighten the box.
Group iteration during collision. Mutating a group from inside a collision callback (e.g. spawning a new member) is safe; iterating the group with a for loop while the same loop’s callback mutates it is not. Prefer group.children.iterate(...).
Debug rendering
Set arcade.debug: true in the config to draw body outlines and velocity vectors. Indispensable while you’re tuning collisions.
Related
- Matter Physics — when AABB isn’t enough.
- Game Loop — what “per second” actually means.
- Game Objects — what
physics.addis wrapping.