Skip to main content

anvilkit_core/time/
time.rs

1//! # 核心时间资源
2//! 
3//! 提供游戏应用的核心时间跟踪功能,包括帧时间、总运行时间和 FPS 计算。
4//! 
5//! ## 核心概念
6//! 
7//! - **Delta Time**: 上一帧到当前帧的时间间隔,用于帧率无关的游戏逻辑
8//! - **Elapsed Time**: 应用启动以来的总时间
9//! - **Frame Count**: 总帧数,用于 FPS 计算和调试
10//! 
11//! ## 使用模式
12//! 
13//! `Time` 通常作为全局资源在 ECS 系统中使用,每帧调用 `update()` 方法更新时间信息。
14
15use std::time::{Duration, Instant};
16
17/// 核心时间资源,跟踪应用的时间信息
18/// 
19/// `Time` 提供了游戏开发中必需的时间信息,包括帧间隔时间(delta time)、
20/// 总运行时间和帧计数。它是帧率无关游戏逻辑的基础。
21/// 
22/// ## 线程安全
23/// 
24/// `Time` 实现了 `Send` 和 `Sync`,可以安全地在多线程环境中使用。
25/// 
26/// ## 示例
27/// 
28/// ```rust
29/// use anvilkit_core::time::Time;
30/// use std::time::Duration;
31/// 
32/// let mut time = Time::new();
33/// 
34/// // 模拟游戏循环
35/// loop {
36///     time.update();
37///     
38///     // 使用 delta time 进行帧率无关的移动
39///     let movement_speed = 100.0; // 单位/秒
40///     let distance = movement_speed * time.delta_seconds();
41///     
42///     println!("FPS: {:.1}", time.fps());
43///     
44///     // 游戏逻辑...
45///     
46///     std::thread::sleep(Duration::from_millis(16)); // ~60 FPS
47///     
48///     if time.frame_count() > 100 {
49///         break;
50///     }
51/// }
52/// ```
53#[derive(Debug, Clone)]
54#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::system::Resource))]
55pub struct Time {
56    /// 应用启动时的时间点
57    startup_time: Instant,
58    /// 上一帧的时间点
59    last_update: Instant,
60    /// 当前帧的时间点
61    current_time: Instant,
62    /// 上一帧到当前帧的时间间隔
63    delta_time: Duration,
64    /// 应用启动以来的总时间
65    elapsed_time: Duration,
66    /// 总帧数
67    frame_count: u64,
68    /// 是否是第一次更新
69    first_update: bool,
70}
71
72impl Default for Time {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78impl Time {
79    /// 创建新的时间资源
80    /// 
81    /// 初始化时间资源,记录创建时的时间点作为应用启动时间。
82    /// 
83    /// # 示例
84    /// 
85    /// ```rust
86    /// use anvilkit_core::time::Time;
87    /// 
88    /// let time = Time::new();
89    /// assert_eq!(time.frame_count(), 0);
90    /// assert_eq!(time.delta_seconds(), 0.0);
91    /// ```
92    pub fn new() -> Self {
93        let now = Instant::now();
94        Self {
95            startup_time: now,
96            last_update: now,
97            current_time: now,
98            delta_time: Duration::ZERO,
99            elapsed_time: Duration::ZERO,
100            frame_count: 0,
101            first_update: true,
102        }
103    }
104
105    /// 更新时间信息
106    /// 
107    /// 应该在每帧开始时调用此方法来更新时间信息。
108    /// 这会更新 delta time、elapsed time 和 frame count。
109    /// 
110    /// # 示例
111    /// 
112    /// ```rust
113    /// use anvilkit_core::time::Time;
114    /// use std::time::Duration;
115    ///
116    /// let mut time = Time::new();
117    ///
118    /// // 第一次更新初始化时间
119    /// time.update();
120    /// assert_eq!(time.frame_count(), 1);
121    ///
122    /// // 模拟时间流逝
123    /// std::thread::sleep(Duration::from_millis(16));
124    /// time.update();
125    ///
126    /// assert!(time.delta_seconds() > 0.0);
127    /// assert_eq!(time.frame_count(), 2);
128    /// ```
129    pub fn update(&mut self) {
130        let now = Instant::now();
131        
132        if self.first_update {
133            // 第一次更新时,delta time 为 0
134            self.first_update = false;
135            self.delta_time = Duration::ZERO;
136        } else {
137            self.delta_time = now.duration_since(self.current_time);
138        }
139        
140        self.last_update = self.current_time;
141        self.current_time = now;
142        self.elapsed_time = now.duration_since(self.startup_time);
143        self.frame_count += 1;
144    }
145
146    /// 获取上一帧到当前帧的时间间隔
147    /// 
148    /// Delta time 是实现帧率无关游戏逻辑的关键。
149    /// 
150    /// # 示例
151    /// 
152    /// ```rust
153    /// use anvilkit_core::time::Time;
154    /// 
155    /// let mut time = Time::new();
156    /// time.update();
157    /// 
158    /// let delta = time.delta();
159    /// println!("Frame time: {:?}", delta);
160    /// ```
161    pub fn delta(&self) -> Duration {
162        self.delta_time
163    }
164
165    /// 获取 delta time 的秒数表示(f32)
166    /// 
167    /// 这是最常用的 delta time 获取方法,适用于大多数游戏逻辑计算。
168    /// 
169    /// # 示例
170    /// 
171    /// ```rust
172    /// use anvilkit_core::time::Time;
173    /// 
174    /// let mut time = Time::new();
175    /// time.update();
176    /// 
177    /// let speed = 100.0; // 单位/秒
178    /// let distance = speed * time.delta_seconds();
179    /// ```
180    pub fn delta_seconds(&self) -> f32 {
181        self.delta_time.as_secs_f32()
182    }
183
184    /// 获取 delta time 的秒数表示(f64)
185    /// 
186    /// 提供更高精度的 delta time,适用于需要高精度计算的场景。
187    pub fn delta_seconds_f64(&self) -> f64 {
188        self.delta_time.as_secs_f64()
189    }
190
191    /// 获取 delta time 的毫秒数表示
192    /// 
193    /// # 示例
194    /// 
195    /// ```rust
196    /// use anvilkit_core::time::Time;
197    /// 
198    /// let mut time = Time::new();
199    /// time.update();
200    /// 
201    /// println!("Frame time: {}ms", time.delta_millis());
202    /// ```
203    pub fn delta_millis(&self) -> u128 {
204        self.delta_time.as_millis()
205    }
206
207    /// 获取应用启动以来的总时间
208    /// 
209    /// # 示例
210    /// 
211    /// ```rust
212    /// use anvilkit_core::time::Time;
213    /// use std::time::Duration;
214    /// 
215    /// let mut time = Time::new();
216    /// std::thread::sleep(Duration::from_millis(100));
217    /// time.update();
218    /// 
219    /// assert!(time.elapsed().as_millis() >= 100);
220    /// ```
221    pub fn elapsed(&self) -> Duration {
222        self.elapsed_time
223    }
224
225    /// 获取总运行时间的秒数表示(f32)
226    pub fn elapsed_seconds(&self) -> f32 {
227        self.elapsed_time.as_secs_f32()
228    }
229
230    /// 获取总运行时间的秒数表示(f64)
231    pub fn elapsed_seconds_f64(&self) -> f64 {
232        self.elapsed_time.as_secs_f64()
233    }
234
235    /// 获取总帧数
236    /// 
237    /// # 示例
238    /// 
239    /// ```rust
240    /// use anvilkit_core::time::Time;
241    /// 
242    /// let mut time = Time::new();
243    /// assert_eq!(time.frame_count(), 0);
244    /// 
245    /// time.update();
246    /// assert_eq!(time.frame_count(), 1);
247    /// ```
248    pub fn frame_count(&self) -> u64 {
249        self.frame_count
250    }
251
252    /// 获取平均帧率(基于总运行时间)
253    /// 
254    /// 计算从应用启动到现在的平均 FPS。
255    /// 
256    /// # 示例
257    /// 
258    /// ```rust
259    /// use anvilkit_core::time::Time;
260    /// use std::time::Duration;
261    /// 
262    /// let mut time = Time::new();
263    /// 
264    /// // 模拟多帧
265    /// for _ in 0..10 {
266    ///     std::thread::sleep(Duration::from_millis(16));
267    ///     time.update();
268    /// }
269    /// 
270    /// let fps = time.fps();
271    /// println!("Average FPS: {:.1}", fps);
272    /// ```
273    pub fn fps(&self) -> f64 {
274        if self.elapsed_time.is_zero() || self.frame_count == 0 {
275            0.0
276        } else {
277            self.frame_count as f64 / self.elapsed_seconds_f64()
278        }
279    }
280
281    /// 获取瞬时帧率(基于当前 delta time)
282    /// 
283    /// 计算基于当前帧时间的瞬时 FPS,可能会有较大波动。
284    /// 
285    /// # 示例
286    /// 
287    /// ```rust
288    /// use anvilkit_core::time::Time;
289    /// 
290    /// let mut time = Time::new();
291    /// time.update();
292    /// 
293    /// let instant_fps = time.instant_fps();
294    /// println!("Instant FPS: {:.1}", instant_fps);
295    /// ```
296    pub fn instant_fps(&self) -> f64 {
297        if self.delta_time.is_zero() {
298            0.0
299        } else {
300            1.0 / self.delta_seconds_f64()
301        }
302    }
303
304    /// 获取应用启动时间点
305    /// 
306    /// 返回应用启动时的 `Instant`,可用于计算绝对时间间隔。
307    pub fn startup_time(&self) -> Instant {
308        self.startup_time
309    }
310
311    /// 获取当前时间点
312    /// 
313    /// 返回最后一次调用 `update()` 时的时间点。
314    pub fn current_time(&self) -> Instant {
315        self.current_time
316    }
317
318    /// 检查是否是第一帧
319    /// 
320    /// 在某些初始化逻辑中可能需要知道是否是第一帧。
321    /// 
322    /// # 示例
323    /// 
324    /// ```rust
325    /// use anvilkit_core::time::Time;
326    /// 
327    /// let mut time = Time::new();
328    /// assert!(time.is_first_frame());
329    /// 
330    /// time.update();
331    /// assert!(!time.is_first_frame());
332    /// ```
333    pub fn is_first_frame(&self) -> bool {
334        self.frame_count == 0
335    }
336
337    /// 重置时间资源
338    /// 
339    /// 将时间资源重置到初始状态,就像刚创建一样。
340    /// 这在场景切换或游戏重启时可能有用。
341    /// 
342    /// # 示例
343    /// 
344    /// ```rust
345    /// use anvilkit_core::time::Time;
346    /// 
347    /// let mut time = Time::new();
348    /// time.update();
349    /// 
350    /// assert_eq!(time.frame_count(), 1);
351    /// 
352    /// time.reset();
353    /// assert_eq!(time.frame_count(), 0);
354    /// ```
355    pub fn reset(&mut self) {
356        let now = Instant::now();
357        self.startup_time = now;
358        self.last_update = now;
359        self.current_time = now;
360        self.delta_time = Duration::ZERO;
361        self.elapsed_time = Duration::ZERO;
362        self.frame_count = 0;
363        self.first_update = true;
364    }
365
366    /// 设置时间缩放因子
367    ///
368    /// 注意:这个方法返回一个新的 `ScaledTime` 包装器,而不是修改当前实例。
369    ///
370    /// # 参数
371    ///
372    /// - `scale`: 时间缩放因子,1.0 为正常速度,0.5 为半速,2.0 为双速
373    ///
374    /// # 示例
375    ///
376    /// ```rust
377    /// use anvilkit_core::time::Time;
378    ///
379    /// let time = Time::new();
380    /// let slow_time = time.with_scale(0.5); // 半速
381    ///
382    /// assert_eq!(slow_time.scale(), 0.5);
383    /// ```
384    pub fn with_scale(&self, scale: f32) -> ScaledTime {
385        ScaledTime::new(self.clone(), scale)
386    }
387}
388
389/// 带时间缩放的时间包装器
390/// 
391/// `ScaledTime` 允许对时间进行缩放,实现慢动作、快进等效果。
392/// 它包装了一个 `Time` 实例,并对其时间值应用缩放因子。
393/// 
394/// ## 使用场景
395/// 
396/// - 慢动作效果(scale < 1.0)
397/// - 快进效果(scale > 1.0)
398/// - 暂停效果(scale = 0.0)
399/// - 时间倒流效果(scale < 0.0)
400#[derive(Debug, Clone)]
401pub struct ScaledTime {
402    /// 原始时间资源
403    inner: Time,
404    /// 时间缩放因子
405    scale: f32,
406}
407
408impl ScaledTime {
409    /// 创建新的缩放时间包装器
410    /// 
411    /// # 参数
412    /// 
413    /// - `time`: 原始时间资源
414    /// - `scale`: 缩放因子
415    pub fn new(time: Time, scale: f32) -> Self {
416        Self {
417            inner: time,
418            scale,
419        }
420    }
421
422    /// 获取缩放因子
423    pub fn scale(&self) -> f32 {
424        self.scale
425    }
426
427    /// 设置缩放因子
428    pub fn set_scale(&mut self, scale: f32) {
429        self.scale = scale;
430    }
431
432    /// 获取缩放后的 delta time
433    ///
434    /// **注意**: `Duration` 不能为负,因此当 `scale < 0`(时间倒流)时返回 `Duration::ZERO`。
435    /// 如果需要支持负时间,请使用 [`delta_seconds()`](Self::delta_seconds) 代替。
436    pub fn delta(&self) -> Duration {
437        if self.scale >= 0.0 {
438            Duration::from_secs_f32(self.inner.delta_seconds() * self.scale)
439        } else {
440            // Duration 不支持负值,返回 ZERO
441            Duration::ZERO
442        }
443    }
444
445    /// 获取缩放后的 delta time(秒)
446    ///
447    /// 当 `scale < 0` 时返回负值(表示时间倒流),
448    /// 与 [`delta()`](Self::delta) 不同(后者因 `Duration` 限制返回 ZERO)。
449    pub fn delta_seconds(&self) -> f32 {
450        self.inner.delta_seconds() * self.scale
451    }
452
453    /// 获取原始(未缩放)的时间资源
454    pub fn inner(&self) -> &Time {
455        &self.inner
456    }
457
458    /// 获取原始(未缩放)的时间资源(可变引用)
459    pub fn inner_mut(&mut self) -> &mut Time {
460        &mut self.inner
461    }
462
463    /// 更新内部时间资源
464    pub fn update(&mut self) {
465        self.inner.update();
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472    use std::time::Duration;
473    use approx::assert_relative_eq;
474
475    #[test]
476    fn test_time_creation() {
477        let time = Time::new();
478        assert_eq!(time.frame_count(), 0);
479        assert_eq!(time.delta_seconds(), 0.0);
480        assert!(time.is_first_frame());
481    }
482
483    #[test]
484    fn test_time_update() {
485        let mut time = Time::new();
486        
487        // 第一次更新
488        time.update();
489        assert_eq!(time.frame_count(), 1);
490        assert_eq!(time.delta_seconds(), 0.0); // 第一帧 delta 为 0
491        assert!(!time.is_first_frame());
492        
493        // 模拟时间流逝
494        std::thread::sleep(Duration::from_millis(10));
495        time.update();
496        
497        assert_eq!(time.frame_count(), 2);
498        assert!(time.delta_seconds() > 0.0);
499        assert!(time.elapsed_seconds() > 0.0);
500    }
501
502    #[test]
503    fn test_fps_calculation() {
504        let mut time = Time::new();
505        
506        // 模拟稳定的帧率
507        for _ in 0..10 {
508            std::thread::sleep(Duration::from_millis(16)); // ~60 FPS
509            time.update();
510        }
511        
512        let fps = time.fps();
513        assert!(fps > 30.0 && fps < 120.0, "FPS {:.1} out of expected range", fps); // 宽容的范围,避免 CI 环境波动
514        
515        let instant_fps = time.instant_fps();
516        assert!(instant_fps > 0.0);
517    }
518
519    #[test]
520    fn test_time_reset() {
521        let mut time = Time::new();
522        time.update();
523        time.update();
524        
525        assert_eq!(time.frame_count(), 2);
526        
527        time.reset();
528        assert_eq!(time.frame_count(), 0);
529        assert!(time.is_first_frame());
530    }
531
532    #[test]
533    fn test_scaled_time() {
534        let mut time = Time::new();
535        std::thread::sleep(Duration::from_millis(10));
536        time.update();
537        
538        let original_delta = time.delta_seconds();
539        let scaled_time = time.with_scale(0.5);
540        
541        assert_eq!(scaled_time.scale(), 0.5);
542        assert_relative_eq!(scaled_time.delta_seconds(), original_delta * 0.5, epsilon = 1e-6);
543    }
544
545    #[test]
546    fn test_time_precision() {
547        let mut time = Time::new();
548
549        // 先进行一次更新以初始化时间
550        time.update();
551
552        // 等待一段时间
553        std::thread::sleep(Duration::from_millis(50));
554
555        // 再次更新以计算时间差
556        time.update();
557
558        let delta_f32 = time.delta_seconds();
559        let delta_f64 = time.delta_seconds_f64();
560        let delta_millis = time.delta_millis();
561
562        assert!(delta_f32 > 0.0, "delta_f32 should be positive, got: {}", delta_f32);
563        assert!(delta_f64 > 0.0, "delta_f64 should be positive, got: {}", delta_f64);
564        assert!(delta_millis > 0, "delta_millis should be positive, got: {}", delta_millis);
565
566        // 验证时间值在合理范围内(应该接近50ms,但允许一些误差)
567        assert!(delta_f32 >= 0.01 && delta_f32 <= 0.2, "delta_f32 out of expected range: {}", delta_f32);
568        assert!(delta_f64 >= 0.01 && delta_f64 <= 0.2, "delta_f64 out of expected range: {}", delta_f64);
569    }
570
571    #[test]
572    fn test_time_consistency() {
573        let mut time = Time::new();
574        let start_time = time.startup_time();
575
576        std::thread::sleep(Duration::from_millis(50));
577        time.update();
578
579        // 验证时间一致性
580        assert_eq!(time.startup_time(), start_time);
581        assert!(time.current_time() > start_time);
582        assert!(time.elapsed() > Duration::ZERO);
583
584        let manual_elapsed = time.current_time().duration_since(start_time);
585        let reported_elapsed = time.elapsed();
586
587        // 应该非常接近
588        let diff = if manual_elapsed > reported_elapsed {
589            manual_elapsed - reported_elapsed
590        } else {
591            reported_elapsed - manual_elapsed
592        };
593        assert!(diff < Duration::from_millis(1));
594    }
595
596    #[test]
597    fn test_time_multiple_resets() {
598        let mut time = Time::new();
599        for _ in 0..5 {
600            time.update();
601            time.reset();
602            assert_eq!(time.frame_count(), 0);
603            assert!(time.is_first_frame());
604        }
605    }
606
607    #[test]
608    fn test_time_first_update_zero_delta() {
609        let mut time = Time::new();
610        time.update();
611        // 第一次更新 delta 应为 0
612        assert_eq!(time.delta(), Duration::ZERO);
613        assert_eq!(time.delta_seconds(), 0.0);
614    }
615
616    #[test]
617    fn test_time_elapsed_monotonic() {
618        let mut time = Time::new();
619        let mut prev_elapsed = Duration::ZERO;
620        for _ in 0..10 {
621            time.update();
622            assert!(time.elapsed() >= prev_elapsed);
623            prev_elapsed = time.elapsed();
624        }
625    }
626
627    #[test]
628    fn test_scaled_time_zero_scale() {
629        let mut time = Time::new();
630        std::thread::sleep(Duration::from_millis(10));
631        time.update();
632
633        let scaled = time.with_scale(0.0);
634        assert_eq!(scaled.delta_seconds(), 0.0);
635        assert_eq!(scaled.delta(), Duration::ZERO);
636    }
637
638    #[test]
639    fn test_scaled_time_negative_scale() {
640        let mut time = Time::new();
641        // First update sets delta=0; we need a second update for non-zero delta
642        time.update();
643        std::thread::sleep(Duration::from_millis(10));
644        time.update();
645
646        let scaled = time.with_scale(-1.0);
647        // 负缩放时 delta() 返回 ZERO,但 delta_seconds() 返回负值
648        assert_eq!(scaled.delta(), Duration::ZERO);
649        assert!(scaled.delta_seconds() < 0.0);
650    }
651
652    #[test]
653    fn test_scaled_time_double_speed() {
654        let mut time = Time::new();
655        std::thread::sleep(Duration::from_millis(10));
656        time.update();
657
658        let original_delta = time.delta_seconds();
659        let scaled = time.with_scale(2.0);
660        assert_relative_eq!(scaled.delta_seconds(), original_delta * 2.0, epsilon = 1e-6);
661    }
662
663    #[test]
664    fn test_scaled_time_set_scale() {
665        let time = Time::new();
666        let mut scaled = time.with_scale(1.0);
667        assert_eq!(scaled.scale(), 1.0);
668
669        scaled.set_scale(0.5);
670        assert_eq!(scaled.scale(), 0.5);
671    }
672
673    #[test]
674    fn test_scaled_time_update() {
675        let time = Time::new();
676        let mut scaled = time.with_scale(1.0);
677
678        assert_eq!(scaled.inner().frame_count(), 0);
679        scaled.update();
680        assert_eq!(scaled.inner().frame_count(), 1);
681    }
682
683    #[test]
684    fn test_time_default() {
685        let time = Time::default();
686        assert_eq!(time.frame_count(), 0);
687        assert!(time.is_first_frame());
688    }
689
690    #[test]
691    fn test_time_fps_zero_when_no_frames() {
692        let time = Time::new();
693        assert_eq!(time.fps(), 0.0);
694        assert_eq!(time.instant_fps(), 0.0);
695    }
696}