changed logic of how to run the loop to allow for external event loops
This commit is contained in:
parent
8a1b482b3e
commit
464ebaaf25
@ -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"
|
15
README.md
15
README.md
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -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
46
examples/windowing.rs
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
71
src/lib.rs
71
src/lib.rs
@ -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() {
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user