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}