1use std::time::Duration;
5use web_time::Instant;
11
12use crate::{
13 app::SystemStage,
14 ecs::{plugin::Plugin, system::ResMut},
15};
16
17pub struct Time {
20 start: Instant,
21 last_tick: Instant,
22 delta: Duration,
23 elapsed: Duration,
24}
25
26impl Time {
27 fn new() -> Self {
28 let now = Instant::now();
29 Self { start: now, last_tick: now, delta: Duration::ZERO, elapsed: Duration::ZERO }
30 }
31
32 fn tick(&mut self) {
33 let now = Instant::now();
34 self.delta = now.duration_since(self.last_tick);
35 self.last_tick = now;
36 self.elapsed = now.duration_since(self.start);
37 }
38
39 pub fn delta(&self) -> Duration {
41 self.delta
42 }
43
44 pub fn delta_seconds(&self) -> f32 {
47 self.delta.as_secs_f32()
48 }
49
50 pub fn elapsed(&self) -> Duration {
52 self.elapsed
53 }
54
55 pub fn elapsed_seconds(&self) -> f32 {
57 self.elapsed.as_secs_f32()
58 }
59
60 pub fn fps(&self) -> f32 {
66 let seconds = self.delta_seconds();
67 if seconds > 0.0 { 1.0 / seconds } else { 0.0 }
68 }
69}
70
71fn tick_time(mut time: ResMut<Time>) {
72 time.tick();
73}
74
75pub struct TimePlugin;
84
85impl Plugin for TimePlugin {
86 fn build(&self, app: &mut crate::prelude::App) {
87 app.add_resource(Time::new()).add_system(SystemStage::PreUpdate, tick_time);
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn a_fresh_time_has_zero_delta_and_elapsed() {
97 let time = Time::new();
98 assert_eq!(time.delta(), Duration::ZERO);
99 assert_eq!(time.elapsed(), Duration::ZERO);
100 assert_eq!(time.fps(), 0.0);
101 }
102
103 #[test]
104 fn ticking_advances_delta_and_accumulates_elapsed() {
105 let mut time = Time::new();
106 std::thread::sleep(Duration::from_millis(5));
107 time.tick();
108
109 assert!(time.delta_seconds() > 0.0);
110 assert!(time.elapsed_seconds() >= time.delta_seconds());
111 assert!(time.fps() > 0.0);
112
113 let first_elapsed = time.elapsed();
114 std::thread::sleep(Duration::from_millis(5));
115 time.tick();
116 assert!(time.elapsed() > first_elapsed);
117 }
118}