refactor: more coments
This commit is contained in:
@@ -1,173 +0,0 @@
|
|||||||
use bevy_app::{App, Plugin, PreUpdate};
|
|
||||||
use bevy_derive::{Deref, DerefMut};
|
|
||||||
use bevy_ecs::system::{In, IntoSystem};
|
|
||||||
use bevy_ecs::{
|
|
||||||
event::EventReader,
|
|
||||||
system::{Res, ResMut, Resource},
|
|
||||||
};
|
|
||||||
use bevy_input::{
|
|
||||||
keyboard::KeyCode,
|
|
||||||
mouse::{MouseButton, MouseMotion, MouseScrollUnit, MouseWheel},
|
|
||||||
ButtonInput,
|
|
||||||
};
|
|
||||||
use bevy_utils::HashMap;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
contract::{Action, GameState, InputsContainer},
|
|
||||||
resource::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ControlsPlugin<A: Action, Ic: InputsContainer<A>, Gs: GameState> {
|
|
||||||
_action: std::marker::PhantomData<A>,
|
|
||||||
_inputs_container: std::marker::PhantomData<Ic>,
|
|
||||||
_game_state: std::marker::PhantomData<Gs>,
|
|
||||||
controls: Controls<A, Gs>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Default for ControlsPlugin<A, Ic, Gs> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
_action: std::marker::PhantomData,
|
|
||||||
_inputs_container: std::marker::PhantomData,
|
|
||||||
_game_state: std::marker::PhantomData,
|
|
||||||
controls: Controls::<A, Gs>::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
|
||||||
pub fn new(controls: Controls<A, Gs>) -> Self {
|
|
||||||
Self {
|
|
||||||
_action: std::marker::PhantomData,
|
|
||||||
_inputs_container: std::marker::PhantomData,
|
|
||||||
_game_state: std::marker::PhantomData,
|
|
||||||
controls,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Default, Debug, Deref, DerefMut)]
|
|
||||||
struct TrigeredInputs(HashMap<InputType, InputValue>);
|
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin<A, Ic, Gs> {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app
|
|
||||||
.insert_resource(self.controls.clone())
|
|
||||||
.init_resource::<Ic>()
|
|
||||||
.init_resource::<TrigeredInputs>()
|
|
||||||
.insert_state::<Gs>(Gs::default())
|
|
||||||
.add_systems(
|
|
||||||
PreUpdate,
|
|
||||||
Self::collect_inputs.pipe(Self::fill_players_inputs),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(feature = "reflect")]
|
|
||||||
app.register_type::<Controls<A, Gs>>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
|
||||||
// Separate collecting and filling is necessary becouse order of complicate imputs in future
|
|
||||||
fn collect_inputs(
|
|
||||||
keyboard_button: Res<ButtonInput<KeyCode>>,
|
|
||||||
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
|
||||||
mut scroll_evr: EventReader<MouseWheel>,
|
|
||||||
mut motion_evr: EventReader<MouseMotion>,
|
|
||||||
// mut collected_inputs: ResMut<TrigeredInputs>,
|
|
||||||
) -> TrigeredInputs {
|
|
||||||
let mut collected_inputs = TrigeredInputs(HashMap::new());
|
|
||||||
for key in keyboard_button.get_pressed() {
|
|
||||||
collected_inputs.insert(InputType::Keyboard(*key), InputValue::Boolean(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// buttons.get_just_released()
|
|
||||||
|
|
||||||
for button in mouse_buttons.get_pressed() {
|
|
||||||
collected_inputs.insert(
|
|
||||||
InputType::Mouse(MouseInput::Button(*button)),
|
|
||||||
InputValue::Boolean(true),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ev in motion_evr.read() {
|
|
||||||
collected_inputs.insert(
|
|
||||||
InputType::Mouse(MouseInput::Axis(AxisName::Horizontal)),
|
|
||||||
InputValue::Float(ev.delta.x),
|
|
||||||
);
|
|
||||||
collected_inputs.insert(
|
|
||||||
InputType::Mouse(MouseInput::Axis(AxisName::Vertical)),
|
|
||||||
InputValue::Float(ev.delta.y),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ev in scroll_evr.read() {
|
|
||||||
match ev.unit {
|
|
||||||
MouseScrollUnit::Line => {
|
|
||||||
collected_inputs.insert(
|
|
||||||
InputType::Mouse(MouseInput::Wheel(AxisName::Horizontal)),
|
|
||||||
InputValue::Float(ev.x),
|
|
||||||
);
|
|
||||||
collected_inputs.insert(
|
|
||||||
InputType::Mouse(MouseInput::Wheel(AxisName::Vertical)),
|
|
||||||
InputValue::Float(ev.y),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
MouseScrollUnit::Pixel => {
|
|
||||||
panic!("I'm do not accept you to use touch pad in my game");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collected_inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: ! Zero out values that did not arrive
|
|
||||||
fn fill_players_inputs(
|
|
||||||
In(collected_inputs): In<TrigeredInputs>,
|
|
||||||
mut inputs_container: ResMut<Ic>,
|
|
||||||
controls: Res<Controls<A, Gs>>,
|
|
||||||
game_state: Res<State<Gs>>,
|
|
||||||
) {
|
|
||||||
if let Some(inputs) = inputs_container.me_mut() {
|
|
||||||
// TODO: only on controls change
|
|
||||||
let mut action_binding_pairs: Vec<(&A, &BindingConfig<Gs>, &Binding<Gs>)> = Vec::new();
|
|
||||||
for (action, config) in controls.iter() {
|
|
||||||
for binding in config.bindings.iter() {
|
|
||||||
action_binding_pairs.push((action, config, binding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
action_binding_pairs.sort_by(|a, b| b.2.input.len().cmp(&a.2.input.len()));
|
|
||||||
|
|
||||||
'bindings_loop: for (action, _config, binding) in action_binding_pairs.iter() {
|
|
||||||
// check binding conditions
|
|
||||||
for condition in &binding.conditions {
|
|
||||||
match condition {
|
|
||||||
BindingCondition::InGameState(state) => {
|
|
||||||
if *state != *game_state.get() {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//log::info!("{:#?}", collected_inputs);
|
|
||||||
//log::info!("{:#?}", action_binding_pairs[0]);
|
|
||||||
|
|
||||||
match &binding.input {
|
|
||||||
ButtonCombination::Single(input_type) => {
|
|
||||||
// TODO: exclude if used
|
|
||||||
if let Some(value) = collected_inputs.get(&*input_type) {
|
|
||||||
inputs.forced_set(**action, *value);
|
|
||||||
} else {
|
|
||||||
inputs.forced_set(**action, InputValue::Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: print onece
|
|
||||||
log::warn!("cannot find me in inputs container");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -69,7 +69,13 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
||||||
// Separate collecting and filling is necessary becouse order of complicate imputs in future
|
/// Collects input events from various input devices
|
||||||
|
/// (keyboard, mouse buttons, mouse motion, and scroll)
|
||||||
|
/// and stores them in a `TrigeredInputs` structure.
|
||||||
|
///
|
||||||
|
/// This function is separated from filling inputs to allow for more complex input handling
|
||||||
|
/// in the future, particularly in scenarios where the order of input events becomes significant.
|
||||||
|
///
|
||||||
fn collect_inputs(
|
fn collect_inputs(
|
||||||
keyboard_button: Res<ButtonInput<KeyCode>>,
|
keyboard_button: Res<ButtonInput<KeyCode>>,
|
||||||
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
mouse_buttons: Res<ButtonInput<MouseButton>>,
|
||||||
@@ -78,12 +84,15 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
// mut collected_inputs: ResMut<TrigeredInputs>,
|
// mut collected_inputs: ResMut<TrigeredInputs>,
|
||||||
) -> TrigeredInputs {
|
) -> TrigeredInputs {
|
||||||
let mut collected_inputs = TrigeredInputs(HashMap::new());
|
let mut collected_inputs = TrigeredInputs(HashMap::new());
|
||||||
|
|
||||||
|
// Collect pressed keyboard buttons
|
||||||
for key in keyboard_button.get_pressed() {
|
for key in keyboard_button.get_pressed() {
|
||||||
collected_inputs.insert(InputType::Keyboard(*key), InputValue::Boolean(true));
|
collected_inputs.insert(InputType::Keyboard(*key), InputValue::Boolean(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// buttons.get_just_released()
|
// buttons.get_just_released()
|
||||||
|
|
||||||
|
// Collect pressed mouse buttons
|
||||||
for button in mouse_buttons.get_pressed() {
|
for button in mouse_buttons.get_pressed() {
|
||||||
collected_inputs.insert(
|
collected_inputs.insert(
|
||||||
InputType::Mouse(MouseInput::Button(*button)),
|
InputType::Mouse(MouseInput::Button(*button)),
|
||||||
@@ -91,6 +100,7 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect mouse motion events
|
||||||
for ev in motion_evr.read() {
|
for ev in motion_evr.read() {
|
||||||
collected_inputs.insert(
|
collected_inputs.insert(
|
||||||
InputType::Mouse(MouseInput::Axis(AxisName::Horizontal)),
|
InputType::Mouse(MouseInput::Axis(AxisName::Horizontal)),
|
||||||
@@ -102,8 +112,10 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect mouse scroll events
|
||||||
for ev in scroll_evr.read() {
|
for ev in scroll_evr.read() {
|
||||||
match ev.unit {
|
match ev.unit {
|
||||||
|
// Handle scroll in line units
|
||||||
MouseScrollUnit::Line => {
|
MouseScrollUnit::Line => {
|
||||||
collected_inputs.insert(
|
collected_inputs.insert(
|
||||||
InputType::Mouse(MouseInput::Wheel(AxisName::Horizontal)),
|
InputType::Mouse(MouseInput::Wheel(AxisName::Horizontal)),
|
||||||
@@ -114,6 +126,7 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
InputValue::Float(ev.y),
|
InputValue::Float(ev.y),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// Handle scroll in pixel units (not accepted in this game)
|
||||||
MouseScrollUnit::Pixel => {
|
MouseScrollUnit::Pixel => {
|
||||||
panic!("I'm do not accept you to use touch pad in my game");
|
panic!("I'm do not accept you to use touch pad in my game");
|
||||||
}
|
}
|
||||||
@@ -124,27 +137,40 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ! Zero out values that did not arrive
|
// TODO: ! Zero out values that did not arrive
|
||||||
|
/// Fills the player's inputs based on the collected inputs and the current control bindings.
|
||||||
|
///
|
||||||
|
/// This function takes in the current inputs, updates them according to the specified control
|
||||||
|
/// bindings, and then stores the updated inputs in the input container. It only updates the
|
||||||
|
/// inputs if they satisfy the binding conditions, such as being in a specific game state.
|
||||||
|
///
|
||||||
fn fill_players_inputs(
|
fn fill_players_inputs(
|
||||||
In(collected_inputs): In<TrigeredInputs>,
|
In(collected_inputs): In<TrigeredInputs>,
|
||||||
mut inputs_container: ResMut<Ic>,
|
mut inputs_container: ResMut<Ic>,
|
||||||
controls: Res<Controls<A, Gs>>,
|
controls: Res<Controls<A, Gs>>,
|
||||||
game_state: Res<State<Gs>>,
|
game_state: Res<State<Gs>>,
|
||||||
) {
|
) {
|
||||||
|
// Check if the input container is accessible
|
||||||
if let Some(inputs) = inputs_container.me_mut() {
|
if let Some(inputs) = inputs_container.me_mut() {
|
||||||
|
// Initialize a vector to hold tuples of actions and their corresponding bindings
|
||||||
// TODO: only on controls change
|
// TODO: only on controls change
|
||||||
let mut action_binding_pairs: Vec<(&A, &BindingConfig<Gs>, &Binding<Gs>)> = Vec::new();
|
let mut action_binding_pairs: Vec<(&A, &BindingConfig<Gs>, &Binding<Gs>)> = Vec::new();
|
||||||
|
|
||||||
|
// Iterate over the control bindings and collect all action-binding pairs
|
||||||
for (action, config) in controls.iter() {
|
for (action, config) in controls.iter() {
|
||||||
for binding in config.bindings.iter() {
|
for binding in config.bindings.iter() {
|
||||||
action_binding_pairs.push((action, config, binding));
|
action_binding_pairs.push((action, config, binding));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the action-binding pairs by the length of the input sequence, in descending order
|
||||||
action_binding_pairs.sort_by(|a, b| b.2.input.len().cmp(&a.2.input.len()));
|
action_binding_pairs.sort_by(|a, b| b.2.input.len().cmp(&a.2.input.len()));
|
||||||
|
|
||||||
|
// Loop over each binding to apply the corresponding input actions
|
||||||
'bindings_loop: for (action, _config, binding) in action_binding_pairs.iter() {
|
'bindings_loop: for (action, _config, binding) in action_binding_pairs.iter() {
|
||||||
// check binding conditions
|
// Check the binding conditions before applying
|
||||||
for condition in &binding.conditions {
|
for condition in &binding.conditions {
|
||||||
match condition {
|
match condition {
|
||||||
|
// Skip this binding if the game state does not match the condition
|
||||||
BindingCondition::InGameState(state) => {
|
BindingCondition::InGameState(state) => {
|
||||||
if *state != *game_state.get() {
|
if *state != *game_state.get() {
|
||||||
continue 'bindings_loop;
|
continue 'bindings_loop;
|
||||||
@@ -153,12 +179,10 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log::info!("{:#?}", collected_inputs);
|
// Match the binding input type to update the player's inputs
|
||||||
//log::info!("{:#?}", action_binding_pairs[0]);
|
|
||||||
|
|
||||||
match &binding.input {
|
match &binding.input {
|
||||||
ButtonCombination::Single(input_type) => {
|
ButtonCombination::Single(input_type) => {
|
||||||
// TODO: exclude if used
|
// TODO: Exclude this input if it has already been used
|
||||||
if let Some(value) = collected_inputs.get(&*input_type) {
|
if let Some(value) = collected_inputs.get(&*input_type) {
|
||||||
inputs.forced_set(**action, *value);
|
inputs.forced_set(**action, *value);
|
||||||
} else {
|
} else {
|
||||||
@@ -168,7 +192,8 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: print onece
|
// Log a warning if the inputs container cannot be accessed
|
||||||
|
// TODO: Ensure this warning is only printed once
|
||||||
log::warn!("cannot find me in inputs container");
|
log::warn!("cannot find me in inputs container");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user