f56a1eea3b
Full project snapshot migrated to new Gitea remote without history: engine, editor, physics, script, examples, tests, docs, and assets. Relicensed from GPLv3 to MIT and updated repo URLs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
2.7 KiB
Rust
70 lines
2.7 KiB
Rust
//! The play-mode tick decision (Stage 8.7).
|
|
//!
|
|
//! The host runner ([`oxide_editor::main`](crate)) owns the play [`App`] and the
|
|
//! window loop; this module isolates the one piece of that loop worth testing on
|
|
//! its own: **how far to advance the simulation this frame** given the current
|
|
//! [`PlayState`] and whether a single **Step** was requested.
|
|
//!
|
|
//! Keeping it a pure function pins the play-mode contract in a unit test instead
|
|
//! of burying it in the (un-testable) GUI runner:
|
|
//!
|
|
//! - [`Playing`](PlayState::Playing) → advance one real frame ([`Tick::Frame`]).
|
|
//! - [`Paused`](PlayState::Paused) + Step → advance exactly one fixed tick
|
|
//! ([`Tick::FixedStep`]); a stray Step while *Playing* is ignored (the frame
|
|
//! already advances).
|
|
//! - [`Editing`](PlayState::Editing), or Paused with no Step → do nothing
|
|
//! ([`Tick::Idle`]).
|
|
//!
|
|
//! [`App`]: oxide_engine::app::App
|
|
|
|
use crate::state::PlayState;
|
|
|
|
/// How the host runner should advance the play [`App`](oxide_engine::app::App)
|
|
/// this frame. The runner maps each variant onto an engine call: `Frame` →
|
|
/// [`App::update`](oxide_engine::app::App::update), `FixedStep` →
|
|
/// [`App::step`](oxide_engine::app::App::step), `Idle` → no call.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Tick {
|
|
/// Do not advance the simulation (editing, or paused with no step queued).
|
|
Idle,
|
|
/// Advance one normal frame by the real delta (playing).
|
|
Frame,
|
|
/// Advance exactly one fixed timestep (a single step while paused).
|
|
FixedStep,
|
|
}
|
|
|
|
/// Decides how to advance the simulation this frame. `step_requested` is whether
|
|
/// the user asked for a single **Step** since the last frame; it is honoured
|
|
/// only while [`Paused`](PlayState::Paused). See the [module docs](self).
|
|
pub fn tick_for(play: PlayState, step_requested: bool) -> Tick {
|
|
match play {
|
|
PlayState::Playing => Tick::Frame,
|
|
PlayState::Paused if step_requested => Tick::FixedStep,
|
|
PlayState::Paused | PlayState::Editing => Tick::Idle,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn playing_advances_a_frame_regardless_of_step() {
|
|
assert_eq!(tick_for(PlayState::Playing, false), Tick::Frame);
|
|
// A stray step while playing is ignored — the frame already advances.
|
|
assert_eq!(tick_for(PlayState::Playing, true), Tick::Frame);
|
|
}
|
|
|
|
#[test]
|
|
fn paused_steps_only_when_requested() {
|
|
assert_eq!(tick_for(PlayState::Paused, false), Tick::Idle);
|
|
assert_eq!(tick_for(PlayState::Paused, true), Tick::FixedStep);
|
|
}
|
|
|
|
#[test]
|
|
fn editing_never_advances() {
|
|
assert_eq!(tick_for(PlayState::Editing, false), Tick::Idle);
|
|
assert_eq!(tick_for(PlayState::Editing, true), Tick::Idle);
|
|
}
|
|
}
|