nightshade-api 0.53.0

Procedural high level API for the nightshade game engine
Documentation
//! Navmesh agents and a character controller sharing one floor.
//! Walkers path between waypoints on a baked navmesh, and holding F flips the
//! first person camera into an autopilot that patrols with move_character.

use nightshade_api::prelude::*;

struct Crowd {
    agents: Vec<(Entity, usize)>,
    waypoints: Vec<Vec3>,
    player: Entity,
    patrol: usize,
    hud: Entity,
}

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

fn setup(world: &mut World) -> Crowd {
    spawn_floor(world, 20.0);
    for position in obstacles() {
        let block = spawn_cube(world, position);
        set_scale(world, block, vec3(2.0, 2.0, 2.0));
        set_color(world, block, STEEL);
    }

    bake_navmesh_with(
        world,
        &RecastNavMeshConfig {
            agent_radius: 0.4,
            ..Default::default()
        },
    );

    let waypoints = vec![
        vec3(-12.0, 0.0, -12.0),
        vec3(12.0, 0.0, -12.0),
        vec3(12.0, 0.0, 12.0),
        vec3(-12.0, 0.0, 12.0),
    ];

    let mut agents = Vec::new();
    for index in 0..4 {
        let agent = spawn_walker(world, waypoints[index]);
        set_walk_speed(world, agent, 3.5);
        let target = (index + 1) % waypoints.len();
        walk_to(world, agent, waypoints[target]);
        agents.push((agent, target));
    }

    let player = first_person(world, vec3(0.0, 1.2, 16.0));
    set_controller_speed(world, player, 7.0);
    set_controller_jump(world, player, 6.0);

    let hud = spawn_text(world, "agents", ScreenAnchor::TopLeft);
    spawn_text(
        world,
        "WASD walk   hold F autopilot patrol",
        ScreenAnchor::BottomLeft,
    );

    Crowd {
        agents,
        waypoints,
        player,
        patrol: 0,
        hud,
    }
}

fn obstacles() -> Vec<Vec3> {
    vec![
        vec3(-5.0, 1.0, 0.0),
        vec3(5.0, 1.0, 4.0),
        vec3(0.0, 1.0, -6.0),
    ]
}

fn update(world: &mut World, state: &mut Crowd) {
    for slot in 0..state.agents.len() {
        let (agent, target) = state.agents[slot];
        let here = position(world, agent);
        if (here - state.waypoints[target]).magnitude() < 1.5 {
            if slot == 0 {
                stop_walking(world, agent);
            }
            let next = (target + 1) % state.waypoints.len();
            walk_to(world, agent, state.waypoints[next]);
            state.agents[slot].1 = next;
        }
    }

    if key_down(world, KeyCode::KeyF) {
        let goal = state.waypoints[state.patrol];
        let here = position(world, state.player);
        let to_goal = vec3(goal.x - here.x, 0.0, goal.z - here.z);
        if to_goal.magnitude() < 1.5 {
            state.patrol = (state.patrol + 1) % state.waypoints.len();
        }
        let movement = if to_goal.magnitude() > 0.1 {
            let direction = nalgebra_glm::normalize(&to_goal);
            vec2(direction.x, direction.z)
        } else {
            vec2(0.0, 0.0)
        };
        move_character(
            world,
            state.player,
            movement,
            key_down(world, KeyCode::Space),
        );
    }

    let grounded = is_grounded(world, state.player);
    let speed = controller_velocity(world, state.player).magnitude();
    set_text(
        world,
        state.hud,
        &format!(
            "grounded {}   speed {:.1}   patrol {}",
            grounded, speed, state.patrol
        ),
    );
}