use std::time::Duration; use bevy_replicon::client::confirm_history::ConfirmHistory; use bevy_replicon::prelude::RepliconTick; use web_time::Instant; use crate::prelude::*; use crate::shared::plugins::TICK_RATE; const MAX_INTERPOLATION_DURATION: Duration = Duration::from_millis(500); #[derive(Component)] pub struct TransformInterpolation { start: Transform, end: Transform, end_tick: RepliconTick, duration: Duration, end_received_at: Instant, } impl TransformInterpolation { pub fn new(transform: Transform, tick: RepliconTick) -> Self { Self { start: transform, end: transform, end_tick: tick, duration: Duration::ZERO, end_received_at: Instant::now(), } } } pub fn transform_interpolation_plugin(app: &mut App) { app.add_systems(Update, (record_transform_changes, apply_transform_interpolation).chain()); } // must run before `apply_transform_interpolation` fn record_transform_changes( mut entities: Query<(&Transform, &ConfirmHistory, &mut TransformInterpolation), Changed>, ) { let now = Instant::now(); for (transform, confirm_history, mut interpolation) in &mut entities { let tick = confirm_history.last_tick(); let tick_delta = tick - interpolation.end_tick; let duration = Duration::from_secs_f64(tick_delta as f64 / TICK_RATE).min(MAX_INTERPOLATION_DURATION); interpolation.start = interpolation.end; interpolation.end = *transform; interpolation.end_tick = tick; interpolation.duration = duration; interpolation.end_received_at = now; } } fn apply_transform_interpolation(mut entities: Query<(&mut Transform, &TransformInterpolation)>) { let now = Instant::now(); for (mut transform, interpolation) in &mut entities { let t = if interpolation.duration > Duration::ZERO { (now.duration_since(interpolation.end_received_at).as_secs_f64() / interpolation.duration.as_secs_f64()) .clamp(0.0, 1.0) as f32 } else { 1.0 }; *transform.bypass_change_detection() = Transform { translation: interpolation.start.translation.lerp(interpolation.end.translation, t), rotation: interpolation.start.rotation.slerp(interpolation.end.rotation, t), scale: interpolation.start.scale.lerp(interpolation.end.scale, t), }; } }