summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--game/src/main.rs3
-rw-r--r--graphics/src/lib.rs9
-rw-r--r--graphics/src/render.rs40
-rw-r--r--graphics/src/timer.rs67
4 files changed, 89 insertions, 30 deletions
diff --git a/game/src/main.rs b/game/src/main.rs
index 9449710..28498dc 100644
--- a/game/src/main.rs
+++ b/game/src/main.rs
@@ -68,7 +68,8 @@ impl Game {
};
// Update game state
- self.dungeon.update(inputs, self.window.delta_time());
+ self.dungeon
+ .update(inputs, self.window.delta_time().as_secs_f32());
// Draw a single frame
self.window.draw_frame(&self.dungeon);
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index 8dccd63..43bf3ac 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -1,6 +1,8 @@
//! The `graphics` crate contains the core functionality for
//! rendering using the `raylib` library.
+use std::time::Duration;
+
use dungeon::Dungeon;
use raylib::prelude::*;
@@ -9,6 +11,7 @@ use crate::render::Renderer;
mod audio;
mod render;
+mod timer;
/// The `Error` type used within this crate
pub type Error = Box<dyn std::error::Error>;
@@ -77,7 +80,6 @@ impl From<Key> for KeyboardKey {
}
/// The `WindowBuilder` type allows setting partial options for the window
-#[derive(Debug)]
pub struct WindowBuilder<'a> {
title: &'a str,
width: u16,
@@ -182,7 +184,6 @@ impl Default for WindowBuilder<'_> {
}
/// The `Window` type represents the game window
-#[derive(Debug)]
pub struct Window {
// persistant renderer
renderer: Renderer,
@@ -211,8 +212,8 @@ impl Window {
}
/// Returns the per frame delta time
- pub fn delta_time(&self) -> f32 {
- self.handle.get_frame_time()
+ pub fn delta_time(&self) -> Duration {
+ self.renderer.delta_time()
}
/// Returns if the provided `Key` has just been pressed
diff --git a/graphics/src/render.rs b/graphics/src/render.rs
index 78ff94d..6fd2ea4 100644
--- a/graphics/src/render.rs
+++ b/graphics/src/render.rs
@@ -1,4 +1,4 @@
-use std::{f32, io::Write};
+use std::{f32, io::Write, time::Duration};
use dungeon::{
Direction, Dungeon, Entity, EntityKind, Floor, Item, MAP_SIZE, PLAYER_INVENTORY_SIZE,
@@ -13,6 +13,8 @@ use raylib::{
texture::{RaylibTexture2D, RenderTexture2D, Texture2D},
};
+use crate::timer::Timer;
+
macro_rules! downcast {
($usize:expr, $type:ty) => {
<$type>::try_from($usize).unwrap_or(<$type>::MAX)
@@ -124,7 +126,6 @@ const UI_COL2: u16 = UI_COL1 + FONT_SIZE * 10;
/// The maxmimum length any text string can be
const MAX_TEXT_LEN: usize = 16;
-#[derive(Debug)]
struct Textures {
// Tilemap
atlas: Texture2D,
@@ -156,7 +157,6 @@ impl Textures {
}
}
-#[derive(Debug)]
pub struct Renderer {
/* Persistant Render Data */
/// All loaded image resources/textures
@@ -175,11 +175,8 @@ pub struct Renderer {
debug: bool,
/// Scratch buffer to format text into
text_buf: [u8; MAX_TEXT_LEN],
- /* Per Frame Caculated Data */
- /// Last known FPS
- fps: u32,
- /// The current frame
- frame: u64,
+ /// Frame timer
+ timer: Timer,
}
impl Renderer {
pub fn new(handle: &mut RaylibHandle, thread: &RaylibThread) -> crate::Result<Self> {
@@ -206,8 +203,7 @@ impl Renderer {
framebuffer: Some(framebuffer),
debug: false,
text_buf: [0u8; MAX_TEXT_LEN],
- fps: 0,
- frame: 0,
+ timer: Timer::new(),
})
}
@@ -215,6 +211,10 @@ impl Renderer {
self.debug = !self.debug;
}
+ pub fn delta_time(&self) -> Duration {
+ self.timer.delta_time()
+ }
+
pub fn draw_frame(
&mut self,
handle: &mut RaylibHandle,
@@ -224,12 +224,12 @@ impl Renderer {
let render_width = handle.get_render_width() as f32;
let render_height = handle.get_render_height() as f32;
- // Update render info before drawing
- self.update_per_frame_data(handle);
-
// Start the frame
let mut r = handle.begin_drawing(t);
+ // Update render info before drawing
+ self.timer.update();
+
// Update cached tilemaps
self.update_tilemaps(&mut r, t, &dungeon.floor);
@@ -263,16 +263,6 @@ impl Renderer {
};
}
- /// Update frame metadata while we still have access to the
- /// `RaylibHandle`. These fields are inaccessable once it's
- /// turned into a `RaylibDrawHandle`.
- fn update_per_frame_data(&mut self, handle: &RaylibHandle) {
- // Get last known fps
- self.fps = handle.get_fps();
- // Update frame counter
- self.frame += 1;
- }
-
/// Draws the game dungeon
fn draw_dungeon<R>(&mut self, r: &mut R, dungeon: &Dungeon)
where
@@ -668,7 +658,7 @@ impl Renderer {
let player = &dungeon.player;
// Draw FPS
- draw_text!(self, r, UI_COL1, UI_ROW1, "FPS {}", self.fps);
+ draw_text!(self, r, UI_COL1, UI_ROW1, "FPS {}", self.timer.get_fps());
// Draw Player position
let (x, y) = &player.entity.pos.xy();
@@ -687,7 +677,7 @@ impl Renderer {
draw_text!(self, r, UI_COL2, UI_ROW2, "{hash:016X}");
// Draw current frame number
- let frame = &self.frame;
+ let frame = self.timer.get_frame();
draw_text!(self, r, UI_COL2, UI_ROW3, "FRAME {frame}");
}
diff --git a/graphics/src/timer.rs b/graphics/src/timer.rs
new file mode 100644
index 0000000..8310776
--- /dev/null
+++ b/graphics/src/timer.rs
@@ -0,0 +1,67 @@
+use std::{
+ ops::{Add, Div, Mul},
+ time::{Duration, Instant},
+};
+
+/// How often to update the fps interval on screen
+const FPS_INTERVAL: Duration = Duration::from_millis(250);
+
+/// How much to smooth he `delta_avg` amount (`AVG_RATIO`:1)
+const AVG_RATIO: u32 = 10;
+
+/// Keeps track of frame intervals and FPS
+pub struct Timer {
+ /// Last time updated
+ previous: Instant,
+ /// Current time
+ now: Instant,
+ /// Rolling delta time average
+ delta_avg: Duration,
+ /// Computed fps
+ fps: f64,
+ /// Last time the fps was updated
+ fps_time: Instant,
+ /// Frame count
+ count: u32,
+}
+impl Timer {
+ pub fn new() -> Self {
+ let now = Instant::now();
+ Self {
+ previous: now,
+ now,
+ delta_avg: Duration::ZERO,
+ fps: 0.0,
+ fps_time: now,
+ count: 0,
+ }
+ }
+
+ pub fn update(&mut self) {
+ self.previous = self.now;
+ self.now = Instant::now();
+ self.delta_avg = self
+ .delta_avg
+ .mul(AVG_RATIO)
+ .add(self.delta_time())
+ .div(AVG_RATIO + 1);
+ if (self.now - self.fps_time) > FPS_INTERVAL {
+ self.fps = 1.0 / self.delta_avg.as_secs_f64();
+ self.fps_time = self.now;
+ }
+ self.count += 1;
+ }
+
+ pub fn get_frame(&self) -> u32 {
+ self.count
+ }
+
+ #[expect(clippy::cast_possible_truncation)]
+ pub fn get_fps(&self) -> u32 {
+ self.fps.ceil() as u32
+ }
+
+ pub fn delta_time(&self) -> Duration {
+ self.now - self.previous
+ }
+}