use std::time::{Duration, Instant};
pub struct TimeState {
startup_time: Instant,
last_frame_time: Instant,
delta_time: Duration,
elapsed_time: Duration,
frame_count: u64,
frame_time_history: Vec<Duration>,
max_history: usize,
}
impl TimeState {
pub fn new() -> Self {
let now = Instant::now();
Self {
startup_time: now,
last_frame_time: now,
delta_time: Duration::ZERO,
elapsed_time: Duration::ZERO,
frame_count: 0,
frame_time_history: Vec::new(),
max_history: 60, }
}
pub fn update(&mut self) {
let now = Instant::now();
self.delta_time = now.duration_since(self.last_frame_time);
self.elapsed_time = now.duration_since(self.startup_time);
self.last_frame_time = now;
self.frame_count += 1;
self.frame_time_history.push(self.delta_time);
if self.frame_time_history.len() > self.max_history {
self.frame_time_history.remove(0);
}
}
pub fn delta_seconds(&self) -> f32 {
self.delta_time.as_secs_f32()
}
pub fn delta(&self) -> Duration {
self.delta_time
}
pub fn elapsed_seconds(&self) -> f32 {
self.elapsed_time.as_secs_f32()
}
pub fn elapsed(&self) -> Duration {
self.elapsed_time
}
pub fn frame_count(&self) -> u64 {
self.frame_count
}
pub fn fps(&self) -> f32 {
if self.frame_time_history.is_empty() {
return 0.0;
}
let total_time: Duration = self.frame_time_history.iter().sum();
let average_frame_time = total_time.as_secs_f32() / self.frame_time_history.len() as f32;
if average_frame_time > 0.0 {
1.0 / average_frame_time
} else {
0.0
}
}
pub fn average_frame_time_ms(&self) -> f32 {
if self.frame_time_history.is_empty() {
return 0.0;
}
let total_time: Duration = self.frame_time_history.iter().sum();
(total_time.as_secs_f32() / self.frame_time_history.len() as f32) * 1000.0
}
pub fn reset(&mut self) {
let now = Instant::now();
self.startup_time = now;
self.last_frame_time = now;
self.delta_time = Duration::ZERO;
self.elapsed_time = Duration::ZERO;
self.frame_count = 0;
self.frame_time_history.clear();
}
pub fn is_first_frame(&self) -> bool {
self.frame_count <= 1
}
pub fn time_scale(&self, scale: f32) -> f32 {
self.delta_seconds() * scale
}
}
impl Default for TimeState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Timer {
duration: Duration,
elapsed: Duration,
repeating: bool,
finished: bool,
}
impl Timer {
pub fn new(duration: Duration, repeating: bool) -> Self {
Self {
duration,
elapsed: Duration::ZERO,
repeating,
finished: false,
}
}
pub fn once(duration: Duration) -> Self {
Self::new(duration, false)
}
pub fn repeating(duration: Duration) -> Self {
Self::new(duration, true)
}
pub fn tick(&mut self, delta: Duration) -> bool {
if self.finished && !self.repeating {
return false;
}
self.elapsed += delta;
if self.elapsed >= self.duration {
self.finished = true;
if self.repeating {
self.elapsed = Duration::ZERO;
self.finished = false;
}
true
} else {
false
}
}
pub fn just_finished(&self) -> bool {
self.finished
}
pub fn progress(&self) -> f32 {
if self.duration.is_zero() {
1.0
} else {
(self.elapsed.as_secs_f32() / self.duration.as_secs_f32()).min(1.0)
}
}
pub fn remaining(&self) -> Duration {
self.duration.saturating_sub(self.elapsed)
}
pub fn reset(&mut self) {
self.elapsed = Duration::ZERO;
self.finished = false;
}
}
pub mod utils {
use super::*;
pub fn seconds(secs: f32) -> Duration {
Duration::from_secs_f32(secs)
}
pub fn milliseconds(ms: f32) -> Duration {
Duration::from_secs_f32(ms / 1000.0)
}
pub fn smooth_lerp(from: f32, to: f32, progress: f32) -> f32 {
let smooth_progress = progress * progress * (3.0 - 2.0 * progress);
from + (to - from) * smooth_progress
}
pub fn lerp(from: f32, to: f32, progress: f32) -> f32 {
from + (to - from) * progress
}
pub fn exp_decay(current: f32, target: f32, decay_rate: f32, delta_time: f32) -> f32 {
target + (current - target) * (-decay_rate * delta_time).exp()
}
pub fn spring_damper(
current: f32,
target: f32,
velocity: &mut f32,
spring_strength: f32,
damping: f32,
delta_time: f32,
) -> f32 {
let force = (target - current) * spring_strength - *velocity * damping;
*velocity += force * delta_time;
current + *velocity * delta_time
}
}