My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/development/libs/little/little/src/game.rs

108 lines
3.0 KiB

use crate::{LittleError, Result};
use little_core::{
event::{Event, Events},
settings::GameSettings,
};
use little_gl::Gl;
use std::collections::VecDeque;
pub struct Game {
layers: VecDeque<Box<dyn GameLayer + 'static>>,
events: Events,
gl: Gl,
}
impl Game {
/// Create a game loop with a valid settings objects
pub fn with_settings(_: GameSettings) -> Result<Self> {
todo!()
}
/// Push a new layer on top of the layer stack
pub fn push_layer<L: GameLayer + 'static>(&mut self, mut layer: L) -> Result<()> {
layer.initialise()?;
self.layers.push_front(Box::new(layer));
Ok(())
}
/// Pop the last created layer from the stack
pub fn pop_layer(&mut self) -> Result<()> {
match self.layers.pop_front() {
Some(_) => Ok(()),
None => Err(LittleError::NoLayer)
}
}
/// Run this game
pub fn run(&mut self) {
'main: loop {
for event in &mut self.events {
match event {
Event::Quit { .. } => break 'main,
_ => {
// TODO: send to the event reactor
}
}
}
// Update layers sequentially - in stack order
for mut layer in (&mut self.layers).iter_mut() {
if let Err(e) = Self::update_layer(&self.gl, &mut layer) {
eprintln!("{}", e);
}
if layer.terminate_frame() && break {}
}
}
}
// Game internals
/// Run update, render and post_hook for a layer
fn update_layer(gl: &Gl, l: &mut Box<dyn GameLayer>) -> Result<()> {
l.update()?;
l.render(gl)?;
l.post_hook()?;
Ok(())
}
}
/// A modular gaem layer to attach user code to the main run loop
///
/// In `little` a `Game` is the basic runtime handle of the engine.
/// The `GameLayer` trait exists to allow users to attach their own
/// behaviour to the lifecycle of this runtime. If multiple layers
/// are attached to the game they will be processed in the reverse
/// order they were added (FILO - like a stack).
///
/// Optionally a layer may also consume all events and terminate a
/// frame prematurely by implementing `terminate_frame` to return
/// `true`). This is useful when creating nested menus and screens
/// where only the top screen should be rendered, while pausing
/// executiong on lower layers.
pub trait GameLayer {
/// Called only once during layer initialisation
fn initialise(&mut self) -> Result<()> {
Ok(())
}
/// Called every frame to update a layer's logic
fn update(&mut self) -> Result<()> {
Ok(())
}
/// Called every frame with the current render context
fn render(&mut self, _: &Gl) -> Result<()> {
Ok(())
}
/// Called at every end of frame
fn post_hook(&mut self) -> Result<()> {
Ok(())
}
/// Indicate whether this layer should terminate the frame
fn terminate_frame(&self) -> bool {
false
}
}