changed logic of how to run the loop to allow for external event loops

This commit is contained in:
skyeshroom 2021-08-12 13:11:46 -07:00
parent 8a1b482b3e
commit 464ebaaf25
5 changed files with 98 additions and 54 deletions

View File

@ -10,4 +10,7 @@ include = ["/src", "LICENSE", "/examples"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
[dev-dependencies]
winit = "0.25.0"

View File

@ -18,21 +18,26 @@ fn main() {
let mut x: f32 = 0.0; let mut x: f32 = 0.0;
// create a closure containing your update logic // create a closure containing your update logic
let update_logic = |state: &mut State| { let mut update_logic = move |state: &mut State| {
// access loop metadata via the State object // access loop metadata via the State object
x += state.get_timescale(); x += state.get_timescale();
print!("x: {} | ", x); print!("x: {} | ", x);
// print information about the current tick's timings // print information about the current tick's timings
state.debug_tick(); state.debug_time();
}; };
// create a closure containing your display logic // create a closure containing your display logic
let display_logic = |state: &State| { let display_logic = move |state: &State| {
// //
}; };
// run the simulation with your user-defined update and display logic // run the simulation with your user-defined update and display logic
sim.run(update_logic, display_logic); // initialize the sim (cleans internal clocks, etc.)
sim.init();
loop {
// "step" the sim forward
sim.step(&mut update_logic, &display_logic);
}
} }
``` ```

View File

@ -8,20 +8,25 @@ fn main() {
let mut x: f32 = 0.0; let mut x: f32 = 0.0;
// create a closure containing your update logic // create a closure containing your update logic
let update_logic = |state: &mut State| { let mut update_logic = move |state: &mut State| {
// access loop metadata via the State object // access loop metadata via the State object
x += state.get_timescale(); x += state.get_timescale();
print!("x: {} | ", x); print!("x: {} | ", x);
// print information about the current tick's timings // print information about the current tick's timings
state.debug_tick(); state.debug_time();
}; };
// create a closure containing your display logic // create a closure containing your display logic
let display_logic = |state: &State| { let display_logic = move |state: &State| {
// //
}; };
// run the simulation with your user-defined update and display logic // run the simulation with your user-defined update and display logic
sim.run(update_logic, display_logic); // initialize the sim (cleans internal clocks, etc.)
sim.init();
loop {
// "step" the sim forward
sim.step(&mut update_logic, &display_logic);
}
} }

46
examples/windowing.rs Normal file
View File

@ -0,0 +1,46 @@
use hypoloop::core::{State, Loop};
use winit::{
event::{ElementState, Event, KeyboardInput, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, WindowBuilder},
};
fn main() {
// create sim with default configuration
let mut sim = Loop::new();
// test variable
let mut x: f32 = 0.0;
// create a winit event loop
let event_loop = EventLoop::new();
// create a winit window
let window = WindowBuilder::new().build(&event_loop).unwrap();
window.set_title("Windowing test with hypoloop");
// create a closure containing your update logic
let mut update_logic = move |state: &mut State| {
// access loop metadata via the State object
x += state.get_timescale();
print!("x: {} | ", x);
// print information about the current tick's timings
state.debug_time();
};
// create a closure containing your display logic
let display_logic = move |state: &State| {
// redraw the winit window
window.request_redraw();
};
// initialize the sim (cleans internal clocks, etc.)
sim.init();
// run the winit event loop with embedded hypoloop sim
event_loop.run(move |event, _, control_flow| {
// "step" the sim forward
sim.step(&mut update_logic, &display_logic);
});
}

View File

@ -7,10 +7,11 @@ pub mod core {
update_interval: u32, update_interval: u32,
timescale: f32, timescale: f32,
simulate: bool, simulate: bool,
clock_start: Instant,
last_tick: Instant,
delta_time: u32, delta_time: u32,
irl_time: Duration, irl_time: Duration,
sim_time: Duration, sim_time: Duration
last_tick: Instant
} }
impl State { impl State {
@ -21,10 +22,11 @@ pub mod core {
update_interval: 40, update_interval: 40,
timescale: 1.0, timescale: 1.0,
simulate: true, simulate: true,
clock_start: Instant::now(),
last_tick: Instant::now(),
delta_time: 0, delta_time: 0,
irl_time: Duration::new(0,0), irl_time: Duration::new(0,0),
sim_time: Duration::new(0,0), sim_time: Duration::new(0,0)
last_tick: Instant::now()
}; };
// Make sure that delta_time always starts the same as update_interval // Make sure that delta_time always starts the same as update_interval
@ -59,12 +61,12 @@ pub mod core {
self.last_tick self.last_tick
} }
/// Pauses the simulation /// Pauses the simulation from within update logic
pub fn pause(&mut self) { pub fn pause(&mut self) {
self.simulate = false; self.simulate = false;
} }
/// Resumes the simulation /// Resumes the simulation from within update logic
pub fn resume(&mut self) { pub fn resume(&mut self) {
self.simulate = true; self.simulate = true;
} }
@ -74,12 +76,15 @@ pub mod core {
self.timescale = timescale; self.timescale = timescale;
} }
/// Prints a string of information about the current tick /// Prints a string of information about the current time (IRL time, Sim time, Delta time (tick), Delta time (step))
pub fn debug_tick(self) { /// IRL time: Real time (in ms) elapsed since the start of the loop
/// Sim time: Virtual time (in ms) elapsed since the start of the loop
/// Delta time (tick): Real time (in ms) elapsed between the last tick and the previous tick
/// Delta time (step): Real time (in ms with nanosecond accuracy) elapsed since the last update tick
pub fn debug_time(self) {
let elapsed_time = Instant::now().duration_since(self.last_tick); let elapsed_time = Instant::now().duration_since(self.last_tick);
let loop_delay_ms = elapsed_time.as_nanos() as f32 / 1_000_000.0; let loop_delay_ms = elapsed_time.as_nanos() as f32 / 1_000_000.0;
let loop_rate_hz = 1000.0 / loop_delay_ms; println!("IRL time: {}ms | Sim time: {}ms | Delta time (tick): {}ms | Delta time (step): {}ms", self.irl_time.as_millis(), self.sim_time.as_millis(), self.delta_time, loop_delay_ms);
println!("IRL time: {}ms | Sim time: {}ms | Tick delay/rate: {}ms/{}hz", self.irl_time.as_millis(), self.sim_time.as_millis(), loop_delay_ms, loop_rate_hz);
} }
} }
@ -99,15 +104,21 @@ pub mod core {
} }
} }
/// Initializes and runs the simulation using a user-supplied callback as the update logic /// Initializes or re-initializes the simulation
pub fn run(&mut self, mut update_callback: impl FnMut(&mut State), mut display_callback: impl FnMut(&State)) { pub fn init(&mut self) {
// Make sure the simulation will run // Make sure the simulation will run
self.state.simulate = true; self.state.simulate = true;
// start the clock to keep track of real time // reset the internal clocks
let clock_start = Instant::now(); self.state.clock_start = Instant::now();
self.state.irl_time = Duration::new(0,0);
while self.state.simulate { self.state.sim_time = Duration::new(0,0);
}
/// Executes the per-loop logic (can be triggered manually so that hypoloop can be tied into external event loops)
pub fn step(&mut self, mut update_callback: impl FnMut(&mut State), display_callback: impl Fn(&State)) {
// don't run if the simulation is paused
if self.state.simulate {
// TODO - support frameskips // TODO - support frameskips
if !self.realtime || delta_time(self.state.last_tick) >= self.state.update_interval { if !self.realtime || delta_time(self.state.last_tick) >= self.state.update_interval {
// mutable delta time and timescale for flexibility // mutable delta time and timescale for flexibility
@ -121,7 +132,7 @@ pub mod core {
} else { } else {
self.state.delta_time = self.state.update_interval; self.state.delta_time = self.state.update_interval;
self.state.sim_time += Duration::from_millis(self.state.update_interval as u64); self.state.sim_time += Duration::from_millis(self.state.update_interval as u64);
self.state.irl_time = Instant::now().duration_since(clock_start); self.state.irl_time = Instant::now().duration_since(self.state.clock_start);
} }
// update // update
@ -144,34 +155,8 @@ pub mod core {
} }
} }
// update function
// this is where all your per-tick logic should go
fn update(user_function: fn(), delta_time: u32, timescale: f32) {
// use timestep to scale per-tick calculations appropriately
let timestep: f32 = delta_time as f32 / 1000.0 * timescale;
// call user update function
user_function();
}
// display function
// this is where you should call a render function
fn display(delta_time: u32, timescale: f32, update_interval: u32) {
// use interpolation to smooth display values between ticks
let interpolation: f32 = delta_time as f32 / update_interval as f32 * timescale;
}
// gets the time in milliseconds that's elapsed since the earlier Instant // gets the time in milliseconds that's elapsed since the earlier Instant
fn delta_time(earlier: Instant) -> u32 { fn delta_time(earlier: Instant) -> u32 {
Instant::now().duration_since(earlier).as_millis() as u32 Instant::now().duration_since(earlier).as_millis() as u32
} }
// default update function (does nothing)
fn default_update() {
}
// default display function (does nothing)
fn default_display() {
}
} }