Skip to main content

goud_engine/core/events/
frame.rs

1//! Frame timing events.
2//!
3//! Contains events emitted at the start and end of each engine frame.
4
5/// Emitted at the beginning of each frame.
6///
7/// This event provides timing information for the new frame. Use it to
8/// update game logic, animations, and physics with consistent delta time.
9///
10/// # Timing Fields
11///
12/// - `frame`: Monotonically increasing frame counter (starts at 0)
13/// - `delta`: Time elapsed since the previous frame in seconds
14/// - `total_time`: Total time elapsed since engine start in seconds
15///
16/// # Example
17///
18/// ```ignore
19/// use goud_engine::core::events::FrameStarted;
20/// use goud_engine::core::event::Events;
21///
22/// fn update_game(events: &Events<FrameStarted>) {
23///     let mut reader = events.reader();
24///     for event in reader.read() {
25///         // Update physics with delta time
26///         let velocity_change = 9.8 * event.delta; // gravity
27///
28///         // Check if it's a new second for FPS display
29///         let prev_second = (event.total_time - event.delta as f64) as u64;
30///         let curr_second = event.total_time as u64;
31///         if curr_second > prev_second {
32///             println!("Frame {} - Total time: {:.1}s", event.frame, event.total_time);
33///         }
34///     }
35/// }
36/// ```
37#[derive(Debug, Clone, Copy, PartialEq)]
38pub struct FrameStarted {
39    /// The frame number (0-indexed, monotonically increasing).
40    pub frame: u64,
41    /// Time elapsed since the previous frame, in seconds.
42    pub delta: f32,
43    /// Total time elapsed since engine start, in seconds.
44    pub total_time: f64,
45}
46
47impl FrameStarted {
48    /// Creates a new `FrameStarted` event.
49    #[must_use]
50    pub fn new(frame: u64, delta: f32, total_time: f64) -> Self {
51        Self {
52            frame,
53            delta,
54            total_time,
55        }
56    }
57
58    /// Returns the current frames per second based on delta time.
59    ///
60    /// Returns `f32::INFINITY` if delta is 0 (to avoid division by zero).
61    #[must_use]
62    pub fn fps(&self) -> f32 {
63        if self.delta == 0.0 {
64            f32::INFINITY
65        } else {
66            1.0 / self.delta
67        }
68    }
69}
70
71impl Default for FrameStarted {
72    fn default() -> Self {
73        Self::new(0, 0.0, 0.0)
74    }
75}
76
77/// Emitted at the end of each frame, after all systems have run.
78///
79/// Use this for cleanup, profiling, or operations that should happen
80/// after all game logic but before the next frame begins.
81///
82/// # Example
83///
84/// ```rust
85/// use goud_engine::core::events::FrameEnded;
86/// use goud_engine::core::event::Events;
87///
88/// fn end_frame_profiling(events: &Events<FrameEnded>) {
89///     let mut reader = events.reader();
90///     for event in reader.read() {
91///         // Record frame timing for profiling
92///         println!("Frame {} completed in {:.2}ms", event.frame, event.frame_time_ms);
93///     }
94/// }
95/// ```
96#[derive(Debug, Clone, Copy, PartialEq)]
97pub struct FrameEnded {
98    /// The frame number that just completed.
99    pub frame: u64,
100    /// Time taken to process this frame, in milliseconds.
101    pub frame_time_ms: f32,
102}
103
104impl FrameEnded {
105    /// Creates a new `FrameEnded` event.
106    #[must_use]
107    pub fn new(frame: u64, frame_time_ms: f32) -> Self {
108        Self {
109            frame,
110            frame_time_ms,
111        }
112    }
113
114    /// Returns the frame time in seconds.
115    #[must_use]
116    pub fn frame_time_secs(&self) -> f32 {
117        self.frame_time_ms / 1000.0
118    }
119}
120
121impl Default for FrameEnded {
122    fn default() -> Self {
123        Self::new(0, 0.0)
124    }
125}