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.
108 lines
3.0 KiB
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
|
|
}
|
|
}
|
|
|