docs: simplest example
This commit is contained in:
+79
-63
@@ -14,22 +14,19 @@ use bevy_controls::{
|
|||||||
Action, ActionInner, GameState, GameStateInner, InputsContainer, InputsContainerInner,
|
Action, ActionInner, GameState, GameStateInner, InputsContainer, InputsContainerInner,
|
||||||
},
|
},
|
||||||
plugin::ControlsPlugin,
|
plugin::ControlsPlugin,
|
||||||
resource::{Binding, ButtonCombination, Controls, InputType, Inputs, PlayerInputs},
|
resource::{Binding, ButtonCombination, Controls, InputType, InputValue, Inputs, PlayerInputs},
|
||||||
};
|
};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
const FIRA_CODE_PATH: &str = "font/FiraCode/FiraCode-VariableFont_wght.ttf";
|
const FIRA_CODE_PATH: &str = "font/FiraCode/FiraCode-VariableFont_wght.ttf";
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, EnumIter, Clone, Copy)]
|
#[derive(PartialEq, Eq, Hash, EnumIter, Clone, Copy, Debug)]
|
||||||
enum MyAction {
|
enum MyAction {
|
||||||
MoveForward,
|
MoveForward,
|
||||||
MoveBack,
|
MoveBack,
|
||||||
MoveLeft,
|
MoveLeft,
|
||||||
MoveRight,
|
MoveRight,
|
||||||
Jump,
|
Jump,
|
||||||
Interact,
|
|
||||||
Shoot,
|
|
||||||
Scope,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Action for MyAction {}
|
impl Action for MyAction {}
|
||||||
@@ -46,7 +43,7 @@ impl GameState for MyGameState {}
|
|||||||
|
|
||||||
impl GameStateInner for MyGameState {}
|
impl GameStateInner for MyGameState {}
|
||||||
|
|
||||||
#[derive(Resource, Default, Clone)]
|
#[derive(Resource, Default, Clone, Debug)]
|
||||||
struct MyInputsContainer {
|
struct MyInputsContainer {
|
||||||
// When the game does not provide multiplayer, one field is enough
|
// When the game does not provide multiplayer, one field is enough
|
||||||
player_inputs: PlayerInputs<MyAction>,
|
player_inputs: PlayerInputs<MyAction>,
|
||||||
@@ -78,58 +75,49 @@ fn main() {
|
|||||||
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::default(),
|
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, (add_bindings, setup))
|
.add_systems(Startup, (add_bindings, setup))
|
||||||
|
.add_systems(Update, text_update_system)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: default bingings...
|
// TODO: default bingings...
|
||||||
fn add_bindings(mut controls: ResMut<Controls<MyAction, MyGameState>>) {
|
fn add_bindings(mut controls: ResMut<Controls<MyAction, MyGameState>>) {
|
||||||
controls.push(
|
controls.force_push(
|
||||||
|
MyAction::MoveLeft,
|
||||||
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::A))),
|
||||||
|
);
|
||||||
|
controls.force_push(
|
||||||
|
MyAction::MoveBack,
|
||||||
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::S))),
|
||||||
|
);
|
||||||
|
controls.force_push(
|
||||||
MyAction::MoveForward,
|
MyAction::MoveForward,
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::W))),
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::W))),
|
||||||
);
|
);
|
||||||
|
controls.force_push(
|
||||||
|
MyAction::MoveRight,
|
||||||
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::D))),
|
||||||
|
);
|
||||||
|
controls.force_push(
|
||||||
|
MyAction::Jump,
|
||||||
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(KeyCode::Space))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct TextBlock;
|
struct TextBlock;
|
||||||
|
|
||||||
|
const TEXT_SIZE: f32 = 30.;
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn(Camera2dBundle::default());
|
commands.spawn(Camera2dBundle::default());
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
TextBundle::from_sections([
|
TextBundle::from_sections([
|
||||||
TextSection::new(
|
TextSection::new(
|
||||||
"Input: ",
|
"Inputs: A S W D Space",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font: asset_server.load(FIRA_CODE_PATH),
|
font: asset_server.load(FIRA_CODE_PATH),
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextSection::new(
|
|
||||||
"A ",
|
|
||||||
TextStyle {
|
|
||||||
font_size: 60.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextSection::new(
|
|
||||||
"S ",
|
|
||||||
TextStyle {
|
|
||||||
font_size: 60.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextSection::new(
|
|
||||||
"W ",
|
|
||||||
TextStyle {
|
|
||||||
font_size: 60.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextSection::new(
|
|
||||||
"D ",
|
|
||||||
TextStyle {
|
|
||||||
font_size: 60.0,
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -137,35 +125,42 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
"\nAction: ",
|
"\nAction: ",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font: asset_server.load(FIRA_CODE_PATH),
|
font: asset_server.load(FIRA_CODE_PATH),
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSection::new(
|
TextSection::new(
|
||||||
"MoveLeft",
|
"MoveLeft ",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSection::new(
|
TextSection::new(
|
||||||
"MoveBack",
|
"MoveBack ",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSection::new(
|
TextSection::new(
|
||||||
"MoveForward",
|
"MoveForward ",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSection::new(
|
TextSection::new(
|
||||||
"MoveRight",
|
"MoveRight ",
|
||||||
TextStyle {
|
TextStyle {
|
||||||
font_size: 60.0,
|
font_size: TEXT_SIZE,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSection::new(
|
||||||
|
"Jump",
|
||||||
|
TextStyle {
|
||||||
|
font_size: TEXT_SIZE,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -175,31 +170,52 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// text sections indexes
|
// text sections indexes
|
||||||
const A_INDEX: usize = 2;
|
const MOVE_LEFT_INDEX: usize = 2;
|
||||||
const S_INDEX: usize = 3;
|
const MOVE_BACK_INDEX: usize = 3;
|
||||||
const W_INDEX: usize = 4;
|
const MOVE_FORWARD_INDEX: usize = 4;
|
||||||
const D_INDEX: usize = 5;
|
const MOVE_RIGHT_INDEX: usize = 5;
|
||||||
|
const JUMP_INDEX: usize = 6;
|
||||||
const MOVE_LEFT_INDEX: usize = 6;
|
|
||||||
const MOVE_BACK_INDEX: usize = 7;
|
|
||||||
const MOVE_FORWARD_INDEX: usize = 8;
|
|
||||||
const MOVE_RIGHT_INDEX: usize = 9;
|
|
||||||
|
|
||||||
fn text_update_system(
|
fn text_update_system(
|
||||||
mut query: Query<&mut Text, With<TextBlock>>,
|
mut query: Query<&mut Text, With<TextBlock>>,
|
||||||
inputs_container: MyInputsContainer,
|
inputs_container: Res<MyInputsContainer>,
|
||||||
) {
|
) {
|
||||||
for mut text in &mut query {
|
for text in query.iter_mut() {
|
||||||
|
|
||||||
let input_value = inputs_container.me().expect("This is bad").get(MyAction::MoveLeft);
|
let player_inputs = inputs_container.me().expect("This is bad");
|
||||||
if input_value.is_boolean() && input_value.to_boolean() {
|
let mut text = update_text(
|
||||||
text.sections[MOVE_LEFT_INDEX].style.color = Color::GOLD;
|
text,
|
||||||
} else {
|
player_inputs.get(MyAction::MoveLeft),
|
||||||
text.sections[MOVE_LEFT_INDEX].style.color = Color::GOLD;
|
MOVE_LEFT_INDEX);
|
||||||
}
|
text = update_text(
|
||||||
|
text,
|
||||||
|
player_inputs.get(MyAction::MoveBack),
|
||||||
|
MOVE_BACK_INDEX);
|
||||||
|
text = update_text(
|
||||||
|
text,
|
||||||
|
player_inputs.get(MyAction::MoveForward),
|
||||||
|
MOVE_FORWARD_INDEX);
|
||||||
|
text = update_text(
|
||||||
|
text,
|
||||||
|
player_inputs.get(MyAction::MoveRight),
|
||||||
|
MOVE_RIGHT_INDEX);
|
||||||
|
_ = update_text(
|
||||||
|
text,
|
||||||
|
InputValue::Boolean(player_inputs.get_just_pressed(MyAction::Jump).unwrap_or(false)),
|
||||||
|
JUMP_INDEX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_text(mut text: Mut<Text>, input_value: InputValue, index: usize) -> Mut<Text>{
|
||||||
|
if input_value.is_boolean() && input_value.to_boolean() {
|
||||||
|
text.sections[index].style.color = Color::GOLD;
|
||||||
|
} else {
|
||||||
|
text.sections[index].style.color = Color::WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
text
|
||||||
|
}
|
||||||
|
|
||||||
// fn text_update_system(
|
// fn text_update_system(
|
||||||
// diagnostics: Res<DiagnosticsStore>,
|
// diagnostics: Res<DiagnosticsStore>,
|
||||||
// mut query: Query<&mut Text, With<FpsText>>,
|
// mut query: Query<&mut Text, With<FpsText>>,
|
||||||
|
|||||||
+3
-2
@@ -23,13 +23,14 @@ pub trait ActionInner:
|
|||||||
+ IntoEnumIterator
|
+ IntoEnumIterator
|
||||||
+ Clone
|
+ Clone
|
||||||
+ Copy
|
+ Copy
|
||||||
|
+ std::fmt::Debug
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States + Default {}
|
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States + Default + std::fmt::Debug {}
|
||||||
|
|
||||||
pub trait InputsContainerInner<A: Action>:
|
pub trait InputsContainerInner<A: Action>:
|
||||||
'static + std::marker::Sync + std::marker::Send + Resource + Default
|
'static + std::marker::Sync + std::marker::Send + Resource + Default + std::fmt::Debug
|
||||||
{
|
{
|
||||||
/// The method returning an iterator over references to the PlayerInputs.
|
/// The method returning an iterator over references to the PlayerInputs.
|
||||||
/// Note: the use of 'a for both the method's lifetime and the returned Iterator's lifetime.
|
/// Note: the use of 'a for both the method's lifetime and the returned Iterator's lifetime.
|
||||||
|
|||||||
@@ -63,9 +63,13 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::trace!("action {:?} in condition", action);
|
||||||
|
|
||||||
match &binding.input {
|
match &binding.input {
|
||||||
ButtonCombination::Single(button) => match button {
|
ButtonCombination::Single(button) => match button {
|
||||||
InputType::Keyboard(key) => {
|
InputType::Keyboard(key) => {
|
||||||
|
log::trace!("presed?: {:?}", keyboard_input.pressed(*key));
|
||||||
|
log::trace!("active");
|
||||||
inputs.forced_set(*action, keyboard_input.pressed(*key));
|
inputs.forced_set(*action, keyboard_input.pressed(*key));
|
||||||
}
|
}
|
||||||
InputType::Mouse(input) => {
|
InputType::Mouse(input) => {
|
||||||
@@ -117,6 +121,7 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::trace!("active");
|
||||||
// TODO: should be [`Chord`](ButtonCombination::Chord) only [`Boolean`](InputValue::Boolean) type
|
// TODO: should be [`Chord`](ButtonCombination::Chord) only [`Boolean`](InputValue::Boolean) type
|
||||||
inputs.forced_set(*action, true);
|
inputs.forced_set(*action, true);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-3
@@ -112,6 +112,7 @@ impl<A: Action> PlayerInputs<A> {
|
|||||||
panic!("This input is not boolean")
|
panic!("This input is not boolean")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this is must be binding option for non axis inputs
|
||||||
/// Returns `true` if current `InputValue` has become `InputValue::Boolean(true)` in this frame
|
/// Returns `true` if current `InputValue` has become `InputValue::Boolean(true)` in this frame
|
||||||
/// If input has not same type as `InputValue` on previous frame return `Error()`
|
/// If input has not same type as `InputValue` on previous frame return `Error()`
|
||||||
pub fn get_just_pressed(&self, action: A) -> Result<bool, Box<dyn error::Error>> {
|
pub fn get_just_pressed(&self, action: A) -> Result<bool, Box<dyn error::Error>> {
|
||||||
@@ -238,7 +239,7 @@ common_traits_conditions! {
|
|||||||
Tap,
|
Tap,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ActivationOptions {
|
pub struct ActivationOptions {
|
||||||
/// Manner in which input is registered
|
/// Manner in which input is registered
|
||||||
pub mode: ActivationMode,
|
pub mode: ActivationMode,
|
||||||
@@ -388,7 +389,7 @@ impl InputValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common_traits_conditions! {
|
common_traits_conditions! {
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Bindings<Gs: GameState> {
|
pub struct Bindings<Gs: GameState> {
|
||||||
/// List of bindings for action
|
/// List of bindings for action
|
||||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||||
@@ -415,6 +416,10 @@ impl<Gs: GameState> Bindings<Gs> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn force_push(&mut self, binding: Binding<Gs>) {
|
||||||
|
self.list.push(binding);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, binding: Binding<Gs>) {
|
pub fn push(&mut self, binding: Binding<Gs>) {
|
||||||
if self.options == OptionsMode::Immutable {
|
if self.options == OptionsMode::Immutable {
|
||||||
warn!("You try to push binding to immutable bindings");
|
warn!("You try to push binding to immutable bindings");
|
||||||
@@ -468,7 +473,7 @@ impl<Gs: GameState> Bindings<Gs> {
|
|||||||
common_traits_conditions! {
|
common_traits_conditions! {
|
||||||
/// Contains all bindings for action
|
/// Contains all bindings for action
|
||||||
/// and different activation options
|
/// and different activation options
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct BindingConfig<Gs: GameState> {
|
pub struct BindingConfig<Gs: GameState> {
|
||||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||||
pub bindings: Bindings<Gs>,
|
pub bindings: Bindings<Gs>,
|
||||||
@@ -517,6 +522,17 @@ impl<A: Action, Gs: GameState> Controls<A, Gs> {
|
|||||||
self.0.get(&action)
|
self.0.get(&action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forced push new binding for action
|
||||||
|
pub fn force_push(&mut self, action: A, binding: Binding<Gs>) {
|
||||||
|
// TODO: warning if config is not exist yet
|
||||||
|
// TODO: interface for [`ActivationOptions`]
|
||||||
|
self.0
|
||||||
|
.entry(action)
|
||||||
|
.or_insert_with(BindingConfig::default)
|
||||||
|
.bindings
|
||||||
|
.force_push(binding);
|
||||||
|
}
|
||||||
|
|
||||||
/// Push new binding for action
|
/// Push new binding for action
|
||||||
pub fn push(&mut self, action: A, binding: Binding<Gs>) {
|
pub fn push(&mut self, action: A, binding: Binding<Gs>) {
|
||||||
// TODO: warning if config is not exist yet
|
// TODO: warning if config is not exist yet
|
||||||
|
|||||||
Reference in New Issue
Block a user