From 668ed934754113b3c84cd284407b1dee8dee0e8b Mon Sep 17 00:00:00 2001 From: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:41:21 -0500 Subject: [PATCH] Simpler + Better Inputs (#82) * switch to events for instantaneous inputs * input simplification + improvements * fix trail crash * fix clippy warnings * qualify `Trail` in fn signature --- crates/client/src/client.rs | 2 +- .../client/src/control/controller_flying.rs | 67 ++-- crates/client/src/control/controls.rs | 351 +++++++----------- crates/client/src/control/mod.rs | 8 +- crates/client/src/player.rs | 54 +-- crates/server/src/backpack/backpack_ui.rs | 72 ++-- crates/server/src/player.rs | 4 +- crates/shared/src/abilities/mod.rs | 28 +- crates/shared/src/camera.rs | 20 +- crates/shared/src/cash.rs | 6 +- crates/shared/src/cash_heal.rs | 50 +-- crates/shared/src/character.rs | 2 +- .../shared/src/control/controller_running.rs | 25 +- crates/shared/src/control/mod.rs | 101 +++-- crates/shared/src/heads/mod.rs | 73 ++-- crates/shared/src/player.rs | 15 +- crates/shared/src/protocol/components.rs | 18 +- crates/shared/src/protocol/mod.rs | 17 +- crates/shared/src/utils/trail.rs | 6 +- 19 files changed, 433 insertions(+), 486 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 4739754..3b0bd6e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -74,7 +74,7 @@ pub fn plugin(app: &mut App) { // fn on_connected_state(mut commands: Commands, mut game_state: ResMut>) { - commands.client_trigger(ClientEnteredPlaying::default()); + commands.client_trigger(ClientEnteredPlaying); game_state.set(GameState::Playing); } diff --git a/crates/client/src/control/controller_flying.rs b/crates/client/src/control/controller_flying.rs index dd91d94..25fc301 100644 --- a/crates/client/src/control/controller_flying.rs +++ b/crates/client/src/control/controller_flying.rs @@ -1,8 +1,8 @@ use bevy::prelude::*; use shared::{ GameState, - control::{ControlState, ControllerSet, LookDirMovement}, - player::PlayerBodyMesh, + control::{ControllerSet, Inputs, LookDirMovement}, + player::{LocalPlayer, PlayerBodyMesh}, }; use std::f32::consts::PI; @@ -17,35 +17,42 @@ pub fn plugin(app: &mut App) { } fn rotate_rig( - controls: Res, + inputs: Single<&Inputs, With>, look_dir: Res, - mut player: Query<&mut Transform, With>, + local_player: Single<&Children, With>, + mut player_mesh: Query<&mut Transform, With>, ) { - for mut rig_transform in player.iter_mut() { - if controls.view_mode { - continue; - } - - let look_dir = look_dir.0; - - // todo: Make consistent with the running controller - let sensitivity = 0.001; - let max_pitch = 35.0 * PI / 180.0; - let min_pitch = -25.0 * PI / 180.0; - - rig_transform.rotate_y(look_dir.x * -sensitivity); - - let euler_rot = rig_transform.rotation.to_euler(EulerRot::YXZ); - let yaw = euler_rot.0; - let pitch = euler_rot.1 + look_dir.y * -sensitivity; - - let pitch_clamped = pitch.clamp(min_pitch, max_pitch); - rig_transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch_clamped, 0.0); - - // The following can be used to limit the amount of rotation per frame - // let target_rotation = rig_transform.rotation - // * Quat::from_rotation_x(controls.keyboard_state.look_dir.y * sensitivity); - // let clamped_rotation = rig_transform.rotation.rotate_towards(target_rotation, 0.01); - // rig_transform.rotation = clamped_rotation; + if inputs.view_mode { + return; } + + local_player.iter().find(|&child| { + if let Ok(mut rig_transform) = player_mesh.get_mut(child) { + let look_dir = look_dir.0; + + // todo: Make consistent with the running controller + let sensitivity = 0.001; + let max_pitch = 35.0 * PI / 180.0; + let min_pitch = -25.0 * PI / 180.0; + + rig_transform.rotate_y(look_dir.x * -sensitivity); + + let euler_rot = rig_transform.rotation.to_euler(EulerRot::YXZ); + let yaw = euler_rot.0; + let pitch = euler_rot.1 + look_dir.y * -sensitivity; + + let pitch_clamped = pitch.clamp(min_pitch, max_pitch); + rig_transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch_clamped, 0.0); + + // The following can be used to limit the amount of rotation per frame + // let target_rotation = rig_transform.rotation + // * Quat::from_rotation_x(controls.keyboard_state.look_dir.y * sensitivity); + // let clamped_rotation = rig_transform.rotation.rotate_towards(target_rotation, 0.01); + // rig_transform.rotation = clamped_rotation; + + true + } else { + false + } + }); } diff --git a/crates/client/src/control/controls.rs b/crates/client/src/control/controls.rs index 54c2ca0..34d1df0 100644 --- a/crates/client/src/control/controls.rs +++ b/crates/client/src/control/controls.rs @@ -1,40 +1,30 @@ -use super::{ControlState, Controls}; use crate::{GameState, control::CharacterInputEnabled}; use bevy::{ input::{ - ButtonState, gamepad::{GamepadConnection, GamepadEvent}, - mouse::{MouseButtonInput, MouseMotion}, + mouse::MouseMotion, }, prelude::*, }; use shared::{ - control::{ControllerSet, LookDirMovement}, - player::PlayerBodyMesh, + control::{ + BackpackLeftPressed, BackpackRightPressed, BackpackSwapPressed, BackpackTogglePressed, + CashHealPressed, ClientInputs, ControllerSet, Inputs, LocalInputs, LookDirMovement, + SelectLeftPressed, SelectRightPressed, + }, + player::{LocalPlayer, PlayerBodyMesh}, }; -use std::{collections::HashMap, hash::Hash}; pub fn plugin(app: &mut App) { - app.init_resource::(); - app.init_resource::>(); - - app.register_required_components::>(); - - app.add_systems(PreUpdate, (cache_keyboard_state, cache_gamepad_state)); - app.add_systems( PreUpdate, ( - reset_lookdir, - gamepad_controls, - keyboard_controls, - mouse_rotate, - mouse_click, gamepad_connections.run_if(on_message::), - combine_controls, + reset_lookdir, + keyboard_controls, + gamepad_controls, + mouse_rotate, get_lookdir, - clear_keyboard_just, - clear_gamepad_just, send_inputs, ) .chain() @@ -43,7 +33,8 @@ pub fn plugin(app: &mut App) { in_state(GameState::Playing) .and(resource_exists_and_equals(CharacterInputEnabled::On)), ), - ); + ) + .add_systems(PreUpdate, overwrite_local_inputs); app.add_systems( Update, @@ -51,10 +42,18 @@ pub fn plugin(app: &mut App) { ); } +/// Overwrite inputs for this client that were replicated from the server with the local inputs +fn overwrite_local_inputs( + mut inputs: Single<&mut Inputs, With>, + local_inputs: Single<&LocalInputs>, +) { + **inputs = local_inputs.0; +} + /// Write inputs from combined keyboard/gamepad state into the networked input buffer /// for the local player. -fn send_inputs(mut writer: MessageWriter, controls: Res) { - writer.write(*controls); +fn send_inputs(mut writer: MessageWriter, local_inputs: Single<&LocalInputs>) { + writer.write(ClientInputs(local_inputs.0)); } fn reset_lookdir(mut look_dir: ResMut) { @@ -64,128 +63,21 @@ fn reset_lookdir(mut look_dir: ResMut) { /// Reset character inputs to default when character input is disabled. fn reset_control_state_on_disable( state: Res, - mut controls: ResMut, - mut control_state: ResMut, + mut inputs: Single<&mut LocalInputs>, ) { if state.is_changed() && matches!(*state, CharacterInputEnabled::Off) { - *controls = Controls::default(); - *control_state = ControlState { - look_dir: control_state.look_dir, + inputs.0 = Inputs { + look_dir: inputs.0.look_dir, ..default() }; } } -/// Caches information that depends on the Update schedule so that it can be read safely from the Fixed schedule -/// without losing/duplicating info -#[derive(Component, Resource)] -struct InputStateCache