Files
games/crates/engines/engine-v1-common/src/game_loop.rs

72 lines
2.4 KiB
Rust

// file: crates/engines/engine-v1-common/src/game_loop.rs
// version: 4
/// Minimal update contract implemented by a game state consumed by engine V1.
pub trait EngineGame {
/// Advances the game by one engine update using platform-independent input.
fn update(&mut self, frame: crate::EngineFrame, input: crate::InputState);
/// Handles a platform-independent request to leave the current runtime.
///
/// Games may mutate their state and return [`crate::QuitDecision::Continue`] to implement
/// pause/confirmation screens, navigation, score submission, saving, or other workflows.
fn quit_requested(&mut self, _request: crate::QuitRequest) -> crate::QuitDecision {
return crate::QuitDecision::Exit;
}
/// Produces the platform-independent scene rendered after the update.
fn scene(&self) -> crate::EngineScene {
return crate::EngineScene::empty(crate::RenderColor::rgb(18, 18, 24));
}
}
/// Deterministic fixed-step driver used before and underneath platform event loops.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FixedStepRunner {
delta: std::time::Duration,
next_frame_index: u64,
elapsed: std::time::Duration,
}
impl FixedStepRunner {
/// Creates a fixed-step runner using the supplied update duration.
#[must_use]
pub const fn new(delta: std::time::Duration) -> Self {
return Self { delta, next_frame_index: 0, elapsed: std::time::Duration::ZERO };
}
/// Returns the configured fixed update duration.
#[must_use]
pub const fn delta(self) -> std::time::Duration {
return self.delta;
}
/// Returns the index that will be assigned to the next update.
#[must_use]
pub const fn next_frame_index(self) -> u64 {
return self.next_frame_index;
}
/// Returns the accumulated simulated time.
#[must_use]
pub const fn elapsed(self) -> std::time::Duration {
return self.elapsed;
}
/// Advances one game state by one deterministic update.
pub fn tick<G>(&mut self, game: &mut G, input: crate::InputState)
where
G: crate::EngineGame,
{
let frame = crate::EngineFrame::new(self.next_frame_index, self.delta, self.elapsed);
game.update(frame, input);
self.next_frame_index = self.next_frame_index.saturating_add(1);
self.elapsed = self.elapsed.saturating_add(self.delta);
return;
}
}
#[cfg(test)]
#[path = "../unit_tests/game_loop.rs"]
mod tests;