M crates/unified/src/client/interpolation.rs => crates/unified/src/client/interpolation.rs +51 -13
@@ 1,3 1,4 @@
+use std::f32::consts::PI;
use std::time::{Duration};
use avian2d::parry::transformation::utils::transform;
use crate::prelude::*;
@@ 5,34 6,71 @@ use crate::prelude::*;
pub fn interpolation_plugin(app: &mut App) {
app
.add_systems(Update, update_interpolation_info)
+ .add_systems(Update, sync_non_interpolated_transforms)
.add_systems(Update, do_interpolation);
}
#[derive(Component, Debug)]
-pub struct TransformInterpolationInfo {
+pub struct TranslationInterpolationInfo {
pub last_dt: Duration,
pub this_tick_start: bevy::platform::time::Instant,
- pub latest_transform: Transform,
- pub last_transform: Transform,
+ pub latest_position: Vec2,
+ pub last_position: Vec2,
+}
+#[derive(Component, Debug)]
+pub struct RotationInterpolationInfo {
+ pub last_dt: Duration,
+ pub this_tick_start: bevy::platform::time::Instant,
+ pub last_angular_velocity: f32,
+ pub latest_rotation: f32,
+ pub last_rotation: f32,
}
fn update_interpolation_info(
- mut interpolation_query: Query<(&Transform, &mut TransformInterpolationInfo), Changed<Transform>>,
- time: Res<Time>,
+ mut interpolation_pos_query: Query<(Entity, &Position, &mut TranslationInterpolationInfo), Changed<Position>>,
+ mut interpolation_rot_query: Query<(&Rotation, &AngularVelocity, &mut RotationInterpolationInfo), Changed<Rotation>>,
) {
- for (transform, mut info) in interpolation_query.iter_mut() {
+ for (entity, position, mut info) in interpolation_pos_query.iter_mut() {
info.last_dt = info.this_tick_start.elapsed();
info.this_tick_start = bevy::platform::time::Instant::now();
- info.last_transform = info.latest_transform;
- info.latest_transform = transform.clone();
+ info.last_position = info.latest_position;
+ info.latest_position = position.as_vec2();
+ }
+ for (rotation, angular_velocity, mut info) in interpolation_rot_query.iter_mut() {
+ info.last_dt = info.this_tick_start.elapsed();
+ info.this_tick_start = bevy::platform::time::Instant::now();
+ let avg_angular_velocity = (angular_velocity.0.val_num_f32() + info.last_angular_velocity) / 2.0;
+ let delta_rotation = avg_angular_velocity * info.last_dt.as_secs_f32();
+ let num_revolutions = (delta_rotation / (2.0*PI)).round();
+ info.last_rotation = info.latest_rotation;
+ let mut rotation_offset = 0.0;
+ let rotation = rotation.as_radians().val_num_f32();
+ if delta_rotation + info.last_rotation >= PI {
+ rotation_offset = 2.0*PI;
+ } else if delta_rotation + info.last_rotation < -PI {
+ rotation_offset = -2.0*PI;
+ }
+ info.latest_rotation = rotation + rotation_offset + num_revolutions*2.0*PI;
+ info.last_angular_velocity = angular_velocity.0.val_num_f32();
+ }
+}
+fn sync_non_interpolated_transforms(
+ mut query: Query<(&mut Transform, &Position), (Changed<Position>, Without<TranslationInterpolationInfo>)>,
+) {
+ for (mut transform, position) in &mut query {
+ transform.translation = position.as_vec2().extend(0.0);
}
}
fn do_interpolation(
- mut interpolation_query: Query<(&mut Transform, &TransformInterpolationInfo)>,
+ mut interpolation_query: Query<(Entity, &mut Transform, &TranslationInterpolationInfo, &RotationInterpolationInfo)>,
) {
- for (mut transform, info) in &mut interpolation_query {
- let dt = bevy::platform::time::Instant::now() - info.this_tick_start;
- let progress = dt.as_secs_f32() / info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
- transform.bypass_change_detection().translation = info.last_transform.translation + progress * (info.latest_transform.translation - info.last_transform.translation);
+ for (entity, mut transform, pos_info, rot_info) in &mut interpolation_query {
+ let dt = bevy::platform::time::Instant::now() - pos_info.this_tick_start;
+ let progress = dt.as_secs_f32() / pos_info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
+ transform.translation = (pos_info.last_position + progress * (pos_info.latest_position - pos_info.last_position)).extend(0.0);
+
+ let dt = bevy::platform::time::Instant::now() - rot_info.this_tick_start;
+ let progress = dt.as_secs_f32() / rot_info.last_dt.as_secs_f32(); // should be between 0.0 and 1.0
+ transform.rotation = Quat::from_rotation_z(rot_info.last_rotation + progress * (rot_info.latest_rotation - rot_info.last_rotation));
}
}=
\ No newline at end of file
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +1 -1
@@ 59,7 59,7 @@ pub mod starguide;
pub mod crafting;
pub mod components;
pub mod plugins;
-mod interpolation;
+pub mod interpolation;
pub struct ClientPlugin {
pub server: Option<String>
M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +11 -4
@@ 11,7 11,7 @@ use crate::client::components::Me;
use crate::client::ship::attachment::AttachmentDebugRes;
use crate::prelude::*;
use bevy_replicon::client::confirm_history::ConfirmHistory;
-use crate::client::interpolation::TransformInterpolationInfo;
+use crate::client::interpolation::{RotationInterpolationInfo, TranslationInterpolationInfo};
pub fn parts_plugin(app: &mut App) {
app.insert_resource(DragResource { dragged: None });
@@ 60,11 60,18 @@ fn handle_incoming_parts(
.insert(MAIN_LAYER)
.insert(sprite)
.insert(Pickable::default())
- .insert(TransformInterpolationInfo {
+ .insert(TranslationInterpolationInfo {
last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
this_tick_start: bevy::platform::time::Instant::now(),
- latest_transform: transform.clone(),
- last_transform: transform.clone(),
+ latest_position: transform.translation.truncate(),
+ last_position: transform.translation.truncate(),
+ })
+ .insert(RotationInterpolationInfo {
+ last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
+ this_tick_start: bevy::platform::time::Instant::now(),
+ last_angular_velocity: 0.0,
+ latest_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
+ last_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
})
.observe(on_part_click)
.observe(open_crafting_ui);
M crates/unified/src/client/planet/incoming_planets.rs => crates/unified/src/client/planet/incoming_planets.rs +16 -1
@@ 1,7 1,9 @@
+use std::time::Duration;
use crate::shared::config::planet::{Planet, SpecialSpriteProperties};
use crate::prelude::*;
use crate::shared::ecs::{MAIN_STAR_LAYERS};
use bevy_replicon::client::confirm_history::ConfirmHistory;
+use crate::client::interpolation::{RotationInterpolationInfo, TranslationInterpolationInfo};
pub fn incoming_planets_plugin(app: &mut App) {
app.add_systems(Update, (handle_incoming_planets, handle_updated_planets));
@@ 24,7 26,20 @@ fn handle_incoming_planets(
for (new_entity, new_planet, transform, confirm_history) in new_planets.iter() {
commands.entity(new_entity)
.insert(MAIN_STAR_LAYERS.clone())
- .insert(build_planet_sprite(new_planet, &asset_server));
+ .insert(build_planet_sprite(new_planet, &asset_server))
+ .insert(TranslationInterpolationInfo {
+ last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
+ this_tick_start: bevy::platform::time::Instant::now(),
+ latest_position: transform.translation.truncate(),
+ last_position: transform.translation.truncate(),
+ })
+ .insert(RotationInterpolationInfo {
+ last_dt: Duration::from_millis(50), // assume it was 20tps because we don't know
+ this_tick_start: bevy::platform::time::Instant::now(),
+ last_angular_velocity: 0.0,
+ latest_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
+ last_rotation: transform.rotation.to_euler(EulerRot::XYZ).2,
+ });
trace!(?new_planet, "prepared new planet");
}
}
M crates/unified/src/shared/net.rs => crates/unified/src/shared/net.rs +2 -2
@@ 17,8 17,8 @@ pub fn register_replication(app: &mut App) {
app
.add_mapped_server_message::<Hi>(Channel::Ordered)
- .replicate::<Transform>()
- .replicate::<GlobalTransform>()
+ .replicate_once::<Transform>()
+ .replicate_once::<GlobalTransform>()
.replicate::<Position>()
.replicate::<Rotation>()