mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
fix particle de-synchronisation with paused game
This commit is contained in:
parent
9d7cbd9888
commit
d38aab8000
@ -16,7 +16,7 @@ use dot_vox::DotVoxData;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, WorldExt};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub struct ParticleMgr {
|
pub struct ParticleMgr {
|
||||||
@ -72,17 +72,16 @@ impl ParticleMgr {
|
|||||||
|
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
||||||
if scene_data.particles_enabled {
|
if scene_data.particles_enabled {
|
||||||
let now = Instant::now();
|
// update timings
|
||||||
|
self.scheduler.maintain(scene_data.state.get_time());
|
||||||
|
|
||||||
// remove dead Particle
|
// remove dead Particle
|
||||||
self.particles.retain(|p| p.alive_until > now);
|
self.particles
|
||||||
|
.retain(|p| p.alive_until > scene_data.state.get_time());
|
||||||
|
|
||||||
// add new Particle
|
// add new Particle
|
||||||
self.maintain_body_particles(scene_data);
|
self.maintain_body_particles(scene_data);
|
||||||
self.maintain_boost_particles(scene_data);
|
self.maintain_boost_particles(scene_data);
|
||||||
|
|
||||||
// update timings
|
|
||||||
self.scheduler.maintain();
|
|
||||||
} else {
|
} else {
|
||||||
// remove all particle lifespans
|
// remove all particle lifespans
|
||||||
self.particles.clear();
|
self.particles.clear();
|
||||||
@ -305,36 +304,49 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model<Particl
|
|||||||
/// Accumulates heartbeats to be consumed on the next tick.
|
/// Accumulates heartbeats to be consumed on the next tick.
|
||||||
struct HeartbeatScheduler {
|
struct HeartbeatScheduler {
|
||||||
/// Duration = Heartbeat Frequency/Intervals
|
/// Duration = Heartbeat Frequency/Intervals
|
||||||
/// Instant = Last update time
|
/// f64 = Last update time
|
||||||
/// u8 = number of heartbeats since last update
|
/// u8 = number of heartbeats since last update
|
||||||
/// - if it's more frequent then tick rate, it could be 1 or more.
|
/// - if it's more frequent then tick rate, it could be 1 or more.
|
||||||
/// - if it's less frequent then tick rate, it could be 1 or 0.
|
/// - if it's less frequent then tick rate, it could be 1 or 0.
|
||||||
/// - if it's equal to the tick rate, it could be between 2 and 0, due to
|
/// - if it's equal to the tick rate, it could be between 2 and 0, due to
|
||||||
/// delta time variance etc.
|
/// delta time variance etc.
|
||||||
timers: HashMap<Duration, (Instant, u8)>,
|
timers: HashMap<Duration, (f64, u8)>,
|
||||||
|
|
||||||
|
last_known_time: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeartbeatScheduler {
|
impl HeartbeatScheduler {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
HeartbeatScheduler {
|
HeartbeatScheduler {
|
||||||
timers: HashMap::new(),
|
timers: HashMap::new(),
|
||||||
|
last_known_time: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// updates the last elapsed times and elasped counts
|
/// updates the last elapsed times and elasped counts
|
||||||
/// this should be called once, and only once per tick.
|
/// this should be called once, and only once per tick.
|
||||||
pub fn maintain(&mut self) {
|
pub fn maintain(&mut self, now: f64) {
|
||||||
for (frequency, (last_update, heartbeats)) in self.timers.iter_mut() {
|
self.last_known_time = now;
|
||||||
// the number of iterations since last update
|
|
||||||
*heartbeats =
|
|
||||||
// TODO: use nightly api once stable; https://github.com/rust-lang/rust/issues/63139
|
|
||||||
(last_update.elapsed().as_secs_f32() / frequency.as_secs_f32()).floor() as u8;
|
|
||||||
|
|
||||||
// Instant::now() minus the heart beat count precision,
|
for (frequency, (last_update, heartbeats)) in self.timers.iter_mut() {
|
||||||
// or alternatively as expressed below.
|
// the number of frequency cycles that have occurred.
|
||||||
*last_update += frequency.mul_f32(*heartbeats as f32);
|
let total_heartbeats = (now - *last_update) / frequency.as_secs_f64();
|
||||||
// Note: we want to preserve incomplete heartbeats, and include them
|
|
||||||
// in the next update.
|
// exclude partial frequency cycles
|
||||||
|
let full_heartbeats = total_heartbeats.floor();
|
||||||
|
|
||||||
|
*heartbeats = full_heartbeats as u8;
|
||||||
|
|
||||||
|
// the remaining partial freqency cycle, as a decimal.
|
||||||
|
let partial_heartbeat = total_heartbeats - full_heartbeats;
|
||||||
|
|
||||||
|
// the remaining partial freqency cycle, as a unit of time(f64).
|
||||||
|
let partial_heartbeat_as_time = frequency.mul_f64(partial_heartbeat).as_secs_f64();
|
||||||
|
|
||||||
|
// now minus the left over heart beat count precision as seconds,
|
||||||
|
// Note: we want to preserve incomplete heartbeats, and roll them
|
||||||
|
// over into the next update.
|
||||||
|
*last_update = now - partial_heartbeat_as_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,9 +357,11 @@ impl HeartbeatScheduler {
|
|||||||
/// - if it's equal to the tick rate, it could be between 2 and 0, due to
|
/// - if it's equal to the tick rate, it could be between 2 and 0, due to
|
||||||
/// delta time variance.
|
/// delta time variance.
|
||||||
pub fn heartbeats(&mut self, frequency: Duration) -> u8 {
|
pub fn heartbeats(&mut self, frequency: Duration) -> u8 {
|
||||||
|
let last_known_time = self.last_known_time;
|
||||||
|
|
||||||
self.timers
|
self.timers
|
||||||
.entry(frequency)
|
.entry(frequency)
|
||||||
.or_insert_with(|| (Instant::now(), 0))
|
.or_insert_with(|| (last_known_time, 0))
|
||||||
.1
|
.1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,14 +369,14 @@ impl HeartbeatScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Particle {
|
struct Particle {
|
||||||
alive_until: Instant, // created_at + lifespan
|
alive_until: f64, // created_at + lifespan
|
||||||
instance: ParticleInstance,
|
instance: ParticleInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Particle {
|
impl Particle {
|
||||||
fn new(lifespan: Duration, time: f64, mode: ParticleMode, pos: Vec3<f32>) -> Self {
|
fn new(lifespan: Duration, time: f64, mode: ParticleMode, pos: Vec3<f32>) -> Self {
|
||||||
Particle {
|
Particle {
|
||||||
alive_until: Instant::now() + lifespan,
|
alive_until: time + lifespan.as_secs_f64(),
|
||||||
instance: ParticleInstance::new(time, mode, pos),
|
instance: ParticleInstance::new(time, mode, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user