1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use crate::*; use riddle_common::define_handles; use std::sync::Mutex; /// The Riddle time system core state. /// /// Manages tracking framerate and timer state. /// /// It is possible to manage the audio system state independantly - the most important /// thing to note is that [`TimeSystem::process_frame`] must be called once per "frame". /// The default Riddle integration calls that method whenever the /// `riddle::Event::ProcessFrame` event is fired. pub struct TimeSystem { weak_self: TimeSystemWeak, frame_time: Mutex<FrameTime>, timers: TimerSet, } define_handles!(<TimeSystem>::weak_self, pub TimeSystemHandle, pub TimeSystemWeak); impl TimeSystem { /// Create a new time system. The time the system is created is used as the time /// of the 0th frame. pub fn new() -> TimeSystemHandle { TimeSystemHandle::new(|weak_self| Self { weak_self, frame_time: Mutex::new(FrameTime::new()), timers: TimerSet::new(), }) } /// Update the time system state, marking the beginning of a the next frame. /// /// The instant that this method is called is taken as the reference time for /// the frame that is about to be executed. /// /// Timers will also be triggered during this function call if they are due /// to trigger. /// /// **Do not** call this function directly if you are using this through the /// `riddle` crate. /// /// # Example /// /// ``` /// # use riddle_time::*; doctest::simple(|time_system| { /// let frame_1 = time_system.frame_instant(); /// /// // A while later /// # doctest::pump_for_secs(time_system, 1); /// let frame_n = time_system.frame_instant(); /// /// assert_eq!(true, frame_n - frame_1 > std::time::Duration::from_secs(0)); /// # }); /// ``` pub fn process_frame(&self) { let mut locked_time = self.frame_time.lock().unwrap(); locked_time.update(); let delta = locked_time.frame_delta; drop(locked_time); self.timers.update(delta); } /// Get the current FPS as calculated based on previous frame durations. pub fn fps(&self) -> f32 { self.frame_time.lock().unwrap().fps } /// Get the current delta t as calculated based on previous frame durations. pub fn delta_secs(&self) -> f32 { 1.0 / self.frame_time.lock().unwrap().fps } /// Get the reference time for this frame. Captured during [`TimeSystem::process_frame`]. pub fn frame_instant(&self) -> std::time::Instant { self.frame_time.lock().unwrap().frame_instant } /// Register a timer with a callback which will be fired when the time elpases. /// /// The returned handle may be dropped without cancelling the timer. /// /// # Example /// /// ``` /// # use std::sync::{Arc, atomic::{AtomicBool, Ordering}}; /// # use riddle_time::*; doctest::simple(|time_system| { /// let val = Arc::new(AtomicBool::new(false)); /// /// time_system.register_timer(std::time::Duration::from_millis(200), { /// let val = val.clone(); /// move || { val.store(true, Ordering::Relaxed); } /// }); /// /// // A while later /// # doctest::pump_for_secs(time_system, 1); /// assert_eq!(true, val.load(Ordering::Relaxed)); /// # }); /// ``` pub fn register_timer<F>(&self, duration: std::time::Duration, callback: F) -> TimerHandle where F: FnOnce() + Send + 'static, { self.timers.register_timer(duration, Box::new(callback)) } } struct FrameTime { frame_instant: std::time::Instant, frame_delta: std::time::Duration, fps: f32, } impl FrameTime { fn new() -> Self { Self { frame_instant: std::time::Instant::now(), frame_delta: Default::default(), fps: 0.0, } } fn update(&mut self) { let now = std::time::Instant::now(); self.frame_delta = now.duration_since(self.frame_instant); self.fps = 1.0 / self.frame_delta.as_secs_f32().max(0.001); self.frame_instant = now; } }