feat: controlls builder
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
- [x] loby
|
||||
- [x] actions
|
||||
- [x] game state
|
||||
-[ ] way to define default controlls
|
||||
- [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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user