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}