Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Physics API

The physics API is based on rapier2d.

Create a PhysicsWorld with PhysicsWorld::new(). This returns a WorldRef that you call .update() on each frame to step the simulation.

Rapier2D uses positive y up, but SGE uses positive y down, so coordinates converted before being returned, so that the position in the world can be the same as the position onscreen. A conversion rate of 100 pixels per Rapier2D meter is used.

#[main("Physics")]
fn main() {
    let mut world = PhysicsWorld::new();

    loop {
        world.update();

        if should_quit() {
            break;
        }

        next_frame().await;
    }
}

Creating Objects

There are three types of physics objects:

  • Dynamic: affected by gravity and forces, collides with everything.
  • Fixed: immovable, used for walls, floors, and static geometry.
  • Kinematic: moved manually (e.g. via set_position), but participates in collision detection.
#![allow(unused)]
fn main() {
let dynamic_obj = world.create_dynamic(Bounds::Circle(20.0));
let wall = world.create_fixed(Bounds::Rect(Vec2::new(1000.0, 50.0)));
let platform = world.create_kinematic(Bounds::Rect(Vec2::new(200.0, 20.0)));
}

Each of these also has a _with variant that accepts a ColliderConfig for customizing physical material properties:

#![allow(unused)]
fn main() {
let bouncy = world.create_dynamic_with(
    Bounds::Circle(15.0),
    ColliderConfig::default()
        .restitution(0.9)
        .friction(0.1)
        .density(2.0),
);
}

Objects can be removed with .remove().

Once created, objects can be manipulated using the methods on the ObjectRef struct.

#![allow(unused)]
fn main() {
let obj = world
    .create_dynamic(Bounds::Circle(15.0))
    .with_position(Vec2::new(200.0, 100.0))
    .with_velocity(Vec2::new(150.0, 0.0))
    .with_ccd(); // continuous collision detection

// Reading state
let pos = obj.get_position();
let vel = obj.get_velocity();
let rot = obj.get_rotation(); // all rotations are in radians
let mass = obj.get_mass();

obj.set_position(Vec2::new(300.0, 200.0));
obj.set_velocity(Vec2::new(0.0, -100.0));
obj.set_rotation(std::f32::consts::PI * 0.25);

obj.add_velocity(Vec2::new(50.0, 0.0));
obj.add_force(Vec2::new(0.0, -500.0));
obj.move_by(Vec2::new(5.0, 0.0));

obj.set_angvel(2.0); // rad/s
obj.add_angvel(0.5);
}

Bounds

Bounds describes the shape of a collider. The available variants are:

  • Bounds::Circle(radius)
  • Bounds::Rect(Vec2): full size from center
  • Bounds::Capsule { half_height, radius }: vertical capsule
  • Bounds::CapsuleX { half_width, radius }: horizontal capsule
  • Bounds::Triangle(a, b, c): relative to the object’s position
  • Bounds::ConvexHull(points): computed from a point cloud
  • Bounds::Polyline(points): a series of connected line segments, useful for terrain
  • Bounds::Line { a, b }
  • Bounds::Compound(children): multiple shapes combined, each with an offset

Sensors

Sensors are not physical objects, other objects will pass right through them, but they still check if something is colliding with them.

#![allow(unused)]
fn main() {
let sensor = world
    .create_fixed_with(Bounds::Circle(80.0), ColliderConfig::default().sensor(true))
    .with_position(Vec2::new(400.0, 300.0));

if sensor.is_colliding() {
    // something is inside the sensor
}
}

Collisions

#![allow(unused)]
fn main() {
if obj.is_colliding() { ... }

if obj.is_colliding_with(other) { ... }

if let Some(points) = obj.check_collision_with(other) {
    let normal = points.normal; 
    let depth = points.depth; 
}

for info in obj.collisions() {
    let other: ObjectRef = info.other;
    let normal: Vec2 = info.points.normal;
    match info.event {
        CollisionType::Started => { /* wasn't colliding last frame, colliding now */  }
        CollisionType::Ongoing => { /* colliding last frame, still colliding now */  }
        CollisionType::Stopped => { /* was colliding last frame, not anymore */ }
    }
}
}

World Settings

#![allow(unused)]
fn main() {
world.set_gravity(980.0);  // in pixels/world units, so 9.8 will be very slow
let g = world.get_gravity();
}

Debug Visualization

You can draw outlines of all colliders and collision normals for debugging:

#![allow(unused)]
fn main() {
world.draw_colliders();
// or
world.draw_colliders_world();
}

Colliders are drawn in red, objects currently in collision are highlighted in yellow with arrows showing the collision normals.


See: physics module

See also: /examples/physics.rs