diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 4e415f1..fd8aeea 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,4 +1,4 @@ -use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion}, game::Game, plugin::Plugin, render::window::{CursorGrabMode, WindowOptions}, change_tracker::Ct}; +use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId}, game::Game, plugin::Plugin, render::window::{CursorGrabMode, WindowOptions}, change_tracker::Ct}; use lyra_engine::assets::{ResourceManager, Model}; mod free_fly_camera; @@ -58,6 +58,22 @@ struct TpsAccumulator(f32); #[async_std::main] async fn main() { + let _action_handler = ActionHandler::new() + .add_layout(LayoutId::from(0)) + .add_action("forward_backward", Action::new(ActionKind::Button)) + .add_action("left_right", Action::new(ActionKind::Button)) + .add_mapping(ActionMapping::new(LayoutId::from(0), ActionMappingId::from(0)) + .bind("forward_backward", &[ + ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), + ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0) + ]) + .bind("left_right", &[ + ActionSource::Keyboard(KeyCode::A).into_binding_modifier(1.0), + ActionSource::Keyboard(KeyCode::D).into_binding_modifier(-1.0) + ]) + .finish() + ); + let setup_sys = |world: &mut World| -> anyhow::Result<()> { { let mut window_options = world.get_resource_mut::>().unwrap(); diff --git a/src/input/action.rs b/src/input/action.rs new file mode 100644 index 0000000..549a7db --- /dev/null +++ b/src/input/action.rs @@ -0,0 +1,253 @@ +use std::{collections::HashMap, cell::RefCell, borrow::BorrowMut}; + +use edict::action; + +use crate::castable_any::CastableAny; + +use super::{Button, KeyCode}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +enum GamepadFormat { + DualAxis, + Joystick, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +enum GamepadButton { + FaceBottom, + FaceLeft, + FaceRight, + FaceTop, + VirtualConfirm, + VirtualDeny, + LThumbstick, + RThumbstick, + DPadUp, + DPadDown, + DPadLeft, + DPadRight, + LShoulder, + RShoulder, + LTrigger, + RTrigger, + Special, + LSpecial, + RSpecial, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +enum GamepadAxis { + LThumbstickX, + LThumbstickY, + RThumbstickX, + RThumbstickY, + LTrigger, + RTrigger, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +enum GamepadInput { + Button(GamepadButton), + Axis(GamepadAxis), +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum ActionSource { + Keyboard(KeyCode), + Gamepad(GamepadFormat, GamepadInput), +} + +impl ActionSource { + /// Convert this Source into a Binding. Uses a default Binding modifier value of `1.0`. + pub fn into_binding(self) -> Binding { + Binding::from_source(self) + } + + /// Convert this Source into a Binding, setting the modifier. + pub fn into_binding_modifier(self, modifier: f32) -> Binding { + Binding::from_source_modifier(self, modifier) + } +} + +#[derive(Clone, Debug)] +pub struct Binding { + pub source: ActionSource, + pub modifier: f32, +} + +impl Binding { + /// Create a binding from a Source. Uses a default value of `1.0` as the modifier. + pub fn from_source(source: ActionSource) -> Self { + Self { + source, + modifier: 1.0, + } + } + + /// Create a binding from a Source, with a modifier + pub fn from_source_modifier(source: ActionSource, modifier: f32) -> Self { + Self { + source, + modifier, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum ActionKind { + Button, + Axis +} + +#[derive(Clone, Debug)] +pub struct Action { + kind: ActionKind, +} + +impl Action { + pub fn new(kind: ActionKind) -> Self { + Self { + kind, + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct LayoutId(u32); + +impl From for LayoutId { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl Default for LayoutId { + fn default() -> Self { + Self(0) + } +} + +#[derive(Clone)] +pub struct Layout { + mappings: HashMap, +} + +impl Layout { + pub fn new() -> Self { + Self { + mappings: HashMap::new(), + } + } + + pub fn add_mapping(&mut self, mapping: ActionMapping) -> &mut Self { + self.mappings.insert(mapping.id, mapping); + + self + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ActionMappingId(u32); + +impl From for ActionMappingId { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl Default for ActionMappingId { + fn default() -> Self { + Self(0) + } +} + +#[derive(Clone)] +pub struct ActionMapping { + layout: LayoutId, + id: ActionMappingId, + action_binds: HashMap>, +} + +impl ActionMapping { + pub fn new(layout: LayoutId, id: ActionMappingId) -> Self { + Self { + layout, + id, + action_binds: HashMap::new(), + } + } + + /// Creates a binding for the action. + /// + /// If the action is not in this layout, this will panic! + /// + /// Parameters: + /// * `action_label` - The label corresponding to the action in this Layout. + /// * `bind` - The Binding to add to the Action. + pub fn bind(mut self, action_label: &str, bindings: &[Binding]) -> Self { + let mut bindings = bindings.to_vec(); + + let action_binds = self.action_binds.entry(action_label.to_string()) + .or_insert_with(|| Vec::new()); + action_binds.append(&mut bindings); + + self + } + + /// Creates multiple binding for the action. + /// + /// If the action is not in this layout, this will panic! + /// + /// Parameters: + /// * `action_label` - The label corresponding to the action in this Layout. + /// * `bindings` - The list of Bindings to add to the Action. + /* pub fn add_bindings(&mut self, action_label: String, bindings: &[Binding]) -> &mut Self { + let mut bindings = bindings.to_vec(); + let action_binds = self.action_binds.entry(action_label) + .or_insert_with(|| Vec::new()); + action_binds.append(&mut bindings); + + self + } */ + + pub fn finish(self) -> Self { + self + } +} + +pub struct ActionHandler { + actions: HashMap, + layouts: HashMap, + current_layout: LayoutId, +} + +impl ActionHandler { + pub fn new() -> Self { + Self { + actions: HashMap::new(), + layouts: HashMap::new(), + current_layout: LayoutId::default(), + } + } + + pub fn add_layout(&mut self, id: LayoutId) -> &mut Self { + self.layouts.insert(id, Layout::new()); + + self + } + + pub fn add_action(&mut self, label: &str, action: Action) -> &mut Self { + /* let layout = self.layouts.get_mut(&layout_id).unwrap(); + layout.actions.insert(label.to_string(), action); */ + self.actions.insert(label.to_string(), action); + + self + } + + pub fn add_mapping(&mut self, mapping: ActionMapping) -> &mut Self { + let layout = self.layouts.get_mut(&mapping.layout).unwrap(); + layout.add_mapping(mapping); + + self + } +} \ No newline at end of file diff --git a/src/input/buttons.rs b/src/input/buttons.rs index e279b03..e4205d2 100644 --- a/src/input/buttons.rs +++ b/src/input/buttons.rs @@ -2,14 +2,20 @@ use std::{hash::{Hash, Hasher}, collections::{HashMap, hash_map::DefaultHasher}} use winit::event::ElementState; +//pub trait Button : Clone + Hash + Eq + PartialEq + 'static {} +/* pub trait Button {} +impl Button for T {} */ + +pub trait Button = Clone + Hash + Eq + PartialEq + 'static; + #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub enum ButtonEvent { +pub enum ButtonEvent { Pressed(T), Released(T), JustPressed(T), } -impl ButtonEvent { +impl ButtonEvent { fn take_val(self) -> T { match self { ButtonEvent::Pressed(t) => t, @@ -24,19 +30,19 @@ impl ButtonEvent { } #[derive(Clone)] -pub struct InputButtons { +pub struct InputButtons { // the u64 as the key is the hashed value of T. this makes it easier to // search for a button and see its state button_events: HashMap>, } -impl Default for InputButtons { +impl Default for InputButtons { fn default() -> Self { Self::new() } } -impl InputButtons { +impl InputButtons { pub fn new() -> Self { Self { button_events: HashMap::new(), diff --git a/src/input/mod.rs b/src/input/mod.rs index d322eea..f03bd5f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -8,4 +8,7 @@ pub mod events; pub use events::*; pub mod buttons; -pub use buttons::*; \ No newline at end of file +pub use buttons::*; + +pub mod action; +pub use action::*; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b776596..0892b77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(hash_extract_if)] #![feature(lint_reasons)] +#![feature(trait_alias)] pub mod game; pub mod render;