nightshade-api 0.51.0

Procedural high level API for the nightshade game engine
Documentation
//! Physics depth: live-tuned bodies, joints, sensors, contacts, and cloth.
//! Sliders retune one cube, Space kicks it, a hinge door and a spring bob swing,
//! a sensor pad reports overlaps, and a windblown banner resets on R.

use nightshade_api::prelude::*;

struct Lab {
    cube: Entity,
    pad: Entity,
    banner: Entity,
    friction: Entity,
    restitution: Entity,
    gravity: Entity,
    mass: Entity,
    hud: Entity,
    impacts: u32,
}

fn main() {
    run(setup, update).unwrap();
}

fn setup(world: &mut World) -> Lab {
    spawn_floor(world, 24.0);

    let cube = spawn_object(
        world,
        Object {
            position: vec3(-2.0, 6.0, 0.0),
            color: GREEN,
            body: Body::Dynamic { mass: 1.0 },
            ..Object::default()
        },
    );
    for index in 1..5 {
        spawn_object(
            world,
            Object {
                position: vec3(index as f32 - 2.0, 6.0 + index as f32, 0.0),
                color: GREEN,
                body: Body::Dynamic { mass: 1.0 },
                ..Object::default()
            },
        );
    }

    spawn_capsule_body(world, vec3(-6.0, 4.0, 0.0), 0.5, 0.4, 1.0, BLUE);
    spawn_cylinder_body(world, vec3(6.0, 4.0, 0.0), 0.5, 0.5, 1.0, ORANGE);

    let pad = spawn_object(
        world,
        Object {
            position: vec3(0.0, 0.1, 6.0),
            scale: vec3(2.0, 0.2, 2.0),
            color: TEAL,
            body: Body::Static,
            ..Object::default()
        },
    );
    make_sensor(world, pad);

    build_door(world);
    build_spring(world);

    let banner = spawn_cloth(world, vec3(-3.0, 6.0, -8.0), 6.0, 4.0, 16, 12);
    set_wind(world, vec3(1.0, 0.0, 0.3), 6.0);

    let panel = spawn_panel(world, ScreenAnchor::TopRight, 240.0, 200.0);
    panel_label(world, panel, "friction restitution gravity mass");
    let friction = panel_slider(world, panel, 0.0, 2.0, 0.7);
    let restitution = panel_slider(world, panel, 0.0, 1.0, 0.2);
    let gravity = panel_slider(world, panel, -1.0, 2.0, 1.0);
    let mass = panel_slider(world, panel, 0.2, 8.0, 1.0);

    let hud_panel = spawn_panel(world, ScreenAnchor::TopLeft, 380.0, 96.0);
    let hud = panel_label(world, hud_panel, "physics");
    set_text_size(world, hud, 16.0);
    spawn_text(
        world,
        "Space kick cube   R reset banner",
        ScreenAnchor::BottomLeft,
    );

    Lab {
        cube,
        pad,
        banner,
        friction,
        restitution,
        gravity,
        mass,
        hud,
        impacts: 0,
    }
}

fn build_door(world: &mut World) {
    let frame = spawn_object(
        world,
        Object {
            position: vec3(-9.0, 2.0, 4.0),
            scale: vec3(0.3, 4.0, 0.3),
            color: STEEL,
            body: Body::Static,
            ..Object::default()
        },
    );
    let door = spawn_object(
        world,
        Object {
            position: vec3(-7.5, 2.0, 4.0),
            scale: vec3(3.0, 3.5, 0.2),
            color: GOLD,
            body: Body::Dynamic { mass: 2.0 },
            ..Object::default()
        },
    );
    attach_hinge(world, frame, door, JointAxisDirection::Y);
}

fn build_spring(world: &mut World) {
    let anchor = spawn_object(
        world,
        Object {
            position: vec3(9.0, 7.0, 4.0),
            scale: vec3(0.3, 0.3, 0.3),
            color: STEEL,
            body: Body::Static,
            ..Object::default()
        },
    );
    let bob = spawn_object(
        world,
        Object {
            shape: Shape::Sphere,
            position: vec3(9.0, 4.0, 4.0),
            scale: vec3(0.6, 0.6, 0.6),
            color: PINK,
            body: Body::Dynamic { mass: 1.5 },
        },
    );
    attach_spring(world, anchor, bob, 3.0, 40.0, 1.5);
}

fn update(world: &mut World, state: &mut Lab) {
    set_friction(world, state.cube, slider_value(world, state.friction));
    set_restitution(world, state.cube, slider_value(world, state.restitution));
    set_gravity_scale(world, state.cube, slider_value(world, state.gravity));
    set_mass(world, state.cube, slider_value(world, state.mass));

    if key_pressed(world, KeyCode::Space) {
        apply_force(world, state.cube, vec3(0.0, 400.0, 0.0));
        apply_torque(world, state.cube, vec3(0.0, 40.0, 0.0));
        set_angular_velocity(world, state.cube, vec3(0.0, 6.0, 0.0));
    }
    if key_pressed(world, KeyCode::KeyR) {
        reset_cloth(world, state.banner);
    }

    if !collisions(world).is_empty() {
        state.impacts += collisions(world).len() as u32;
    }

    let on_pad = overlap_sphere(world, position(world, state.pad), 2.0).len();
    let speed = velocity(world, state.cube).unwrap_or_default().magnitude();
    let spin = angular_velocity(world, state.cube)
        .unwrap_or_default()
        .magnitude();
    set_text(
        world,
        state.hud,
        &format!(
            "impacts {}   on pad {}   speed {:.1}   spin {:.1}",
            state.impacts, on_pad, speed, spin
        ),
    );
}