nightshade-api 0.56.0

Procedural high level API for the nightshade game engine
Documentation
//! Game components in a member world of the engine group.
//!
//! The engine `World` renders and owns the transforms. The game declares its
//! own components with `dynamic_schema!` and registers them as a third member
//! world, sharing entity identity with the render entities: every bouncing
//! cube carries a `Position` and `Velocity` in the member world on the same
//! entity the engine draws.
//!
//! The frame is two passes. `integrate` advances the member world on its own
//! data. `render_sync` then pushes the result into the engine with
//! `set_position`, an ordinary facade call on the same entities.

use nightshade_api::prelude::*;

freecs::dynamic_schema! {
    pub fn register_game_components {
        position: Position => POSITION,
        velocity: Velocity => VELOCITY,
    }
}

#[derive(Debug, Clone, Copy, Default)]
pub struct Position(Vec3);

#[derive(Debug, Clone, Copy, Default)]
pub struct Velocity(Vec3);

const COUNT: usize = 24;
const BOUNDS: f32 = 6.0;
const HEIGHT: f32 = 0.6;

fn main() {
    run(setup, |world, _| {
        integrate(world);
        render_sync(world);
    })
    .unwrap();
}

fn setup(world: &mut World) {
    world.ecs.add_world_at(GAME, register_game_components());

    let floor = spawn_floor(world, BOUNDS + 1.0);
    set_texture(world, floor, "checkerboard");
    set_texture_tiling(world, floor, 6.0);
    orbit_camera(world, vec3(0.0, 0.0, 0.0), 18.0);

    for index in 0..COUNT {
        let fraction = index as f32 / COUNT as f32;
        let angle = fraction * std::f32::consts::TAU;
        let position = vec3(angle.cos() * 4.0, HEIGHT, angle.sin() * 4.0);
        let velocity = vec3(angle.sin(), 0.0, -angle.cos()) * 3.0;

        let cube = spawn_cube(world, position);
        set_color(
            world,
            cube,
            [0.25 + 0.7 * fraction, 0.5, 1.0 - 0.6 * fraction, 1.0],
        );

        world.set(cube, Position(position));
        world.set(cube, Velocity(velocity));
    }
}

fn integrate(world: &mut World) {
    let delta = delta_time(world);
    world
        .ecs
        .query::<(&mut Position, &mut Velocity)>()
        .for_each(|_, (position, velocity)| {
            let mut next_velocity = velocity.0;
            let mut next_position = position.0 + next_velocity * delta;
            for axis in [0, 2] {
                if next_position[axis].abs() > BOUNDS {
                    next_position[axis] = next_position[axis].clamp(-BOUNDS, BOUNDS);
                    next_velocity[axis] = -next_velocity[axis];
                }
            }
            position.0 = next_position;
            velocity.0 = next_velocity;
        });
}

fn render_sync(world: &mut World) {
    let positions: Vec<(Entity, Vec3)> = world
        .query_ref::<&Position>()
        .iter()
        .map(|(entity, position)| (entity, position.0))
        .collect();
    for (entity, position) in positions {
        set_position(world, entity, position);
    }
}