Files
Oxide/editor/src/play.rs
T
Homer Simpson f56a1eea3b Import Oxide engine (Stages 0–10) under MIT license
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>
2026-07-05 20:41:02 +02:00

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);
}
}