Arcade Physics

For most 2D games, you don't need a full rigid body engine: you need fast, predictable AABB physics with velocities, gravity, collisions and overlaps. That's exactly what the arcade plugin provides (a port of the battle-tested Arcade Physics from Phaser).

The arcade bounce example. Click to add balls!

Enabling the plugin

Add arcade to the plugins of your ceramic.yml:

ceramic.yml
app:
    package: mycompany.myapp
    name: myapp

    plugins:
        - arcade

Bodies on visuals

Any visual can get a physics body with initArcadePhysics(). Once enabled, the visual exposes physics properties directly:

Giving physics to a visual
var ball = new Quad();
ball.size(32, 32);
ball.anchor(0.5, 0.5);
ball.pos(320, 100);
add(ball);

// Enable physics on this visual
ball.initArcadePhysics();

// Velocity, bounce, gravity... The visual follows its body automatically
ball.velocityX = 150;
ball.velocityY = -120;
ball.bounceX = 1;
ball.bounceY = 1;
ball.gravityY = 400;

// Keep it inside the world bounds
ball.arcade.body.collideWorldBounds = true;

By default, the arcade world bounds follow the screen size. You can change that with app.arcade.world.setBounds(x, y, width, height) (and disable the automatic update with app.arcade.autoUpdateWorldBounds = false), like the pixel platformer does with its tilemap size.

An immovable body collides without ever being pushed (platforms, walls, sensors):

Static bodies
platform.initArcadePhysics();
platform.immovable = true;
platform.arcade.body.allowGravity = false;

Collisions and overlaps

Collisions and overlaps are tested explicitly, usually in the arcade update callback. The difference between the two:

  • collide() separates the bodies (they push each other, bounce...)
  • overlap() only detects that bodies intersect, without touching them
Testing collisions each frame
var world = app.arcade.world;

app.arcade.onUpdate(this, delta -> {

    // Make every member of the group collide with the others
    world.collide(balls);

    // Make the player collide with the tilemap or a platform
    world.collide(player, platform);

    // Only detect (without moving anything) which balls overlap the zone
    for (ball in balls.items) {
        if (world.overlap(ball, zone)) {
            trace('Ball inside zone: ' + ball);
        }
    }

});

A Group<Visual> (from ceramic.Group) can be passed directly to collide()/overlap(): all its members are tested together. This is the idiomatic way to handle many objects of the same kind.

You can also listen to collision events on the visual itself:

Collision events
player.onCollide(this, (visual1, visual2) -> {
    trace(visual1 + ' collided with ' + visual2);
});

Where to look next

  • Arcade bounce: bodies, groups, collide vs overlap, in isolation
  • Pixel platformer: a real platformer combining arcade physics with a tilemap, a state machine and a camera
  • The nape plugin offers full rigid body physics (rotations, joints, materials) when arcade's AABB model is not enough

Continue reading ➔ Tilemaps