feat: controlls builder
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
# v0.1.0
|
# v0.1.0
|
||||||
-[x] contracts
|
- [x] contracts
|
||||||
-[x] loby
|
- [x] loby
|
||||||
-[x] actions
|
- [x] actions
|
||||||
-[x] game state
|
- [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
|
- [ ] layers for controlls: if on (shift + space) action enable, ignore on (space) action
|
||||||
-[ ] warnings on actions collision
|
- [ ] warnings on actions collision
|
||||||
-[ ] basic example
|
- [ ] basic example
|
||||||
|
|
||||||
# v0.2.0
|
# v0.2.0
|
||||||
-[ ] layers for controlls: user posibility give priority to controls
|
- [ ] layers for controlls: user posibility give priority to controls
|
||||||
-[ ] controller support
|
- [ ] controller support
|
||||||
-[ ] touch pad support
|
- [ ] touch pad support
|
||||||
-[ ] posibility support more that one game state
|
- [ ] posibility support more that one game state
|
||||||
|
|||||||
@@ -66,85 +66,80 @@ fn main() {
|
|||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::default(),
|
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::new(
|
||||||
))
|
Controls::<MyAction, MyGameState>::new()
|
||||||
.insert_resource(ClearColor(Color::NONE))
|
.with(
|
||||||
.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(
|
|
||||||
MyAction::Left,
|
MyAction::Left,
|
||||||
// this way input will work only on latin layout only on `A`
|
// this way input will work only on latin layout only on `A`
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::KeyCode(KeyCode::A),
|
Keyboard::KeyCode(KeyCode::A),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
controls.force_push(
|
.with(
|
||||||
MyAction::Left,
|
MyAction::Left,
|
||||||
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or
|
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or
|
||||||
// `О` for `ru` layout
|
// `О` for `ru` layout
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::ScanCode(ScanCode(35)),
|
Keyboard::ScanCode(ScanCode(35)),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
|
.with(
|
||||||
controls.force_push(
|
|
||||||
MyAction::Back,
|
MyAction::Back,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::KeyCode(KeyCode::S),
|
Keyboard::KeyCode(KeyCode::S),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
controls.force_push(
|
.with(
|
||||||
MyAction::Back,
|
MyAction::Back,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::ScanCode(ScanCode(36)),
|
Keyboard::ScanCode(ScanCode(36)),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
|
.with(
|
||||||
controls.force_push(
|
|
||||||
MyAction::Forward,
|
MyAction::Forward,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::KeyCode(KeyCode::W),
|
Keyboard::KeyCode(KeyCode::W),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
controls.force_push(
|
.with(
|
||||||
MyAction::Forward,
|
MyAction::Forward,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::ScanCode(ScanCode(37)),
|
Keyboard::ScanCode(ScanCode(37)),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
|
.with(
|
||||||
controls.force_push(
|
|
||||||
MyAction::Right,
|
MyAction::Right,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::KeyCode(KeyCode::D),
|
Keyboard::KeyCode(KeyCode::D),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
controls.force_push(
|
.with(
|
||||||
MyAction::Right,
|
MyAction::Right,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::ScanCode(ScanCode(38)),
|
Keyboard::ScanCode(ScanCode(38)),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
|
.with(
|
||||||
controls.force_push(
|
|
||||||
MyAction::Up,
|
MyAction::Up,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
Keyboard::KeyCode(KeyCode::Space),
|
Keyboard::KeyCode(KeyCode::Space),
|
||||||
))),
|
))),
|
||||||
);
|
)
|
||||||
controls.force_push(
|
.with(
|
||||||
MyAction::Down,
|
MyAction::Down,
|
||||||
Binding::new(ButtonCombination::Chord(vec![
|
Binding::new(ButtonCombination::Chord(vec![
|
||||||
InputType::Keyboard(Keyboard::KeyCode(KeyCode::ShiftLeft)),
|
InputType::Keyboard(Keyboard::KeyCode(KeyCode::ShiftLeft)),
|
||||||
InputType::Keyboard(Keyboard::KeyCode(KeyCode::Space)),
|
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)]
|
#[derive(Component)]
|
||||||
@@ -158,7 +153,7 @@ fn setup(mut commands: Commands) {
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
TextBundle::from_sections([
|
TextBundle::from_sections([
|
||||||
TextSection::new(
|
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 {
|
TextStyle {
|
||||||
font_size: TEXT_SIZE,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
|
|||||||
@@ -14,26 +14,40 @@ use crate::{
|
|||||||
resource::*,
|
resource::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ControlsPlugin<A, Ic, Gs> {
|
pub struct ControlsPlugin<A: Action, Ic: InputsContainer<A>, Gs: GameState> {
|
||||||
_action: std::marker::PhantomData<A>,
|
_action: std::marker::PhantomData<A>,
|
||||||
_inputs_container: std::marker::PhantomData<Ic>,
|
_inputs_container: std::marker::PhantomData<Ic>,
|
||||||
_game_state: std::marker::PhantomData<Gs>,
|
_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 {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
_action: std::marker::PhantomData,
|
_action: std::marker::PhantomData,
|
||||||
_inputs_container: std::marker::PhantomData,
|
_inputs_container: std::marker::PhantomData,
|
||||||
_game_state: 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> {
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin<A, Ic, Gs> {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app
|
||||||
.init_resource::<Controls<A, Gs>>()
|
.insert_resource(self.controls.clone())
|
||||||
.init_resource::<Ic>()
|
.init_resource::<Ic>()
|
||||||
.add_state::<Gs>()
|
.add_state::<Gs>()
|
||||||
.add_systems(Update, Self::save_input);
|
.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>>,
|
controls: Res<Controls<A, Gs>>,
|
||||||
game_state: Res<State<Gs>>,
|
game_state: Res<State<Gs>>,
|
||||||
) {
|
) {
|
||||||
|
// get current client inputs
|
||||||
if let Some(inputs) = inputs_container.me_mut() {
|
if let Some(inputs) = inputs_container.me_mut() {
|
||||||
for (action, config) in controls.iter() {
|
for (action, config) in controls.iter() {
|
||||||
'bindings_loop: for binding in config.bindings.iter() {
|
'bindings_loop: for binding in config.bindings.iter() {
|
||||||
|
// check binding conditions
|
||||||
for condition in &binding.conditions {
|
for condition in &binding.conditions {
|
||||||
match condition {
|
match condition {
|
||||||
BindingCondition::InGameState(state) => {
|
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);
|
log::trace!("action binding {:?} {:?} in condition", action, binding);
|
||||||
|
|
||||||
|
// check inputs & mark actions
|
||||||
match &binding.input {
|
match &binding.input {
|
||||||
ButtonCombination::Single(button) => match button {
|
ButtonCombination::Single(button) => match button {
|
||||||
InputType::Keyboard(Keyboard::KeyCode(key)) => {
|
InputType::Keyboard(Keyboard::KeyCode(key)) => {
|
||||||
log::trace!("presed?: {:?}", keyboard_input_keycode.pressed(*key));
|
|
||||||
log::trace!("active");
|
|
||||||
if keyboard_input_keycode.pressed(*key) {
|
if keyboard_input_keycode.pressed(*key) {
|
||||||
inputs.forced_set(*action, true);
|
inputs.forced_set(*action, true);
|
||||||
break; // when on binding trigered no sence check another
|
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)) => {
|
InputType::Keyboard(Keyboard::ScanCode(code)) => {
|
||||||
log::trace!("presed?: {:?}", keyboard_input_scancode.pressed(*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);
|
inputs.forced_set(*action, true);
|
||||||
break; // when on binding trigered no sence check another
|
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 {
|
InputType::Mouse(input) => match input {
|
||||||
MouseInput::Axis(_axis) => {
|
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! {
|
common_traits_conditions! {
|
||||||
/// Contains all bindings for actions
|
/// Contains all bindings for actions
|
||||||
#[derive(Resource, Clone)]
|
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||||
#[cfg_attr(feature = "reflect", reflect(Resource))]
|
#[cfg_attr(feature = "reflect", reflect(Resource))]
|
||||||
// TODO: Add delay for input
|
// TODO: Add delay for input
|
||||||
pub struct Controls<A: Action, Gs: GameState>(
|
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> {
|
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
|
/// Returns bindings for action
|
||||||
pub fn get(&self, action: A) -> Option<&BindingConfig<Gs>> {
|
pub fn get(&self, action: A) -> Option<&BindingConfig<Gs>> {
|
||||||
self.0.get(&action)
|
self.0.get(&action)
|
||||||
|
|||||||
+27
-2
@@ -1,3 +1,28 @@
|
|||||||
<ul>
|
<h1 id="v0.1.0">v0.1.0</h1>
|
||||||
<li>[ ]</li>
|
<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>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user