~starkingdoms/starkingdoms

ref: 011a026fb2c7d43d688798bdeb8ea70a6315b7b3 starkingdoms/crates/unified/src/client/interpolation.rs -rw-r--r-- 2.4 KiB
011a026f — core netcode: rd3 6 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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<Transform>>,
) {
    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),
        };
    }
}