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
use crate::game::Floatable; use serde::{Deserialize, Serialize}; use std::time::Duration; /// Doing math with [std::time::Duration](https://doc.rust-lang.org/std/time/struct.Duration.html) /// is sort of a pain, so this wraps it up in an easy package. /// Create a timer set how you like it, call `.update(delta)` with a delta duration each time around /// the main loop, check `.ready` to see if the timer has gone off, and call `.reset()` to start /// over. /// /// You still have to keep track of time by getting an /// [Instant](https://doc.rust-lang.org/std/time/struct.Instant.html) and storing its `elapsed()` /// duration to use during your game loop. It looks like this: /// /// ``` /// use std::time::{Duration, Instant}; /// use rusty_sword_arena::timer::Timer; /// /// // Create some timers /// let mut timer1 = Timer::from_millis(100); /// let mut timer2 = Timer::from_millis(200); /// /// // The current time on the clock /// let mut instant = Instant::now(); /// /// // The "delta time", or time it took to make it around the loop last time /// let mut dt = Duration::from_secs(0); /// /// // Your game loop /// loop { /// // All your game logic, including pumping your timers like this... /// timer1.update(dt); /// timer2.update(dt); /// /// // Get the delta time for the next loop iteration /// dt = instant.elapsed(); /// instant = Instant::now(); /// # break /// } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Timer { time: Duration, time_left: Duration, /// True while the timer is at zero. Once the timer reaches zero, it stays at zero until you /// call `.reset()` or `.set_millis_transient()` pub ready: bool, } impl Timer { /// Create a timer, initializing with a value in milliseconds. pub fn from_millis(ms: u64) -> Self { let duration = Duration::from_millis(ms); Self { time: duration, time_left: duration, ready: false, } } /// Create a timer, initializing with a value in nanoseconds. pub fn from_nanos(nanos: u64) -> Self { let duration = Duration::from_nanos(nanos); Self { time: duration, time_left: duration, ready: false, } } /// Resets the timer back to the starting time. `ready` goes back to `false` pub fn reset(&mut self) { self.ready = false; self.time_left = self.time; } /// Just like `.reset()` but the timer is set to an arbitrary time. Calling `.reset()` will /// still use the original starting time. pub fn set_millis_transient(&mut self, ms: u64) { self.ready = false; self.time_left = Duration::from_millis(ms); } /// IMPORTANT! You must call this method in your game loop! This is how the timer counts-down. /// Every time you call this, the timer counts down the amount in `delta`. If the timer reaches /// zero, `ready` becomes true and the timer stays at zero until `.reset()` or /// `.set_millis_transient()` is called. pub fn update(&mut self, delta: Duration) { if self.ready { return; } if let Some(result) = self.time_left.checked_sub(delta) { self.time_left = result; } else { self.ready = true; } } /// How much time is left as an f32 percentage from 0.0 to 1.0. Very useful if your timer is /// being used for some sort of animation. pub fn time_left_percent(&self) -> f32 { if self.ready { 1.0 } else { self.time_left.as_millis() as f32 / self.time.as_millis() as f32 } } } impl Floatable for Timer { /// The time left on the timer as an f32 fn f32(&self) -> f32 { self.time_left.as_secs() as f32 + self.time_left.subsec_nanos() as f32 * 1e-9 } }