move clientside gated parts of control to the client (#70)
This commit is contained in:
@@ -1,21 +1,15 @@
|
|||||||
use super::{ControlState, Controls};
|
use super::Controls;
|
||||||
#[cfg(feature = "client")]
|
|
||||||
use crate::control::ControllerSet;
|
|
||||||
use crate::{GameState, control::CharacterInputEnabled};
|
use crate::{GameState, control::CharacterInputEnabled};
|
||||||
#[cfg(feature = "client")]
|
|
||||||
use bevy::input::{
|
use bevy::input::{
|
||||||
ButtonState,
|
ButtonState,
|
||||||
gamepad::{GamepadConnection, GamepadEvent},
|
gamepad::{GamepadConnection, GamepadEvent},
|
||||||
mouse::{MouseButtonInput, MouseMotion},
|
mouse::{MouseButtonInput, MouseMotion},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
#[cfg(feature = "client")]
|
|
||||||
use lightyear::input::client::InputSet;
|
use lightyear::input::client::InputSet;
|
||||||
#[cfg(feature = "client")]
|
|
||||||
use lightyear::prelude::input::native::{ActionState, InputMarker};
|
use lightyear::prelude::input::native::{ActionState, InputMarker};
|
||||||
use serde::{Deserialize, Serialize};
|
use shared::control::{ControlState, ControllerSet};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "client")]
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
@@ -24,37 +18,32 @@ pub fn plugin(app: &mut App) {
|
|||||||
|
|
||||||
app.register_required_components::<Gamepad, InputStateCache<GamepadButton>>();
|
app.register_required_components::<Gamepad, InputStateCache<GamepadButton>>();
|
||||||
|
|
||||||
app.register_type::<ControllerSettings>();
|
|
||||||
|
|
||||||
app.add_systems(PreUpdate, (cache_keyboard_state, cache_gamepad_state));
|
app.add_systems(PreUpdate, (cache_keyboard_state, cache_gamepad_state));
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
app.add_systems(
|
||||||
{
|
FixedPreUpdate,
|
||||||
app.add_systems(
|
(
|
||||||
FixedPreUpdate,
|
gamepad_controls,
|
||||||
(
|
keyboard_controls,
|
||||||
gamepad_controls,
|
mouse_rotate,
|
||||||
keyboard_controls,
|
mouse_click,
|
||||||
mouse_rotate,
|
gamepad_connections.run_if(on_event::<GamepadEvent>),
|
||||||
mouse_click,
|
combine_controls,
|
||||||
gamepad_connections.run_if(on_event::<GamepadEvent>),
|
clear_keyboard_state,
|
||||||
combine_controls,
|
clear_gamepad_state,
|
||||||
clear_keyboard_state,
|
|
||||||
clear_gamepad_state,
|
|
||||||
)
|
|
||||||
.chain()
|
|
||||||
.in_set(ControllerSet::CollectInputs)
|
|
||||||
.before(InputSet::WriteClientInputs)
|
|
||||||
.run_if(
|
|
||||||
in_state(GameState::Playing)
|
|
||||||
.and(resource_exists_and_equals(CharacterInputEnabled::On)),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.add_systems(
|
.chain()
|
||||||
FixedPreUpdate,
|
.in_set(ControllerSet::CollectInputs)
|
||||||
buffer_inputs.in_set(InputSet::WriteClientInputs),
|
.before(InputSet::WriteClientInputs)
|
||||||
);
|
.run_if(
|
||||||
}
|
in_state(GameState::Playing)
|
||||||
|
.and(resource_exists_and_equals(CharacterInputEnabled::On)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
FixedPreUpdate,
|
||||||
|
buffer_inputs.in_set(InputSet::WriteClientInputs),
|
||||||
|
);
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
@@ -62,14 +51,6 @@ pub fn plugin(app: &mut App) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, PartialEq, Reflect, Serialize, Deserialize)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct ControllerSettings {
|
|
||||||
pub deceleration_factor: f32,
|
|
||||||
pub jump_force: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Write inputs from combined keyboard/gamepad state into the networked input buffer
|
/// Write inputs from combined keyboard/gamepad state into the networked input buffer
|
||||||
/// for the local player.
|
/// for the local player.
|
||||||
fn buffer_inputs(
|
fn buffer_inputs(
|
||||||
@@ -104,7 +85,6 @@ impl<Button> Default for InputStateCache<Button> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
impl<Button: Hash + Eq> InputStateCache<Button> {
|
impl<Button: Hash + Eq> InputStateCache<Button> {
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
for state in self.map.values_mut() {
|
for state in self.map.values_mut() {
|
||||||
@@ -151,7 +131,6 @@ fn cache_keyboard_state(
|
|||||||
cache_key(KeyCode::KeyE);
|
cache_key(KeyCode::KeyE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
fn clear_keyboard_state(mut cache: ResMut<InputStateCache<KeyCode>>) {
|
fn clear_keyboard_state(mut cache: ResMut<InputStateCache<KeyCode>>) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
@@ -182,14 +161,12 @@ fn cache_gamepad_state(mut gamepads: Query<(&Gamepad, &mut InputStateCache<Gamep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
fn clear_gamepad_state(mut caches: Query<&mut InputStateCache<GamepadButton>>) {
|
fn clear_gamepad_state(mut caches: Query<&mut InputStateCache<GamepadButton>>) {
|
||||||
for mut cache in caches.iter_mut() {
|
for mut cache in caches.iter_mut() {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Take keyboard and gamepad state and combine them into unified input state
|
/// Take keyboard and gamepad state and combine them into unified input state
|
||||||
fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<ControlState>) {
|
fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<ControlState>) {
|
||||||
let keyboard = controls.keyboard_state;
|
let keyboard = controls.keyboard_state;
|
||||||
@@ -210,7 +187,6 @@ fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<Contr
|
|||||||
combined_controls.cash_heal = gamepad.cash_heal | keyboard.cash_heal;
|
combined_controls.cash_heal = gamepad.cash_heal | keyboard.cash_heal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Applies a square deadzone to a Vec2
|
/// Applies a square deadzone to a Vec2
|
||||||
fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
|
fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
|
||||||
Vec2::new(
|
Vec2::new(
|
||||||
@@ -219,7 +195,6 @@ fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Collect gamepad inputs
|
/// Collect gamepad inputs
|
||||||
fn gamepad_controls(
|
fn gamepad_controls(
|
||||||
gamepads: Query<(Entity, &Gamepad, &InputStateCache<GamepadButton>)>,
|
gamepads: Query<(Entity, &Gamepad, &InputStateCache<GamepadButton>)>,
|
||||||
@@ -282,7 +257,6 @@ fn gamepad_controls(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Collect mouse movement input
|
/// Collect mouse movement input
|
||||||
fn mouse_rotate(mut mouse: EventReader<MouseMotion>, mut controls: ResMut<Controls>) {
|
fn mouse_rotate(mut mouse: EventReader<MouseMotion>, mut controls: ResMut<Controls>) {
|
||||||
controls.keyboard_state.look_dir = Vec2::ZERO;
|
controls.keyboard_state.look_dir = Vec2::ZERO;
|
||||||
@@ -292,7 +266,6 @@ fn mouse_rotate(mut mouse: EventReader<MouseMotion>, mut controls: ResMut<Contro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Collect keyboard input
|
/// Collect keyboard input
|
||||||
fn keyboard_controls(
|
fn keyboard_controls(
|
||||||
keyboard: Res<ButtonInput<KeyCode>>,
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
@@ -325,7 +298,6 @@ fn keyboard_controls(
|
|||||||
controls.keyboard_state.cash_heal = cache.just_pressed(KeyCode::Enter);
|
controls.keyboard_state.cash_heal = cache.just_pressed(KeyCode::Enter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Collect mouse button input when pressed
|
/// Collect mouse button input when pressed
|
||||||
fn mouse_click(mut events: EventReader<MouseButtonInput>, mut controls: ResMut<Controls>) {
|
fn mouse_click(mut events: EventReader<MouseButtonInput>, mut controls: ResMut<Controls>) {
|
||||||
controls.keyboard_state.just_triggered = false;
|
controls.keyboard_state.just_triggered = false;
|
||||||
@@ -352,7 +324,6 @@ fn mouse_click(mut events: EventReader<MouseButtonInput>, mut controls: ResMut<C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
/// Receive gamepad connections and disconnections
|
/// Receive gamepad connections and disconnections
|
||||||
fn gamepad_connections(mut evr_gamepad: EventReader<GamepadEvent>) {
|
fn gamepad_connections(mut evr_gamepad: EventReader<GamepadEvent>) {
|
||||||
for ev in evr_gamepad.read() {
|
for ev in evr_gamepad.read() {
|
||||||
28
crates/client/src/control/mod.rs
Normal file
28
crates/client/src/control/mod.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use crate::GameState;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use shared::control::{ControlState, ControllerSet};
|
||||||
|
|
||||||
|
pub mod controls;
|
||||||
|
|
||||||
|
#[derive(Resource, Debug, Default)]
|
||||||
|
struct Controls {
|
||||||
|
keyboard_state: ControlState,
|
||||||
|
gamepad_state: Option<ControlState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Debug, PartialEq, Eq)]
|
||||||
|
pub enum CharacterInputEnabled {
|
||||||
|
On,
|
||||||
|
Off,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.insert_resource(CharacterInputEnabled::On);
|
||||||
|
|
||||||
|
app.add_plugins(controls::plugin);
|
||||||
|
|
||||||
|
app.configure_sets(
|
||||||
|
FixedPreUpdate,
|
||||||
|
ControllerSet::CollectInputs.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod backpack;
|
mod backpack;
|
||||||
mod client;
|
mod client;
|
||||||
|
mod control;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod enemy;
|
mod enemy;
|
||||||
mod heal_effect;
|
mod heal_effect;
|
||||||
@@ -124,6 +125,7 @@ fn main() {
|
|||||||
|
|
||||||
app.add_plugins(backpack::plugin);
|
app.add_plugins(backpack::plugin);
|
||||||
app.add_plugins(client::plugin);
|
app.add_plugins(client::plugin);
|
||||||
|
app.add_plugins(control::plugin);
|
||||||
app.add_plugins(debug::plugin);
|
app.add_plugins(debug::plugin);
|
||||||
app.add_plugins(enemy::plugin);
|
app.add_plugins(enemy::plugin);
|
||||||
app.add_plugins(heal_effect::plugin);
|
app.add_plugins(heal_effect::plugin);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::GameState;
|
use crate::{GameState, control::CharacterInputEnabled};
|
||||||
use bevy::{color::palettes::css::BLACK, prelude::*};
|
use bevy::{color::palettes::css::BLACK, prelude::*};
|
||||||
use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets};
|
use shared::{HEDZ_GREEN, HEDZ_PURPLE, loading_assets::UIAssets};
|
||||||
|
|
||||||
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
|
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
|
||||||
#[states(scoped_entities)]
|
#[states(scoped_entities)]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
GameState,
|
GameState,
|
||||||
abilities::TriggerStateRes,
|
abilities::TriggerStateRes,
|
||||||
animation::AnimationFlags,
|
animation::AnimationFlags,
|
||||||
control::{ControlState, SelectedController, controls::ControllerSettings},
|
control::{ControlState, ControllerSettings, SelectedController},
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
heads_database::{HeadControls, HeadsDatabase},
|
heads_database::{HeadControls, HeadsDatabase},
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::{ControlState, ControllerSet};
|
|||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
animation::AnimationFlags,
|
animation::AnimationFlags,
|
||||||
control::{controller_common::MovementSpeedFactor, controls::ControllerSettings},
|
control::{ControllerSettings, controller_common::MovementSpeedFactor},
|
||||||
player::PlayerBodyMesh,
|
player::PlayerBodyMesh,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|||||||
@@ -1,25 +1,60 @@
|
|||||||
|
use bevy::{ecs::entity::MapEntities, prelude::*};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState,
|
GameState,
|
||||||
head::ActiveHead,
|
head::ActiveHead,
|
||||||
heads_database::{HeadControls, HeadsDatabase},
|
heads_database::{HeadControls, HeadsDatabase},
|
||||||
player::Player,
|
player::Player,
|
||||||
};
|
};
|
||||||
use bevy::{ecs::entity::MapEntities, prelude::*};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub mod controller_common;
|
pub mod controller_common;
|
||||||
pub mod controller_flying;
|
pub mod controller_flying;
|
||||||
pub mod controller_running;
|
pub mod controller_running;
|
||||||
pub mod controls;
|
|
||||||
|
|
||||||
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Default)]
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Default)]
|
||||||
enum ControllerSet {
|
pub enum ControllerSet {
|
||||||
CollectInputs,
|
CollectInputs,
|
||||||
ApplyControlsFly,
|
ApplyControlsFly,
|
||||||
#[default]
|
#[default]
|
||||||
ApplyControlsRun,
|
ApplyControlsRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct ControllerSwitchEvent;
|
||||||
|
|
||||||
|
#[derive(Resource, Debug, Default, PartialEq)]
|
||||||
|
pub struct SelectedController(pub ControllerSet);
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.register_type::<ControllerSettings>();
|
||||||
|
|
||||||
|
app.init_resource::<SelectedController>();
|
||||||
|
app.init_resource::<ControlState>();
|
||||||
|
|
||||||
|
app.add_event::<ControllerSwitchEvent>();
|
||||||
|
|
||||||
|
app.add_plugins(controller_common::plugin);
|
||||||
|
app.add_plugins(controller_flying::CharacterControllerPlugin);
|
||||||
|
app.add_plugins(controller_running::CharacterControllerPlugin);
|
||||||
|
|
||||||
|
app.configure_sets(
|
||||||
|
FixedUpdate,
|
||||||
|
(
|
||||||
|
ControllerSet::ApplyControlsFly.run_if(resource_equals(SelectedController(
|
||||||
|
ControllerSet::ApplyControlsFly,
|
||||||
|
))),
|
||||||
|
ControllerSet::ApplyControlsRun.run_if(resource_equals(SelectedController(
|
||||||
|
ControllerSet::ApplyControlsRun,
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
.chain()
|
||||||
|
.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource, Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, Reflect)]
|
#[derive(Resource, Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, Reflect)]
|
||||||
pub struct ControlState {
|
pub struct ControlState {
|
||||||
/// Movement direction with a maximum length of 1.0
|
/// Movement direction with a maximum length of 1.0
|
||||||
@@ -43,57 +78,11 @@ impl MapEntities for ControlState {
|
|||||||
fn map_entities<E: EntityMapper>(&mut self, _entity_mapper: &mut E) {}
|
fn map_entities<E: EntityMapper>(&mut self, _entity_mapper: &mut E) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Debug, Default)]
|
#[derive(Component, Clone, PartialEq, Reflect, Serialize, Deserialize)]
|
||||||
struct Controls {
|
#[reflect(Component)]
|
||||||
#[cfg(feature = "client")]
|
pub struct ControllerSettings {
|
||||||
keyboard_state: ControlState,
|
pub deceleration_factor: f32,
|
||||||
#[cfg(feature = "client")]
|
pub jump_force: f32,
|
||||||
gamepad_state: Option<ControlState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Debug, PartialEq, Eq)]
|
|
||||||
pub enum CharacterInputEnabled {
|
|
||||||
On,
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Event)]
|
|
||||||
pub struct ControllerSwitchEvent;
|
|
||||||
|
|
||||||
#[derive(Resource, Debug, Default, PartialEq)]
|
|
||||||
pub struct SelectedController(ControllerSet);
|
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
|
||||||
app.init_resource::<SelectedController>();
|
|
||||||
app.init_resource::<ControlState>();
|
|
||||||
app.insert_resource(CharacterInputEnabled::On);
|
|
||||||
|
|
||||||
app.add_plugins(controls::plugin);
|
|
||||||
app.add_plugins(controller_common::plugin);
|
|
||||||
app.add_plugins(controller_flying::CharacterControllerPlugin);
|
|
||||||
app.add_plugins(controller_running::CharacterControllerPlugin);
|
|
||||||
|
|
||||||
app.add_event::<ControllerSwitchEvent>();
|
|
||||||
|
|
||||||
app.configure_sets(
|
|
||||||
FixedPreUpdate,
|
|
||||||
ControllerSet::CollectInputs.run_if(in_state(GameState::Playing)),
|
|
||||||
)
|
|
||||||
.configure_sets(
|
|
||||||
FixedUpdate,
|
|
||||||
(
|
|
||||||
ControllerSet::ApplyControlsFly.run_if(resource_equals(SelectedController(
|
|
||||||
ControllerSet::ApplyControlsFly,
|
|
||||||
))),
|
|
||||||
ControllerSet::ApplyControlsRun.run_if(resource_equals(SelectedController(
|
|
||||||
ControllerSet::ApplyControlsRun,
|
|
||||||
))),
|
|
||||||
)
|
|
||||||
.chain()
|
|
||||||
.run_if(in_state(GameState::Playing)),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_change(
|
fn head_change(
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ use crate::{
|
|||||||
cash::CashResource,
|
cash::CashResource,
|
||||||
character::{self, AnimatedCharacter},
|
character::{self, AnimatedCharacter},
|
||||||
control::{
|
control::{
|
||||||
ControlState,
|
ControlState, ControllerSettings,
|
||||||
controller_common::{MovementSpeedFactor, PlayerCharacterController},
|
controller_common::{MovementSpeedFactor, PlayerCharacterController},
|
||||||
controls::ControllerSettings,
|
|
||||||
},
|
},
|
||||||
cutscene::StartCutscene,
|
cutscene::StartCutscene,
|
||||||
global_observer,
|
global_observer,
|
||||||
|
|||||||
Reference in New Issue
Block a user