Files
bevy_controls/1
T

174 lines
5.2 KiB
Plaintext

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");
}
}
}