251 lines
9.1 KiB
Rust
251 lines
9.1 KiB
Rust
use bevy_app::{Plugin, App, Update};
|
|
use bevy_ecs::{system::{Res, ResMut}, schedule::State};
|
|
use bevy_input::{Input, keyboard::KeyCode, mouse::MouseButton};
|
|
|
|
use crate::{resource::*, contract::{Action, GameState, InputsContainer}};
|
|
|
|
pub struct ControlsPlugin<A, Ic, Gs> {
|
|
_action: std::marker::PhantomData<A>,
|
|
_inputs_container: std::marker::PhantomData<Ic>,
|
|
_game_state: std::marker::PhantomData<Gs>,
|
|
}
|
|
|
|
impl<A, Ic, Gs> Default for ControlsPlugin<A, Ic, Gs> {
|
|
fn default() -> Self {
|
|
Self {
|
|
_action: std::marker::PhantomData,
|
|
_inputs_container: std::marker::PhantomData,
|
|
_game_state: std::marker::PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
A: Action,
|
|
Ic: InputsContainer<A>,
|
|
Gs: GameState
|
|
> Plugin for ControlsPlugin<A, Ic, Gs> {
|
|
fn build(&self, app: &mut App) {
|
|
app.init_resource::<Controls<A, Gs>>()
|
|
.init_resource::<Ic>()
|
|
.add_state::<Gs>()
|
|
.add_systems(Update, Self::save_input);
|
|
|
|
#[cfg(feature = "reflect")]
|
|
app.register_type::<Controls<A, Gs>>();
|
|
}
|
|
|
|
}
|
|
|
|
impl<
|
|
A: Action,
|
|
Ic: InputsContainer<A>,
|
|
Gs: GameState
|
|
> ControlsPlugin<A, Ic, Gs> {
|
|
/// Process all hard inputs and bindings to update [`PlayerInputs`]
|
|
fn save_input(
|
|
keyboard_input: Res<Input<KeyCode>>,
|
|
mouse_input: Res<Input<MouseButton>>,
|
|
mut lobby: ResMut<Ic>,
|
|
controls: Res<Controls<A, Gs>>,
|
|
game_state: Res<State<Gs>>,
|
|
) {
|
|
if let Some(inputs) = lobby.me_mut() {
|
|
for (action, config) in controls.iter() {
|
|
'bindings_loop: for binding in config.bindings.iter() {
|
|
for condition in &binding.conditions {
|
|
match condition {
|
|
BindingCondition::InGameState(state) => {
|
|
if *state != *game_state.get() {
|
|
continue 'bindings_loop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
match &binding.input {
|
|
ButtonCombination::Single(button) => match button {
|
|
InputType::Keyboard(key) => {
|
|
inputs.forced_set(*action, keyboard_input.pressed(*key));
|
|
}
|
|
InputType::Mouse(input) => {
|
|
match input {
|
|
MouseInput::Axis(_axis) => {
|
|
todo!();
|
|
}
|
|
MouseInput::Button(button) => {
|
|
if !mouse_input
|
|
.get_pressed()
|
|
.any(|b| b == button)
|
|
{
|
|
continue 'bindings_loop;
|
|
}
|
|
}
|
|
MouseInput::Wheel(_axis) => {
|
|
todo!();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
ButtonCombination::Chord(buttons) => {
|
|
// If any button in chord is not pressed we skip this `binding`
|
|
for button in buttons {
|
|
match button {
|
|
InputType::Keyboard(key) => {
|
|
if !keyboard_input.pressed(*key) {
|
|
continue 'bindings_loop;
|
|
}
|
|
}
|
|
InputType::Mouse(input) => {
|
|
match input {
|
|
MouseInput::Axis(_axis) => {
|
|
todo!();
|
|
}
|
|
MouseInput::Button(button) => {
|
|
if !mouse_input
|
|
.get_pressed()
|
|
.any(|b| b == button)
|
|
{
|
|
continue 'bindings_loop;
|
|
}
|
|
}
|
|
MouseInput::Wheel(_axis) => {
|
|
todo!();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: should be [`Chord`](ButtonCombination::Chord) only [`Boolean`](InputValue::Boolean) type
|
|
inputs.forced_set(*action, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
log::error!("cannot find me in inputs container")
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #[cfg(test)]
|
|
// mod performance_test {
|
|
// use crate::{ util::test::{enable_loggings, measure_time, Times}, resource::PlayerInputs};
|
|
|
|
// use super::Controls;
|
|
// use std::time::Duration;
|
|
|
|
// /// Test for execution time for [`Controls`] get
|
|
// ///
|
|
// /// Example:
|
|
// /// ```
|
|
// /// cargo test --package pih-pah-app --lib --features "dev, ui_egui" -- controls::test::controls_get --exact --nocapture
|
|
// /// ```
|
|
// #[test]
|
|
// fn controls_get() {
|
|
// enable_loggings();
|
|
|
|
// let controls = Controls::default();
|
|
|
|
// let duration = measure_time(
|
|
// || {
|
|
// controls.get(Action::LeverEditorForward);
|
|
// controls.get(Action::LevelEditorBackward);
|
|
// controls.get(Action::LevelEditorLeft);
|
|
// controls.get(Action::LevelEditorRight);
|
|
// },
|
|
// Times::default(),
|
|
// );
|
|
|
|
// log::info!("time: {:?}", duration);
|
|
// }
|
|
|
|
// /// Test for execution speed for [`PlayerInputs`] get
|
|
// fn player_inputs_get() -> Duration {
|
|
// enable_loggings();
|
|
|
|
// let inputs = PlayerInputs::default();
|
|
|
|
// let duration = measure_time(
|
|
// || {
|
|
// inputs.get(Action::LeverEditorForward);
|
|
// inputs.get(Action::LevelEditorBackward);
|
|
// inputs.get(Action::LevelEditorLeft);
|
|
// inputs.get(Action::LevelEditorRight);
|
|
// },
|
|
// Times::default(),
|
|
// );
|
|
|
|
// duration
|
|
// }
|
|
|
|
// /// Test for execution speed for [`PlayerInputs`] get_many
|
|
// fn player_inputs_get_many() -> Duration {
|
|
// enable_loggings();
|
|
|
|
// let inputs = PlayerInputs::default();
|
|
|
|
// let duration = measure_time(
|
|
// || {
|
|
// inputs.get_many(vec![
|
|
// Action::LeverEditorForward,
|
|
// Action::LevelEditorBackward,
|
|
// Action::LevelEditorLeft,
|
|
// Action::LevelEditorRight,
|
|
// ]);
|
|
// },
|
|
// Times::default(),
|
|
// );
|
|
|
|
// duration
|
|
// }
|
|
|
|
// /// Test for execution speed for [`PlayerInputs`] get and get_many
|
|
// ///
|
|
// /// Example:
|
|
// /// ```
|
|
// /// cargo test --package pih-pah-app --lib --features "dev, ui_egui" -- controls::test::compare_player_inputs_get_and_get_many --exact --nocapture
|
|
// /// ```
|
|
// #[test]
|
|
// fn compare_player_inputs_get_and_get_many() {
|
|
// let get = player_inputs_get();
|
|
// let get_many = player_inputs_get_many();
|
|
|
|
// log::info!("get: {:?}", get);
|
|
// log::info!("get_many: {:?}", get_many);
|
|
// }
|
|
|
|
// /// Test for execution speed for [`InputValue`] casting
|
|
// #[test]
|
|
// fn action_casting() {
|
|
// enable_loggings();
|
|
|
|
// let inputs = PlayerInputs::default();
|
|
|
|
// let duration = measure_time(
|
|
// || {
|
|
// let _ = (inputs.get(Action::LeverEditorForward).as_boolean() as i8
|
|
// - inputs.get(Action::LevelEditorBackward).as_boolean() as i8)
|
|
// as f32;
|
|
// },
|
|
// 10000000.into(),
|
|
// );
|
|
|
|
// log::info!("with casting: {:?}", duration);
|
|
|
|
// let inputs = PlayerInputs::default();
|
|
|
|
// let duration = measure_time(
|
|
// || {
|
|
// let _ = inputs.get(Action::LeverEditorForward);
|
|
// let _ = inputs.get(Action::LevelEditorBackward);
|
|
// },
|
|
// 10000000.into(),
|
|
// );
|
|
|
|
// log::info!("without casting: {:?}", duration);
|
|
// }
|
|
// }
|