feat: controlls builder

This commit is contained in:
2024-03-12 07:56:53 +01:00
parent c651f37759
commit e1e762395f
5 changed files with 157 additions and 97 deletions
+12 -12
View File
@@ -1,15 +1,15 @@
# v0.1.0
-[x] contracts
-[x] loby
-[x] actions
-[x] game state
-[ ] way to define default controlls
-[ ] layers for controlls: if on (shift + space) action enable, ignore on (space) action
-[ ] warnings on actions collision
-[ ] basic example
- [x] contracts
- [x] loby
- [x] actions
- [x] game state
- [x] way to define default controlls
- [ ] layers for controlls: if on (shift + space) action enable, ignore on (space) action
- [ ] warnings on actions collision
- [ ] basic example
# v0.2.0
-[ ] layers for controlls: user posibility give priority to controls
-[ ] controller support
-[ ] touch pad support
-[ ] posibility support more that one game state
- [ ] layers for controlls: user posibility give priority to controls
- [ ] controller support
- [ ] touch pad support
- [ ] posibility support more that one game state
+31 -36
View File
@@ -66,85 +66,80 @@ fn main() {
}),
..default()
}),
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::default(),
))
.insert_resource(ClearColor(Color::NONE))
.init_resource::<JumpsCount>()
.add_systems(Startup, (add_bindings, setup))
.add_systems(Update, text_update_system)
.run();
}
// TODO: default bingings...
fn add_bindings(mut controls: ResMut<Controls<MyAction, MyGameState>>) {
controls.force_push(
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::new(
Controls::<MyAction, MyGameState>::new()
.with(
MyAction::Left,
// this way input will work only on latin layout only on `A`
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::A),
))),
);
controls.force_push(
)
.with(
MyAction::Left,
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or
// `О` for `ru` layout
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(35)),
))),
);
controls.force_push(
)
.with(
MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::S),
))),
);
controls.force_push(
)
.with(
MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(36)),
))),
);
controls.force_push(
)
.with(
MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::W),
))),
);
controls.force_push(
)
.with(
MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(37)),
))),
);
controls.force_push(
)
.with(
MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::D),
))),
);
controls.force_push(
)
.with(
MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(38)),
))),
);
controls.force_push(
)
.with(
MyAction::Up,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::Space),
))),
);
controls.force_push(
)
.with(
MyAction::Down,
Binding::new(ButtonCombination::Chord(vec![
InputType::Keyboard(Keyboard::KeyCode(KeyCode::ShiftLeft)),
InputType::Keyboard(Keyboard::KeyCode(KeyCode::Space)),
])),
);
)
.build(),
),
))
.insert_resource(ClearColor(Color::NONE))
.init_resource::<JumpsCount>()
.add_systems(Startup, setup)
.add_systems(Update, text_update_system)
.run();
}
#[derive(Component)]
@@ -158,7 +153,7 @@ fn setup(mut commands: Commands) {
commands.spawn((
TextBundle::from_sections([
TextSection::new(
"Action: ",
"Info: this example is intended to show basic crate capabilities and limitations\nso WASD work only on US layout, but HJKL may work on any layout\nAction: ",
TextStyle {
font_size: TEXT_SIZE,
..default()
+22 -7
View File
@@ -14,26 +14,40 @@ use crate::{
resource::*,
};
pub struct ControlsPlugin<A, Ic, Gs> {
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, Ic, Gs> Default for ControlsPlugin<A, Ic, 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,
}
}
}
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>>()
.insert_resource(self.controls.clone())
.init_resource::<Ic>()
.add_state::<Gs>()
.add_systems(Update, Self::save_input);
@@ -53,9 +67,11 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
controls: Res<Controls<A, Gs>>,
game_state: Res<State<Gs>>,
) {
// get current client inputs
if let Some(inputs) = inputs_container.me_mut() {
for (action, config) in controls.iter() {
'bindings_loop: for binding in config.bindings.iter() {
// check binding conditions
for condition in &binding.conditions {
match condition {
BindingCondition::InGameState(state) => {
@@ -68,16 +84,15 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
log::trace!("action binding {:?} {:?} in condition", action, binding);
// check inputs & mark actions
match &binding.input {
ButtonCombination::Single(button) => match button {
InputType::Keyboard(Keyboard::KeyCode(key)) => {
log::trace!("presed?: {:?}", keyboard_input_keycode.pressed(*key));
log::trace!("active");
if keyboard_input_keycode.pressed(*key) {
inputs.forced_set(*action, true);
break; // when on binding trigered no sence check another
}
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so too frequently
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
}
InputType::Keyboard(Keyboard::ScanCode(code)) => {
log::trace!("presed?: {:?}", keyboard_input_scancode.pressed(*code));
@@ -86,7 +101,7 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
inputs.forced_set(*action, true);
break; // when on binding trigered no sence check another
}
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so too frequently
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
}
InputType::Mouse(input) => match input {
MouseInput::Axis(_axis) => {
+26 -1
View File
@@ -503,9 +503,29 @@ impl<Gs: GameState> BindingConfig<Gs> {
}
}
#[derive(Deref, DerefMut)]
pub struct ControlsBuilder<A: Action, Gs: GameState>(Controls<A, Gs>);
impl<A: Action, Gs: GameState> ControlsBuilder<A, Gs>{
///
pub fn with(mut self, action: A, binding: Binding<Gs>) -> Self {
self.force_push(action, binding);
self
}
///
pub fn build(mut self) -> Controls<A, Gs> {
// If you see this error, you may add new action in menu_actions
// or make sure that you have only one Menufor<'a> Action<'a> with the same name in the Menufor<'a> Action<'a>s
assert!(validate_hash_map(&**self));
std::mem::take(&mut self)
}
}
common_traits_conditions! {
/// Contains all bindings for actions
#[derive(Resource, Clone)]
#[derive(Resource, Clone, Deref, DerefMut)]
#[cfg_attr(feature = "reflect", reflect(Resource))]
// TODO: Add delay for input
pub struct Controls<A: Action, Gs: GameState>(
@@ -533,6 +553,11 @@ impl<A: Action, Gs: GameState> Default for Controls<A, Gs> {
}
impl<A: Action, Gs: GameState> Controls<A, Gs> {
/// New [`Controls`] instance
pub fn new() -> ControlsBuilder<A, Gs> {
ControlsBuilder(Controls(HashMap::new()))
}
/// Returns bindings for action
pub fn get(&self, action: A) -> Option<&BindingConfig<Gs>> {
self.0.get(&action)
+27 -2
View File
@@ -1,3 +1,28 @@
<ul>
<li>[ ]</li>
<h1 id="v0.1.0">v0.1.0</h1>
<ul class="task-list">
<li><label><input type="checkbox" checked="" />contracts</label>
<ul class="task-list">
<li><label><input type="checkbox" checked="" />loby<br />
</label></li>
<li><label><input type="checkbox" checked="" />actions<br />
</label></li>
<li><label><input type="checkbox" checked="" />game state<br />
</label></li>
</ul></li>
<li><label><input type="checkbox" checked="" />way to define default
controlls</label></li>
<li><label><input type="checkbox" />layers for controlls: if on (shift +
space) action enable, ignore on (space) action</label></li>
<li><label><input type="checkbox" />warnings on actions
collision</label></li>
<li><label><input type="checkbox" />basic example</label></li>
</ul>
<h1 id="v0.2.0">v0.2.0</h1>
<ul class="task-list">
<li><label><input type="checkbox" />layers for controlls: user
posibility give priority to controls</label></li>
<li><label><input type="checkbox" />controller support</label></li>
<li><label><input type="checkbox" />touch pad support</label></li>
<li><label><input type="checkbox" />posibility support more that one
game state</label></li>
</ul>