feat!: fix some lifetimes in InputContainerInner, almast done one example, add default for plugin, rebase src from crate/bevy_controls to src
This commit is contained in:
+105
-37
@@ -1,102 +1,170 @@
|
||||
use std::hash::Hash;
|
||||
|
||||
use bevy_ecs::{system::Resource, schedule::States};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use bevy_ecs::{schedule::States, system::Resource};
|
||||
#[cfg(feature = "inspector-egui")]
|
||||
use bevy_inspector_egui::inspector_options::InspectorOptionsType;
|
||||
use strum::IntoEnumIterator;
|
||||
// #[cfg(all(feature = "reflect", feature = "serialize"))]
|
||||
// use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
#[cfg(feature = "reflect")]
|
||||
use bevy_reflect::{Reflect, TypePath, FromReflect};
|
||||
use bevy_reflect::{FromReflect, Reflect, TypePath};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::resource::PlayerInputs;
|
||||
|
||||
pub trait ActionInner: 'static + std::marker::Sync + std::marker::Send + PartialEq + Eq + Hash + IntoEnumIterator + Clone + Copy { }
|
||||
pub trait ActionInner:
|
||||
'static
|
||||
+ std::marker::Sync
|
||||
+ std::marker::Send
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ Hash
|
||||
+ IntoEnumIterator
|
||||
+ Clone
|
||||
+ Copy
|
||||
{
|
||||
}
|
||||
|
||||
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States { }
|
||||
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States + Default {}
|
||||
|
||||
pub trait InputsContainerInner<A: Action>: 'static + std::marker::Sync + std::marker::Send + Resource {
|
||||
pub trait InputsContainerInner<A: Action>:
|
||||
'static + std::marker::Sync + std::marker::Send + Resource + Default
|
||||
{
|
||||
/// 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.
|
||||
fn iter_inputs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a PlayerInputs<A>> + 'a>;
|
||||
/// The method returning an player inputs of this client.
|
||||
fn me<'a>(&self) -> Option<&'a PlayerInputs<A>>;
|
||||
fn me<'a>(&'a self) -> Option<&'a PlayerInputs<A>>;
|
||||
/// The method returning an mutable player inputs of this client.
|
||||
fn me_mut<'a>(&self) -> Option<&'a mut PlayerInputs<A>>;
|
||||
fn me_mut<'a>(&'a mut self) -> Option<&'a mut PlayerInputs<A>>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub trait SerializeImpl: Serialize + DeserializeOwned { }
|
||||
pub trait SerializeImpl: Serialize + DeserializeOwned {}
|
||||
|
||||
#[cfg(feature = "reflect")]
|
||||
pub trait ReflectImpl: Reflect + TypePath + FromReflect { }
|
||||
pub trait ReflectImpl: Reflect + TypePath + FromReflect {}
|
||||
|
||||
#[cfg(feature = "inspector-egui")]
|
||||
pub trait InspectorEguiImpl: InspectorOptionsType { }
|
||||
pub trait InspectorEguiImpl: InspectorOptionsType {}
|
||||
|
||||
// ===== Action =====
|
||||
// SAFATY: inspector-egui includes reflect
|
||||
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait Action: ActionInner {}
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait Action: ActionInner + SerializeImpl {}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait Action: ActionInner + ReflectImpl {}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
|
||||
pub trait Action: ActionInner + SerializeImpl + ReflectImpl{}
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait Action: ActionInner + SerializeImpl + ReflectImpl {}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
|
||||
pub trait Action: ActionInner + ReflectImpl + InspectorEguiImpl {}
|
||||
pub trait Action: ActionInner + ReflectImpl + InspectorEguiImpl {}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
|
||||
pub trait Action: ActionInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
|
||||
pub trait Action: ActionInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
|
||||
|
||||
// ===== GameState =====
|
||||
// SAFATY: inspector-egui includes reflect
|
||||
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait GameState: GameStateInner {}
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait GameState: GameStateInner + SerializeImpl {}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait GameState: GameStateInner + ReflectImpl {}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
|
||||
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl{}
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl {}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
|
||||
pub trait GameState: GameStateInner + ReflectImpl + InspectorEguiImpl {}
|
||||
pub trait GameState: GameStateInner + ReflectImpl + InspectorEguiImpl {}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
|
||||
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
|
||||
|
||||
|
||||
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
|
||||
|
||||
// ===== InputsContainer =====
|
||||
// SAFATY: inspector-egui includes reflect
|
||||
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> {}
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
not(feature = "reflect"),
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl {}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
|
||||
#[cfg(all(
|
||||
not(feature = "serialize"),
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + ReflectImpl {}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl + ReflectImpl{}
|
||||
#[cfg(all(
|
||||
feature = "serialize",
|
||||
feature = "reflect",
|
||||
not(feature = "inspector-egui")
|
||||
))]
|
||||
pub trait InputsContainer<A: Action>:
|
||||
InputsContainerInner<A> + SerializeImpl + ReflectImpl
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + ReflectImpl + InspectorEguiImpl {}
|
||||
pub trait InputsContainer<A: Action>:
|
||||
InputsContainerInner<A> + ReflectImpl + InspectorEguiImpl
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
|
||||
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
|
||||
pub trait InputsContainer<A: Action>:
|
||||
InputsContainerInner<A> + SerializeImpl + ReflectImpl + InspectorEguiImpl
|
||||
{
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
|
||||
+18
-6
@@ -4,10 +4,20 @@ use bevy_input::{Input, keyboard::KeyCode, mouse::MouseButton};
|
||||
|
||||
use crate::{resource::*, contract::{Action, GameState, InputsContainer}};
|
||||
|
||||
pub struct ControlsPlugin<Action, InputsContainer, GameState> {
|
||||
_action: std::marker::PhantomData<Action>,
|
||||
_inputs_container: std::marker::PhantomData<InputsContainer>,
|
||||
_game_state: std::marker::PhantomData<GameState>,
|
||||
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<
|
||||
@@ -17,6 +27,8 @@ impl<
|
||||
> 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")]
|
||||
@@ -34,7 +46,7 @@ impl<
|
||||
fn save_input(
|
||||
keyboard_input: Res<Input<KeyCode>>,
|
||||
mouse_input: Res<Input<MouseButton>>,
|
||||
lobby: ResMut<Ic>,
|
||||
mut lobby: ResMut<Ic>,
|
||||
controls: Res<Controls<A, Gs>>,
|
||||
game_state: Res<State<Gs>>,
|
||||
) {
|
||||
@@ -235,4 +247,4 @@ impl<
|
||||
|
||||
// log::info!("without casting: {:?}", duration);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
+30
-7
@@ -80,6 +80,14 @@ impl<A: Action> PlayerInputs<A> {
|
||||
*self.current.get(&action).unwrap()
|
||||
}
|
||||
|
||||
// TODO
|
||||
// /// Returns current input value for action
|
||||
// ///
|
||||
// /// in any case faster that `get_many` method
|
||||
// pub fn all_active(&self) -> InputValue {
|
||||
// *self.current.iter().filter(|a| {a.})
|
||||
// }
|
||||
|
||||
/// Returns current input values for actions
|
||||
pub fn get_many(&self, actions: Vec<A>) -> Vec<InputValue> {
|
||||
// SAFETY: action is always valid
|
||||
@@ -119,6 +127,8 @@ impl<A: Action> PlayerInputs<A> {
|
||||
}
|
||||
|
||||
/// Set input value to new
|
||||
///
|
||||
/// notice: not recomended to use if you do not know what do you do
|
||||
pub fn forced_set(&mut self, action: A, value: impl Into<InputValue>) {
|
||||
// SAFETY: action is always valid
|
||||
// because we iterate over all actions in `default` method
|
||||
@@ -129,6 +139,8 @@ impl<A: Action> PlayerInputs<A> {
|
||||
|
||||
/// Set input value to new
|
||||
/// if it is not the same InputValue type return `Error()`
|
||||
///
|
||||
/// notice: not recomended to use if you do not know what do you do
|
||||
pub fn set(
|
||||
&mut self,
|
||||
action: A,
|
||||
@@ -150,6 +162,8 @@ impl<A: Action> PlayerInputs<A> {
|
||||
}
|
||||
|
||||
/// Update current inputs
|
||||
///
|
||||
/// notice: not recomended to use if you do not know what do you do
|
||||
pub fn forced_update(&mut self, inputs: Inputs<A>) {
|
||||
*self.previous = self.current.clone().0;
|
||||
*self.current = inputs.0;
|
||||
@@ -158,6 +172,8 @@ impl<A: Action> PlayerInputs<A> {
|
||||
/// Update current inputs
|
||||
/// if any input is not the same InputValue type return `Error()`
|
||||
/// ! remember that it work safely but slow
|
||||
///
|
||||
/// notice: not recomended to use if you do not know what do you do
|
||||
pub fn update(&mut self, inputs: Inputs<A>) -> Result<(), Box<dyn error::Error>> {
|
||||
// SAFETY: action is always valid
|
||||
// because we iterate over all actions in `default` method
|
||||
@@ -222,6 +238,7 @@ common_traits_conditions! {
|
||||
Tap,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ActivationOptions {
|
||||
/// Manner in which input is registered
|
||||
pub mode: ActivationMode,
|
||||
@@ -361,7 +378,7 @@ impl InputValue {
|
||||
matches!(self, InputValue::Float(_))
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> bool {
|
||||
pub fn to_boolean(&self) -> bool {
|
||||
match self {
|
||||
InputValue::Boolean(value) => *value,
|
||||
InputValue::Float(value) => *value > 0.0,
|
||||
@@ -371,6 +388,7 @@ impl InputValue {
|
||||
}
|
||||
|
||||
common_traits_conditions! {
|
||||
#[derive(Clone)]
|
||||
pub struct Bindings<Gs: GameState> {
|
||||
/// List of bindings for action
|
||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||
@@ -450,7 +468,7 @@ impl<Gs: GameState> Bindings<Gs> {
|
||||
common_traits_conditions! {
|
||||
/// Contains all bindings for action
|
||||
/// and different activation options
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct BindingConfig<Gs: GameState> {
|
||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||
pub bindings: Bindings<Gs>,
|
||||
@@ -470,22 +488,26 @@ impl<Gs: GameState> BindingConfig<Gs> {
|
||||
|
||||
common_traits_conditions! {
|
||||
/// Contains all bindings for actions
|
||||
#[derive(Resource)]
|
||||
#[derive(Resource, Clone)]
|
||||
#[cfg_attr(feature = "reflect", reflect(Resource))]
|
||||
// TODO: Add delay for input
|
||||
pub struct Controls<A: Action, Gs: GameState>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, BindingConfig<Gs>>); // TODO: change HashMap to Vec for faster iteration
|
||||
pub struct Controls<A: Action, Gs: GameState>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, BindingConfig<Gs>>); // TODO: change HashMap to Vec for faster iteration
|
||||
}
|
||||
|
||||
impl<A: Action, Gs: GameState> Default for Controls<A, Gs> {
|
||||
fn default() -> Self {
|
||||
// TODO: default must create empty controls or fill empty / default BindingConfig on any actions
|
||||
let controls = Self(HashMap::new());
|
||||
let mut controls = HashMap::new();
|
||||
|
||||
for key in A::iter() {
|
||||
controls.insert(key, BindingConfig::default());
|
||||
}
|
||||
|
||||
// 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(&controls.0));
|
||||
assert!(validate_hash_map(&controls));
|
||||
|
||||
controls
|
||||
Self(controls)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,6 +520,7 @@ impl<A: Action, Gs: GameState> Controls<A, Gs> {
|
||||
/// Push new binding for action
|
||||
pub fn 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)
|
||||
|
||||
Reference in New Issue
Block a user