Predict player entity, unify movement system

main
copygirl 1 month ago
parent 4c9cee7363
commit d2e6bfac2b
  1. 6
      client/src/main.rs
  2. 17
      common/src/network/protocol.rs
  3. 10
      common/src/network/server.rs
  4. 44
      common/src/player.rs

@ -90,7 +90,7 @@ fn main() -> Result {
); );
app.add_observer(spawn_initial_blocks); app.add_observer(spawn_initial_blocks);
app.add_observer(setup_player); app.add_observer(handle_predicted_spawn);
app.run(); app.run();
Ok(()) Ok(())
@ -135,8 +135,8 @@ fn spawn_initial_blocks(_event: On<Add, Started>, mut blocks: Blocks) {
} }
} }
/// When the player we control is being spawned, insert necessary components. /// When the `Predicted` player we control is being spawned, insert necessary components.
fn setup_player(event: On<Add, Controlled>, mut commands: Commands) { fn handle_predicted_spawn(event: On<Add, Predicted>, mut commands: Commands) {
let player = event.entity; let player = event.entity;
commands commands
.entity(player) .entity(player)

@ -25,8 +25,23 @@ impl Plugin for ProtocolPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(InputPlugin::<Inputs>::default()); app.add_plugins(InputPlugin::<Inputs>::default());
app.register_component::<Transform>(); // marker components
app.register_component::<Player>(); app.register_component::<Player>();
app.register_component::<Block>(); app.register_component::<Block>();
app.register_component::<Transform>()
.add_prediction()
.add_interpolation_with(interpolate_transform);
// unified update systems
app.add_systems(FixedUpdate, crate::player::movement);
}
}
fn interpolate_transform(start: Transform, other: Transform, t: f32) -> Transform {
Transform {
translation: start.translation.lerp(other.translation, t),
rotation: start.rotation.slerp(other.rotation, t),
scale: start.scale.lerp(other.scale, t),
} }
} }

@ -26,8 +26,6 @@ impl Plugin for ServerPlugin {
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
app.add_observer(super::server_webtransport::print_certificate_digest); app.add_observer(super::server_webtransport::print_certificate_digest);
app.add_systems(FixedUpdate, crate::player::server_movement);
} }
} }
@ -57,11 +55,11 @@ fn on_server_stopped(event: On<Add, Stopped>) {
fn handle_client_connected( fn handle_client_connected(
event: On<Add, Connected>, event: On<Add, Connected>,
clients: Query<&LinkOf>, clients: Query<(&LinkOf, &RemoteId)>,
mut commands: Commands, mut commands: Commands,
) { ) {
let client = event.entity; let client = event.entity;
let Ok(LinkOf { server }) = clients.get(client) else { let Ok((LinkOf { server }, RemoteId(peer_id))) = clients.get(client) else {
return; // Not a client of the server. (client-side?) return; // Not a client of the server. (client-side?)
}; };
info!("Client '{client}' connected to server '{server}'"); info!("Client '{client}' connected to server '{server}'");
@ -74,11 +72,13 @@ fn handle_client_connected(
commands.spawn(( commands.spawn((
Player, Player,
Name::from("Player"), Name::from("Player"),
Replicate::to_clients(NetworkTarget::All),
ControlledBy { ControlledBy {
owner: client, owner: client,
lifetime: Lifetime::SessionBased, lifetime: Lifetime::SessionBased,
}, },
Replicate::to_clients(NetworkTarget::All),
PredictionTarget::to_clients(NetworkTarget::Single(*peer_id)),
InterpolationTarget::to_clients(NetworkTarget::AllExceptSingle(*peer_id)),
)); ));
} }

@ -1,4 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use lightyear::prelude::*;
use lightyear::prelude::input::native::ActionState; use lightyear::prelude::input::native::ActionState;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -11,28 +12,29 @@ const MOVEMENT_SPEED: f32 = 5.0;
#[require(Transform)] #[require(Transform)]
pub struct Player; pub struct Player;
pub fn server_movement( pub fn movement(
time: Res<Time<Fixed>>, time: Res<Time<Fixed>>,
mut players: Query<(&mut Transform, &ActionState<Inputs>)>, mut players: Query<
(&mut Transform, &ActionState<Inputs>),
// Must be a `Player` which is either be `ControlledBy` a remote
// client (server-side) or its movement `Predicted` on the client.
(With<Player>, Or<(With<ControlledBy>, With<Predicted>)>),
>,
) { ) {
for (mut transform, inputs) in players.iter_mut() { for (mut transform, input) in players.iter_mut() {
shared_movement(&mut transform, *time, inputs); let dt = time.delta_secs();
let mut translation = Vec3::ZERO;
if input.movement != Vec2::ZERO {
let movement = input.movement.clamp_length_max(1.0);
translation.x += movement.x;
translation.z += movement.y;
}
if input.up {
translation.y += 1.0;
}
if input.down {
translation.y -= 1.0;
}
transform.translation += translation * MOVEMENT_SPEED * dt;
} }
} }
pub fn shared_movement(transform: &mut Transform, time: Time<Fixed>, input: &Inputs) {
let dt = time.delta_secs();
let mut translation = Vec3::ZERO;
if input.movement != Vec2::ZERO {
let movement = input.movement.clamp_length_max(1.0);
translation.x += movement.x;
translation.z += movement.y;
}
if input.up {
translation.y += 1.0;
}
if input.down {
translation.y -= 1.0;
}
transform.translation += translation * MOVEMENT_SPEED * dt;
}

Loading…
Cancel
Save