Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

World & Resources

The World is the single value that holds every piece of state in a Nightshade game. It contains the ECS storage (entities and components) and the resources (global singletons). Almost every function in the engine takes &mut World or &World as its first argument.

World Structure

The World is generated by the freecs::ecs! macro and reduces to two fields.

#![allow(unused)]
fn main() {
pub struct World {
    pub entities: EntityStorage,
    pub resources: Resources,
}
}

entities is the archetype-based component storage. resources is a flat struct of singleton values shared across every system.

Resources

Resources are the global state of the engine. Each field is a domain. Window state, input state, the physics world, the asset caches, the active camera, the deferred command queue, and so on.

#![allow(unused)]
fn main() {
pub struct Resources {
    pub world_id: u64,
    pub is_runtime: bool,
    pub window: Window,
    pub secondary_windows: SecondaryWindows,
    pub user_interface: UserInterface,
    pub graphics: Graphics,
    pub input: Input,
    #[cfg(feature = "audio")]
    pub audio: AudioEngine,
    #[cfg(feature = "physics")]
    pub physics: PhysicsWorld,
    pub navmesh: NavMeshWorld,
    pub text_cache: TextCache,
    pub mesh_cache: MeshCache,
    pub animation_cache: AnimationCache,
    pub prefab_cache: PrefabCache,
    pub material_registry: MaterialRegistry,
    pub texture_cache: TextureCache,
    pub pending_font_loads: Vec<PendingFontLoad>,
    pub active_camera: Option<Entity>,
    pub event_bus: EventBus,
    pub command_queue: Vec<WorldCommand>,
    pub transform_dirty_entities: Vec<Entity>,
    pub children_cache: HashMap<Entity, Vec<Entity>>,
    pub children_cache_valid: bool,
    pub cleanup_frame_counter: u64,
    pub dropped_files: Vec<DroppedFile>,
    pub skinning_offsets: HashMap<Entity, usize>,
    pub total_skinning_joints: u32,
    #[cfg(all(feature = "steam", not(target_arch = "wasm32")))]
    pub steam: SteamResources,
    #[cfg(feature = "physics")]
    pub picking_world: PickingWorld,
    pub gpu_picking: GpuPicking,
    pub mesh_render_state: MeshRenderState,
    #[cfg(feature = "scene_graph")]
    pub asset_registry: AssetRegistry,
    pub entity_names: HashMap<String, Entity>,
    pub entity_tags: HashMap<Entity, Vec<String>>,
    pub entity_blueprints: HashMap<Entity, Vec<EntityBlueprint>>,
    pub pending_particle_textures: Vec<ParticleTextureUpload>,
    pub ibl_views: IblViews,
    pub retained_ui: RetainedUiState,
    pub frame_schedule: FrameSchedule,
}
}

There is no separate Time resource. Timing values live on world.resources.window.timing alongside the rest of the window state.

Accessing Resources

Resources are reached through world.resources.<field>. The shape is consistent across the engine, and the borrow rules are the standard Rust ones. Two systems cannot mutably borrow the same resource at the same time.

#![allow(unused)]
fn main() {
fn run_systems(&mut self, world: &mut World) {
    let dt = world.resources.window.timing.delta_time;

    if world.resources.input.keyboard.is_key_pressed(KeyCode::Space) {
        self.jump();
    }

    world.resources.graphics.bloom_enabled = true;
    world.resources.graphics.bloom_intensity = 0.5;
}
}

Common Resources

Timing

world.resources.window.timing is a WindowTiming struct. The relevant fields for game logic are delta_time, frames_per_second, frame_counter, and uptime_milliseconds.

#![allow(unused)]
fn main() {
let dt = world.resources.window.timing.delta_time;
let fps = world.resources.window.timing.frames_per_second;
let elapsed = world.resources.window.timing.uptime_milliseconds as f32 / 1000.0;
}
#![allow(unused)]
fn main() {
pub struct WindowTiming {
    pub frames_per_second: f32,
    pub delta_time: f32,
    pub raw_delta_time: f32,
    pub time_speed: f32,
    pub last_frame_start_instant: Option<web_time::Instant>,
    pub current_frame_start_instant: Option<web_time::Instant>,
    pub initial_frame_start_instant: Option<web_time::Instant>,
    pub frame_counter: u32,
    pub uptime_milliseconds: u64,
}
}

Input

The input resource holds keyboard, mouse, gamepad, and touch state. Polling-style queries read flags. Event-style queries drain the events vec.

#![allow(unused)]
fn main() {
if world.resources.input.keyboard.is_key_pressed(KeyCode::KeyW) {
    move_forward();
}

let mouse_pos = world.resources.input.mouse.position;

if world.resources.input.mouse.state.contains(MouseState::LEFT_JUST_PRESSED) {
    shoot();
}
}

Graphics

world.resources.graphics controls the global rendering toggles. Atmosphere, bloom, SSAO, tonemapping, debug grid.

#![allow(unused)]
fn main() {
world.resources.graphics.show_grid = true;
world.resources.graphics.atmosphere = Atmosphere::Sky;
world.resources.graphics.bloom_enabled = true;
world.resources.graphics.ssao_enabled = true;

world.resources.graphics.color_grading.tonemap_algorithm = TonemapAlgorithm::Aces;
}

Active Camera

world.resources.active_camera: Option<Entity> is the camera that drives the main viewport. Setting it changes what the renderer draws. Reading from it after rendering tells you which entity was used.

#![allow(unused)]
fn main() {
world.resources.active_camera = Some(camera_entity);

if let Some(camera) = world.resources.active_camera {
    let transform = world.core.get_global_transform(camera);
}
}

World Commands

Some operations cannot run synchronously from inside a system. GPU resource creation, texture uploads, recursive despawns, and screenshot capture all need to be deferred to a safe point in the frame. These go through world.queue_command, which pushes a WorldCommand onto world.resources.command_queue.

#![allow(unused)]
fn main() {
world.queue_command(WorldCommand::LoadTexture {
    name: "my_texture".to_string(),
    rgba_data: texture_bytes,
    width: 256,
    height: 256,
});

world.queue_command(WorldCommand::DespawnRecursive { entity });
world.queue_command(WorldCommand::LoadHdrSkybox { hdr_data });
world.queue_command(WorldCommand::CaptureScreenshot { path: None });
}

The queue is drained during the render phase. Commands run in submission order.

For a recursive despawn that has to happen immediately rather than at the end of the frame, call the helper directly:

#![allow(unused)]
fn main() {
despawn_recursive_immediate(world, entity);
}

The cost of despawn_recursive_immediate is that the change is visible to every system that runs after it in the current frame, including the engine schedule. Use it when the deferred version would let invalid data leak through to physics or rendering.