use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct Time {
start: Instant,
last_frame: Instant,
elapsed_secs: f32,
delta_secs: f32,
frame_count: u64,
fps: f32,
fps_frame_count: u64,
fps_update_time: Instant,
fps_update_interval: Duration,
paused: bool,
pause_elapsed: Duration,
fixed_delta: Option<f32>,
time_scale: f32,
}
impl Time {
pub fn new() -> Self {
let now = Instant::now();
Self {
start: now,
last_frame: now,
elapsed_secs: 0.0,
delta_secs: 0.0,
frame_count: 0,
fps: 0.0,
fps_frame_count: 0,
fps_update_time: now,
fps_update_interval: Duration::from_millis(500),
paused: false,
pause_elapsed: Duration::ZERO,
fixed_delta: None,
time_scale: 1.0,
}
}
pub fn update(&mut self) -> (f32, f32) {
let now = Instant::now();
if self.paused {
self.delta_secs = 0.0;
return (self.elapsed_secs, self.delta_secs);
}
let raw_delta = now.duration_since(self.last_frame).as_secs_f32();
self.delta_secs = self.fixed_delta.unwrap_or(raw_delta) * self.time_scale;
self.last_frame = now;
let raw_elapsed = now.duration_since(self.start) - self.pause_elapsed;
self.elapsed_secs = raw_elapsed.as_secs_f32() * self.time_scale;
self.frame_count += 1;
let fps_elapsed = now.duration_since(self.fps_update_time);
if fps_elapsed >= self.fps_update_interval {
let frames_since = self.frame_count - self.fps_frame_count;
self.fps = frames_since as f32 / fps_elapsed.as_secs_f32();
self.fps_frame_count = self.frame_count;
self.fps_update_time = now;
}
(self.elapsed_secs, self.delta_secs)
}
#[inline]
pub fn elapsed(&self) -> f32 {
self.elapsed_secs
}
#[inline]
pub fn delta(&self) -> f32 {
self.delta_secs
}
#[inline]
pub fn frame(&self) -> u64 {
self.frame_count
}
#[inline]
pub fn fps(&self) -> f32 {
self.fps
}
#[inline]
pub fn is_paused(&self) -> bool {
self.paused
}
#[inline]
pub fn time_scale(&self) -> f32 {
self.time_scale
}
pub fn pause(&mut self) {
if !self.paused {
self.paused = true;
}
}
pub fn resume(&mut self) {
if self.paused {
let now = Instant::now();
self.pause_elapsed += now.duration_since(self.last_frame);
self.last_frame = now;
self.paused = false;
}
}
pub fn toggle_pause(&mut self) {
if self.paused {
self.resume();
} else {
self.pause();
}
}
pub fn set_fixed_delta(&mut self, delta: Option<f32>) {
self.fixed_delta = delta;
}
pub fn set_time_scale(&mut self, scale: f32) {
self.time_scale = scale.max(0.0);
}
pub fn reset(&mut self) {
let now = Instant::now();
self.start = now;
self.last_frame = now;
self.elapsed_secs = 0.0;
self.delta_secs = 0.0;
self.frame_count = 0;
self.fps = 0.0;
self.fps_frame_count = 0;
self.fps_update_time = now;
self.paused = false;
self.pause_elapsed = Duration::ZERO;
}
#[inline]
pub fn start_instant(&self) -> Instant {
self.start
}
#[inline]
pub fn elapsed_duration(&self) -> Duration {
self.start.elapsed() - self.pause_elapsed
}
#[inline]
pub fn delta_duration(&self) -> Duration {
Duration::from_secs_f32(self.delta_secs)
}
}
impl Default for Time {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_time_new() {
let time = Time::new();
assert_eq!(time.frame(), 0);
assert!(!time.is_paused());
assert_eq!(time.time_scale(), 1.0);
}
#[test]
fn test_time_update() {
let mut time = Time::new();
thread::sleep(Duration::from_millis(10));
let (elapsed, delta) = time.update();
assert!(elapsed > 0.0);
assert!(delta > 0.0);
assert_eq!(time.frame(), 1);
}
#[test]
fn test_time_pause() {
let mut time = Time::new();
time.update();
time.pause();
assert!(time.is_paused());
let elapsed_before = time.elapsed();
thread::sleep(Duration::from_millis(10));
time.update();
assert_eq!(time.elapsed(), elapsed_before);
assert_eq!(time.delta(), 0.0);
}
#[test]
fn test_time_scale() {
let mut time = Time::new();
time.set_time_scale(2.0);
assert_eq!(time.time_scale(), 2.0);
time.set_time_scale(-1.0);
assert_eq!(time.time_scale(), 0.0);
}
#[test]
fn test_fixed_delta() {
let mut time = Time::new();
time.set_fixed_delta(Some(1.0 / 60.0));
thread::sleep(Duration::from_millis(100));
time.update();
let expected = 1.0 / 60.0;
assert!((time.delta() - expected).abs() < 0.0001);
}
}