use instant::Instant;
const TICKS_PER_SECOND: f64 = 10_000_000.0;
#[inline]
fn ticks_to_seconds(p_ticks: f64) -> f64 {
p_ticks / TICKS_PER_SECOND
}
#[inline]
fn seconds_to_ticks(p_seconds: f64) -> f64 {
p_seconds * TICKS_PER_SECOND
}
#[derive(Debug, Clone, Copy)]
pub struct WGameTime {
pub last_time: Instant,
pub max_delta: f64,
pub elapsed_ticks: f64,
pub total_ticks: f64,
pub left_over_ticks: f64,
pub frame_count: u32,
pub fps: u32,
pub frames_this_second: u32,
pub seconds_counter: f64,
pub fixed_time_step: bool,
pub target_elapsed_ticks: f64,
}
impl Default for WGameTime {
fn default() -> Self {
Self {
last_time: Instant::now(),
max_delta: 313_918.0,
elapsed_ticks: 0.0,
total_ticks: 0.0,
left_over_ticks: 0.0,
frame_count: 0,
fps: 0,
frames_this_second: 0,
seconds_counter: 0.0,
fixed_time_step: false,
target_elapsed_ticks: TICKS_PER_SECOND / 60.0,
}
}
}
impl WGameTime {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
self.last_time = Instant::now();
self.left_over_ticks = 0.0;
self.fps = 0;
self.frames_this_second = 0;
self.seconds_counter = 0.0;
}
pub fn set_fixed_time_step(&mut self, p_value: bool) {
self.fixed_time_step = p_value;
}
#[must_use]
pub const fn get_fixed_time_step(&self) -> bool {
self.fixed_time_step
}
#[must_use]
pub fn get_elapsed_seconds(&self) -> f64 {
ticks_to_seconds(self.elapsed_ticks)
}
#[must_use]
pub fn get_total_elapsed_seconds(&self) -> f64 {
ticks_to_seconds(self.total_ticks)
}
pub fn set_target_elapsed_seconds(&mut self, p_value: f64) {
self.target_elapsed_ticks = seconds_to_ticks(p_value);
}
pub async fn tick_fn(&mut self, mut p_on_tick_fn: impl FnMut()) {
let current_time = Instant::now();
let mut time_delta = (current_time - self.last_time).as_secs_f64();
self.last_time = current_time;
self.seconds_counter += time_delta;
if time_delta > self.max_delta {
time_delta = self.max_delta;
}
time_delta *= TICKS_PER_SECOND;
let last_frame_count = self.frame_count;
if self.fixed_time_step {
let abs_ticks = f64::abs(time_delta - self.target_elapsed_ticks);
let ticks_per_sec_4 = TICKS_PER_SECOND / 4000.0;
if abs_ticks < ticks_per_sec_4 {
time_delta = self.target_elapsed_ticks;
}
self.left_over_ticks += time_delta;
while self.left_over_ticks >= self.target_elapsed_ticks {
self.elapsed_ticks = self.target_elapsed_ticks;
self.total_ticks += self.target_elapsed_ticks;
self.left_over_ticks -= self.target_elapsed_ticks;
self.frame_count += 1;
p_on_tick_fn();
}
} else {
self.elapsed_ticks = time_delta;
self.total_ticks += time_delta;
self.left_over_ticks = 0.0;
self.frame_count += 1;
p_on_tick_fn();
}
self.frames_this_second += self.frame_count - last_frame_count;
if self.seconds_counter >= 1.0 {
self.fps = self.frames_this_second;
self.frames_this_second = 0;
self.seconds_counter %= 1.0;
}
}
pub fn tick(&mut self) {
let current_time = Instant::now();
let mut time_delta = (current_time - self.last_time).as_secs_f64();
self.last_time = current_time;
self.seconds_counter += time_delta;
if time_delta > self.max_delta {
time_delta = self.max_delta;
}
time_delta *= TICKS_PER_SECOND;
let last_frame_count = self.frame_count;
if self.fixed_time_step {
let abs_ticks = f64::abs(time_delta - self.target_elapsed_ticks);
let ticks_per_sec_4 = TICKS_PER_SECOND / 4000.0;
if abs_ticks < ticks_per_sec_4 {
time_delta = self.target_elapsed_ticks;
}
self.left_over_ticks += time_delta;
while self.left_over_ticks >= self.target_elapsed_ticks {
self.elapsed_ticks = self.target_elapsed_ticks;
self.total_ticks += self.target_elapsed_ticks;
self.left_over_ticks -= self.target_elapsed_ticks;
self.frame_count += 1;
}
} else {
self.elapsed_ticks = time_delta;
self.total_ticks += time_delta;
self.left_over_ticks = 0.0;
self.frame_count += 1;
}
self.frames_this_second += self.frame_count - last_frame_count;
if self.seconds_counter >= 1.0 {
self.fps = self.frames_this_second;
self.frames_this_second = 0;
self.seconds_counter %= 1.0;
}
}
}