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

Project Structure

A typical Nightshade project follows this structure:

my_game/
├── Cargo.toml
├── src/
│   ├── main.rs           # Entry point
│   ├── game.rs           # Game state
│   └── systems/          # Game systems
│       ├── player.rs
│       ├── camera.rs
│       └── ...
├── assets/
│   ├── models/           # glTF/GLB files
│   ├── textures/         # PNG, JPG, HDR
│   └── sounds/           # Audio files
└── README.md

Cargo.toml

[package]
name = "my_game"
version = "0.1.0"
edition = "2024"

[dependencies]
nightshade = { git = "https://github.com/matthewjberger/nightshade.git", features = ["engine", "wgpu"] }

Entry Point (main.rs)

Keep main.rs minimal:

mod game;

use nightshade::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    nightshade::launch(game::MyGame::default())
}

Game State (game.rs)

Implement your game logic:

#![allow(unused)]
fn main() {
use nightshade::prelude::*;

#[derive(Default)]
pub struct MyGame {
    player: Option<Entity>,
    score: u32,
}

impl State for MyGame {
    fn title(&self) -> &str {
        "My Game"
    }

    fn initialize(&mut self, world: &mut World) {
        // Setup code
    }

    fn run_systems(&mut self, world: &mut World) {
        // Per-frame logic
    }
}
}

Embedding Assets

For distribution, embed assets directly in the binary:

#![allow(unused)]
fn main() {
const MODEL_BYTES: &[u8] = include_bytes!("../assets/models/character.glb");
const SKY_HDR: &[u8] = include_bytes!("../assets/textures/sky.hdr");
}

Custom ECS

For complex games, create a separate game ECS alongside the engine's World:

#![allow(unused)]
fn main() {
use freecs::ecs;

ecs! {
    GameWorld {
        components {
            player_state: PlayerState,
            inventory: Inventory,
            health: Health,
        },
        resources {
            game_time: GameTime,
            score: u32,
        }
    }
}

pub struct MyGame {
    game: GameWorld,
}
}

This keeps game-specific data separate from engine data while allowing both to coexist.

Module Organization

For larger projects, organize systems into modules:

#![allow(unused)]
fn main() {
// src/systems/mod.rs
pub mod camera;
pub mod player;
pub mod enemies;
pub mod ui;

// src/game.rs
mod systems;

impl State for MyGame {
    fn run_systems(&mut self, world: &mut World) {
        systems::player::update(&mut self.game, world);
        systems::camera::follow(&self.game, world);
        systems::enemies::ai(&mut self.game, world);
        systems::ui::update(&self.game, world);
    }
}
}