diff --git a/Cargo.lock b/Cargo.lock index 6a9b6aa..c70cdd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "hypoloop" -version = "0.1.1" +version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index c98d74b..5f737fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hypoloop" description = "A low-level control loop for real-time and baked simulations." -version = "0.1.1" +version = "0.1.2" edition = "2018" license = "GPL-3.0" repository = "https://github.com/skyeterran/hypoloop" diff --git a/examples/particles/Cargo.toml b/examples/particles/Cargo.toml index c6ce7ef..7c7caa8 100644 --- a/examples/particles/Cargo.toml +++ b/examples/particles/Cargo.toml @@ -6,4 +6,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -hypoloop = "0.1.1" \ No newline at end of file +hypoloop = {version = "0.1.1", path = "D:/Code/hypoloop"} \ No newline at end of file diff --git a/examples/particles/src/main.rs b/examples/particles/src/main.rs index 8753131..22f029c 100644 --- a/examples/particles/src/main.rs +++ b/examples/particles/src/main.rs @@ -1,3 +1,9 @@ +use hypoloop::Simulation; + fn main() { - hypoloop::run_simulation(); + // create sim + let mut sim = Simulation::new(); + + // run sim + sim.run(); } diff --git a/src/lib.rs b/src/lib.rs index 0b5943e..c6dc7af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,73 +1,87 @@ use std::time::{Duration, Instant}; -// sim constants -// UPDATE_INTERVAL is the minimum delay (in milliseconds) between update ticks -// TIMESCALE is the rate of the simulation proportional to real-time -// if REALTIME is false, the simulation runs as fast as possible and doesn't run the display function -const UPDATE_INTERVAL: u32 = 40; -const TIMESCALE: f32 = 1.0; -const REALTIME: bool = true; - // debug constants const DEBUG_LOOP: bool = false; const DEBUG_TIME: bool = true; -pub fn run_simulation() { - // allow the simulation to be stopped from within the loop (by setting simulate to false) - let mut simulate: bool = true; - - // start the clock to keep track of real time - let clock_start = Instant::now(); +/// Contains all per-simulation logic and state +// update_interval is the minimum delay (in milliseconds) between update ticks +// timescale is the rate of the simulation proportional to real-time +// if realtime is false, the simulation runs as fast as possible and doesn't run the display function +pub struct Simulation { + update_interval: u32, + timescale: f32, + realtime: bool, + simulate: bool +} - // keep track of the last tick time - let mut last_tick = Instant::now(); - - // real-time and sim-time clocks - let mut irl_time = Duration::new(0, 0); - let mut sim_time = Duration::new(0, 0); - - while simulate { - // TODO - support frameskips - if !REALTIME || delta_time(last_tick) >= UPDATE_INTERVAL { - // mutable delta time and timescale for flexibility - let mut current_timescale: f32; - let mut current_delta_time: u32; - let elapsed_time = Instant::now().duration_since(last_tick); - - // update clocks - if REALTIME { - current_timescale = TIMESCALE; - current_delta_time = delta_time(last_tick); - sim_time += elapsed_time.mul_f32(TIMESCALE); - irl_time += elapsed_time; - } else { - current_timescale = 1.0; - current_delta_time = UPDATE_INTERVAL; - sim_time += Duration::from_millis(UPDATE_INTERVAL as u64); - irl_time = Instant::now().duration_since(clock_start); - } - - // DEBUG - if DEBUG_TIME { - let loop_delay_ms = elapsed_time.as_nanos() as f32 / 1_000_000.0; - let loop_rate_hz = 1000.0 / loop_delay_ms; - println!("REALTIME: {} | IRL time: {}ms | Sim time: {}ms | Tick delay/rate: {}ms/{}hz", REALTIME, irl_time.as_millis(), sim_time.as_millis(), loop_delay_ms, loop_rate_hz); - } - - // update - update(current_delta_time, current_timescale); - - // record last tick time - last_tick = Instant::now(); +impl Simulation { + // Creates a new simulation with default values + pub fn new() -> Simulation { + Simulation { + update_interval: 40, + timescale: 1.0, + realtime: true, + simulate: true } + } - // display - if REALTIME { - display(delta_time(last_tick), TIMESCALE); + /// Initializes and runs the simulation + pub fn run(&self) { + // start the clock to keep track of real time + let clock_start = Instant::now(); + + // keep track of the last tick time + let mut last_tick = Instant::now(); + + // real-time and sim-time clocks + let mut irl_time = Duration::new(0, 0); + let mut sim_time = Duration::new(0, 0); + + while self.simulate { + // TODO - support frameskips + if !self.realtime || delta_time(last_tick) >= self.update_interval { + // mutable delta time and timescale for flexibility + let mut current_timescale: f32; + let mut current_delta_time: u32; + let elapsed_time = Instant::now().duration_since(last_tick); + + // update clocks + if self.realtime { + current_timescale = self.timescale; + current_delta_time = delta_time(last_tick); + sim_time += elapsed_time.mul_f32(self.timescale); + irl_time += elapsed_time; + } else { + current_timescale = 1.0; + current_delta_time = self.update_interval; + sim_time += Duration::from_millis(self.update_interval as u64); + irl_time = Instant::now().duration_since(clock_start); + } + + // DEBUG + if DEBUG_TIME { + let loop_delay_ms = elapsed_time.as_nanos() as f32 / 1_000_000.0; + let loop_rate_hz = 1000.0 / loop_delay_ms; + println!("Realtime: {} | IRL time: {}ms | Sim time: {}ms | Tick delay/rate: {}ms/{}hz", self.realtime, irl_time.as_millis(), sim_time.as_millis(), loop_delay_ms, loop_rate_hz); + } + + // update + update(current_delta_time, current_timescale); + + // record last tick time + last_tick = Instant::now(); + } + + // display + if self.realtime { + display(delta_time(last_tick), self.timescale, self.update_interval); + } } } } + // update function // this is where all your per-tick logic should go fn update(delta_time: u32, timescale: f32) { @@ -82,14 +96,14 @@ fn update(delta_time: u32, timescale: f32) { // display function // this is where you should call a render function -fn display(delta_time: u32, timescale: f32) { +fn display(delta_time: u32, timescale: f32, update_interval: u32) { // DEBUG if DEBUG_LOOP { println!("Displaying..."); } // use interpolation to smooth display values between ticks - let interpolation: f32 = delta_time as f32 / UPDATE_INTERVAL as f32 * timescale; + let interpolation: f32 = delta_time as f32 / update_interval as f32 * timescale; } // gets the time in milliseconds that's elapsed since the earlier Instant