nightshade-api 0.53.0

Procedural high level API for the nightshade game engine
Documentation
use nightshade_api::prelude::*;
use std::f32::consts::TAU;

const RADIUS: f32 = 5.0;
const TICK_BASE: [f32; 4] = [0.20, 0.22, 0.28, 1.0];

struct Clock {
    running: Stopwatch,
    chase: Timer,
    ticks: Vec<Entity>,
    lit: usize,
    second: Entity,
    minute: Entity,
    hour: Entity,
    scale_slider: Entity,
    pause_button: Entity,
    state_label: Entity,
    elapsed_label: Entity,
}

fn hand(world: &mut World, length: f32, width: f32, color: [f32; 4], glow: [f32; 3]) -> Entity {
    let pivot = spawn_group(world, vec3(0.0, 0.0, 0.0));
    let arm = spawn_object(
        world,
        Object {
            shape: Shape::Cube,
            position: vec3(0.0, length * 0.5, 0.2),
            scale: vec3(width, length, width),
            color,
            ..Object::default()
        },
    );
    set_emissive(world, arm, glow, 3.0);
    set_parent(world, arm, Some(pivot));
    pivot
}

fn main() {
    run(
        |world| {
            set_background(world, Background::Nebula);
            show_grid(world, false);
            set_bloom(world, true);
            orbit_camera(world, vec3(0.0, 0.0, 0.0), 16.0);
            set_orbit_view(world, vec3(0.0, 0.0, 0.0), 16.0, 0.0, 0.0);
            point_light(world, vec3(0.0, 0.0, 6.0), [1.0, 1.0, 1.0], 6.0);

            spawn_object(
                world,
                Object {
                    shape: Shape::Cube,
                    position: vec3(0.0, 0.0, -0.4),
                    scale: vec3(RADIUS * 2.4, RADIUS * 2.4, 0.3),
                    color: [0.05, 0.05, 0.08, 1.0],
                    ..Object::default()
                },
            );

            let mut ticks = Vec::new();
            for index in 0..12 {
                let angle = index as f32 / 12.0 * TAU;
                let major = index % 3 == 0;
                let size = if major { 0.6 } else { 0.35 };
                let tick = spawn_object(
                    world,
                    Object {
                        shape: Shape::Cube,
                        position: vec3(angle.sin() * RADIUS, angle.cos() * RADIUS, 0.0),
                        scale: vec3(size, size, size),
                        color: TICK_BASE,
                        ..Object::default()
                    },
                );
                ticks.push(tick);
            }

            let hour = hand(world, 2.6, 0.22, STEEL, [0.4, 0.5, 0.9]);
            let minute = hand(world, 3.8, 0.16, TEAL, [0.1, 0.7, 0.7]);
            let second = hand(world, 4.6, 0.08, GOLD, [1.0, 0.7, 0.1]);

            let panel = spawn_panel(world, ScreenAnchor::TopLeft, 260.0, 240.0);
            panel_heading(world, panel, "Clock");
            panel_separator(world, panel);
            panel_label(world, panel, "Time scale");
            let scale_slider = panel_slider(world, panel, 0.0, 3.0, 1.0);
            let pause_button = panel_button(world, panel, "Pause / Resume");
            panel_separator(world, panel);
            let state_label = panel_label(world, panel, "running");
            let elapsed_label = panel_label(world, panel, "0.0 s");

            Clock {
                running: Stopwatch::new(),
                chase: Timer::repeating(1.0),
                ticks,
                lit: 0,
                second,
                minute,
                hour,
                scale_slider,
                pause_button,
                state_label,
                elapsed_label,
            }
        },
        |world, state| {
            set_time_scale(world, slider_value(world, state.scale_slider));
            if button_clicked(world, state.pause_button) {
                if is_paused(world) {
                    unpause(world);
                } else {
                    pause(world);
                }
            }

            let step = delta_time(world);
            state.running.tick(step);

            let fired = state.chase.tick(step).times_finished_this_tick();
            if fired > 0 {
                set_emissive(world, state.ticks[state.lit], [0.0, 0.0, 0.0], 0.0);
                state.lit = (state.lit + fired as usize) % state.ticks.len();
                set_emissive(world, state.ticks[state.lit], [1.0, 0.8, 0.25], 6.0);
            }

            let elapsed = state.running.elapsed();
            set_rotation(world, state.second, Vec3::z(), -(elapsed / 12.0) * TAU);
            set_rotation(world, state.minute, Vec3::z(), -(elapsed / 144.0) * TAU);
            set_rotation(world, state.hour, Vec3::z(), -(elapsed / 1728.0) * TAU);

            set_text(
                world,
                state.state_label,
                if is_paused(world) {
                    "paused"
                } else {
                    "running"
                },
            );
            set_text(world, state.elapsed_label, &format!("{elapsed:.1} s"));
        },
    )
    .unwrap();
}