Skip to main content

Crate nightshade_api

Crate nightshade_api 

Source
Expand description

§nightshade-api

A procedural high level API over the nightshade engine. Write a full 3d scene or a small game as straight-line code with free functions and plain data. No trait to implement, no callbacks to wire up, no ECS knowledge required to get started.

A spinning cube:

use nightshade_api::prelude::*;

fn main() {
    let mut app = open();
    let cube = spawn_cube(&mut app.world, vec3(0.0, 0.5, 0.0));
    while frame(&mut app) {
        let step = delta_time(&app.world);
        rotate(&mut app.world, cube, Vec3::y(), step);
    }
}

Add to Cargo.toml:

nightshade-api = "0.48"

§What you get for free

prelude::open gives you a window with a sky, a sun with shadows, a reference grid, an orbit camera focused on the origin, prototype textures like "checkerboard", and escape to exit. Every program starts from a lit, navigable scene. Override any of it with one call: prelude::set_background, prelude::show_grid, prelude::fly_camera, prelude::set_sun.

§The two entry points

Own the loop (native only). Setup is ordinary code before the loop, state is ordinary locals across loop iterations:

let mut app = open();
let mut score = 0;
while frame(&mut app) {
    score += 1;
}

Or hand the engine the loop with run, which also works on wasm. Setup returns your state, the update closure receives it back every frame:

run(
    |world| spawn_cube(world, vec3(0.0, 0.5, 0.0)),
    |world, cube| {
        let step = delta_time(world);
        rotate(world, *cube, Vec3::y(), step);
    },
)
.unwrap();

run returns a Result, so a real main returns it. For several per-frame jobs, the run! macro takes any number of update systems:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    run!(setup, handle_input, move_player, check_collisions)
}

For immediate-mode UI, enable the egui feature, call enable_egui once in setup, then draw from your update closure by pulling the frame’s context with egui_context. No extra closure: it composes with run and run! as is. egui is re-exported from the prelude.

run(
    |world| { enable_egui(world); spawn_cube(world, vec3(0.0, 0.5, 0.0)) },
    |world, cube| {
        rotate(world, *cube, Vec3::y(), delta_time(world));
        if let Some(ctx) = egui_context(world) {
            egui::Window::new("Inspector").show(&ctx, |ui| ui.label(format!("{cube:?}")));
        }
    },
)
.unwrap();

§Vocabulary

Two verbs carry the lifetime rules. spawn_ is retained: the thing exists until you prelude::despawn it. draw_ is immediate: visible for exactly one frame, redraw it every frame you want it on screen.

§Reading the scene back

The setters have readers, which is what a tool that edits a scene rather than just building one needs. prelude::describe_entity gathers an entity’s whole editable state, prelude::get_color and friends read one field, prelude::scene_tree and prelude::children walk the hierarchy, prelude::list_materials reads the shared material registry, and prelude::bounds_of with prelude::frame_entities measure and frame a selection. prelude::save_scene and prelude::load_scene round-trip the whole world to bytes. Components are added, removed, and snapshotted for undo by prelude::ComponentKind, the surface the standalone editor is built on.

§Commands

Every call also has a data form. prelude::Command is a serde enum with one variant per function, prelude::submit_command runs one, and prelude::submit_commands runs a batch where a later command can name an entity an earlier one produced with prelude::Ref::Result, so one batch builds and wires up a scene. The enum is the wire format a binding targets: build Command values, read prelude::CommandReply back, with the json schema from prelude::command_schema. The free functions stay the real implementations and the dispatch forwards to them. See the commands example and docs/COMMAND_API.md.

§Dropping down to the engine

Every function here takes the real engine prelude::World and bottoms out in normal nightshade calls. Nothing is hidden behind a wrapper type, so when a program outgrows the facade you replace one call site at a time. The full engine is re-exported at nightshade, one path away:

use nightshade_api::nightshade::prelude::*;

§Examples

The examples/ directory is the tour. Run one with just run-example solar_system from the repo root, or cargo run -r -p nightshade-api --example solar_system. Every example also runs in the browser with just run-example-wasm solar_system, which serves it through trunk.

Re-exports§

pub use nightshade;

Modules§

prelude
Everything in one import.

Macros§

run
Runs a program from a setup expression and one or more per-frame update expressions, the variadic form of run. Setup runs once and returns your state; each update runs every frame, in the order given, and receives that state. Returns the same Result as run, so main can return it.