Physics API - SGE Documentation

## 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`