nightshade-api 0.43.0

Procedural high level API for the nightshade game engine
Documentation

nightshade-api

A procedural high level API over the nightshade engine. Write full 3d scenes and small games as straight-line code: free functions, plain data, no trait to implement, no callbacks to wire up.

The engine's own hello world is a State impl with a camera, a sun, and dirty-flagged transform math. Here it is through this crate:

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);
    }
}

open() gives you a lit, navigable scene before your first line runs: procedural sky, sun with shadows, reference grid, orbit camera focused on the origin, prototype textures, and escape to exit. Every knob is one call to change.

Getting Started

[dependencies]

nightshade-api = "0.43.0"

Two ways to run

Own the loop. Setup is code before the loop, game state is locals across iterations. Native only:

let mut app = open();
spawn_floor(&mut app.world, 20.0);
let mut score = 0;
while frame(&mut app) {
    if let Some(coin) = clicked_entity(&app.world) {
        despawn(&mut app.world, coin);
        score += 1;
    }
}

Or hand the engine the loop with run, which also works on wasm. Setup returns your state and 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 rather than unwrapping. For more than one per-frame job, the run! macro takes any number of update systems, run in order over the state setup returns:

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

Each system is a fn(&mut World, &mut Data) or a non-capturing closure of that shape.

Vocabulary

Two verbs carry the lifetime rules. spawn_ is retained and lives until you despawn it. draw_ is immediate and visible for exactly one frame.

Area Calls
Scene spawn_cube, spawn_sphere, spawn_floor, spawn_model, spawn_object, set_visible
Batches spawn_objects (shared material), spawn_instanced (one draw call)
Tags tag, untag, has_tag, tagged, for_each_tagged, for_each_with_position
Looks set_color, set_metallic_roughness, set_emissive, set_texture, load_texture, spawn_decal
Placement set_position, rotate, set_scale, set_parent, position
Animation animate_position, animate_scale, animate_color, shake_camera
Effects emit_fire, emit_smoke, emit_burst, spawn_cloth_sheet
Cameras orbit_camera, fly_camera, first_person, fixed_camera, look_at
Environment set_background, show_grid, set_fog, set_bloom, set_time_of_day, set_exposure, set_depth_of_field
Lights point_light, spot_light, set_sun
Input key_down, key_pressed, wasd, axis, mouse_clicked, mouse_position
Picking clicked_entity, entity_under_cursor, cursor_on_ground
Physics Body::Dynamic on spawn_object, push, set_velocity, raycast, collisions
Joints attach_fixed, attach_hinge, attach_spring, attach_rope
Navigation bake_navmesh, spawn_walker, walk_to, set_walk_speed, stop_walking
Immediate draw_cube, draw_sphere, draw_line, draw_text_3d
Text spawn_text, set_text, spawn_label
Audio load_sound, play_sound, play_sound_at, play_sound_looping, set_volume

spawn_object covers the loaded case in one struct literal:

let ball = spawn_object(world, Object {
    shape: Shape::Sphere,
    position: vec3(0.0, 4.0, 0.0),
    color: RED,
    body: Body::Dynamic { mass: 2.0 },
    ..Object::default()
});

Examples

Short programs that do a lot, in examples/. From the repo root:

just run-example physics_playground

Example Shows
spinning_cube The minimal program
gallery Primitives, a metallic and roughness sweep, emissive, textures
solar_system Orbital motion, parenting, emissive sun, bloom
physics_playground A block tower, projectiles, click interaction, HUD text
fps_walk First person walking with collision in fifteen lines
collector A complete walk and collect game with a win condition
juice Tweens, easing, particle bursts, and camera shake
poster Rendering a composed scene straight to a png, no window
model_viewer Animated glb loading (pass a path, defaults to the fox)
hud Anchored screen text, live fps, runtime setting toggles
day_night Time of day, fog, point lights, a procedural skyline
parity The same workload api-spelled and longhand-spelled, timed

Set NIGHTSHADE_API_FRAMES=120 to exit after a frame budget, which is what just test-examples uses to smoke test every example against the real renderer.

Running on the web

Every example also runs in the browser. Serve one with trunk, the same pipeline the engine's apps use:

just run-example-wasm collector

That relies on the index.html and Trunk.toml in this crate (the canvas page plus the wasm build settings), so a project of your own needs an equivalent pair next to its Cargo.toml. Because the browser owns the loop, the examples use the portable run(setup, update) form rather than the native open()/frame() loop. poster is the one with a split: it writes a png natively and renders the same scene to the canvas on the web.

Dropping down to the engine

Every function takes the engine's World and bottoms out in normal nightshade calls. When a program outgrows the facade, replace one call site at a time. The full engine is re-exported at nightshade_api::nightshade:

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

Features

default = ["audio", "physics", "gamepad", "picking", "navmesh"], each forwarding to the engine features of the same names.

License

Dual-licensed under either of:

at your option.