Crate nate_engine

source ·
Expand description

Nate’s Game Engine

§Creating Worlds

#[world(singular=[canvas])]
pub struct World {
    position: (isize, isize),
    velocity: (isize, isize),
    health: usize,
    health_changes: isize,
    canvas: [[bool; 10]; 10],
}

Creates a world named World, where every entity has the possibility of having a position, velocity, health, and health_change, but the world has a singular canvas. This will also generate getters and setters for each component as well as a default initializer for the world.

§Declaring Systems

#[system(world=World, read=[velocity], write=[position], _read=[game_state], _write=[canvas])]
pub fn some_system() {
    ...
}

The above example creates a system named some_system that operates on a world of type World. This system will operate on each entity that has a velocity and position component, such that the entity’s positions are mutable and their velocities are immutable. The world game_state is also readable and the world canvas is writable.

§Examples

One example of using the engine is accessible here. An even simpler example is as follows:

use nate_engine::{Engine, Renderer, system, world};
 
// Game State Enum
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GameState {
    Paused,
    Playing,
    Stopped,
}
 
// World where every entity can have a position, velocity, health, and health_change and
// canvas and game_state are singular components (i.e. components on the world)
#[world(singular=[canvas, game_state])]
pub struct ExampleWorld {
    position: (isize, isize),
    velocity: (isize, isize),
    health: usize,
    health_changes: isize,
 
    canvas: [[bool; 10]; 10],
    game_state: GameState,
}
 
// If the game is playing, update the position of every entity with both and position and
// velocity based on the velocity.
#[system(world=ExampleWorld, read=[velocity], write=[position], _read=[game_state])]
pub fn position_update_system() {
    if game_state == GameState::Playing {
        *position = (position.0 + velocity.0, position.1 + velocity.1);
    }
}
 
// If the game is playing, update the health of every entity with a health change
#[system(world=ExampleWorld, read=[health_changes], write=[health], _read=[game_state])]
pub fn health_update_system() {
    if game_state == GameState::Playing {
        *health = health.saturating_add_signed(*health_changes);
    }
}
 
// Write the positions of entities to the canvas
#[system(world=ExampleWorld, read=[position], _write=[canvas = [[false; 10]; 10])]
pub fn canvas_update_system() {
    let x = position.0.clamp(0.0, 9.0) as usize;
    let y = position.1.clamp(0.0, 9.0) as usize;
    canvas[y][x] = true;
}
 
// Renderer that will print to the console
pub struct ExampleRenderer {}
 
// Make sure the renderer is compatible with the game engine
impl Renderer<ExampleWorld> for ExampleRenderer {
    type Error = String;
 
    fn render(&mut self, world: Arc<RwLock<ExampleWorld>>) -> Result<(), Self::Error> {
        let world = world.read.unwrap();
        let canvas = (*world.canvas.read().unwrap()).unwrap();
         
         
        for y in 0..10 {
            for x in 0..10 {
                if canvas[y][x] {
                    print!("X");
                } else {
                    print!("_");
                }
            }
            print!("\n");
        }
 
        // Handle Ctrl-c or Events to exit the loop
        Ok(())
    }
}
 
// The Frame Rate for the Rendering Engine
const FRAME_RATE: u32 = 30;
// The Number of CPU workers to Compute Systems (1 will be reserved for the rendering engine)
const WORKERS: usize = 2;
 
fn main() {
    let world = ExampleWorld::new();
 
    {
        let mut world = world.write().unwrap();
        // Create Entities in the World
        let entity_ids = world.add_entities(100);
 
        // Add Components to Entities
        world.set_positions(&entity_ids, ...);
        ...
 
        // Initialize Singular Components
        world.set_game_state(GameState::Playing);
        world.set_canvas([[false; 10]; 10]);
    }
 
    let mut engine = Engine::new(
        FRAME_RATE,
        WORKERS,
        world,
        vec![
            // position update system will run every 100ms
            (position_update_system, 100_000),
            // health update system will run every 1s
            (health_update_system, 1_000_000),
            // canvas update system will run every 100ms
            (canvas_update_system, 100_000),
        ],
        Box::new(ExampleRenderer{}),
    );
 
    engine.run();
}

Structs§

  • Re-export of Nate’s Engine Core The basic Engine schedules systems to run at given time intervals in a a threadpool with a singular thread reserved for UI rendering at a given frame rate

Traits§

  • Re-export of Nate’s Engine Core Rendering Engine Trait

Attribute Macros§

  • Re-export of Nate’s Engine Macros
  • Re-export of Nate’s Engine Macros