Merge branch 'feature/input-actions' into 'main'
ci/woodpecker/push/build Pipeline was successful
Details
ci/woodpecker/push/build Pipeline was successful
Details
This commit is contained in:
commit
988fd6bf42
|
@ -1,8 +1,9 @@
|
||||||
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, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::window::{CursorGrabMode, WindowOptions}, change_tracker::Ct};
|
||||||
use lyra_engine::assets::{ResourceManager, Model};
|
use lyra_engine::assets::{ResourceManager, Model};
|
||||||
|
|
||||||
mod free_fly_camera;
|
mod free_fly_camera;
|
||||||
use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera};
|
use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
struct FixedTimestep {
|
struct FixedTimestep {
|
||||||
max_tps: u32,
|
max_tps: u32,
|
||||||
|
@ -128,9 +129,46 @@ async fn main() {
|
||||||
fps_plugin(game);
|
fps_plugin(game);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let action_handler_plugin = |game: &mut Game| {
|
||||||
|
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 test_system = |world: &mut World| -> anyhow::Result<()> {
|
||||||
|
let handler = world.get_resource::<ActionHandler>().unwrap();
|
||||||
|
|
||||||
|
if let Some(alpha) = handler.get_pressed_modifier("forward_backward") {
|
||||||
|
debug!("'forward_backward': {alpha}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(alpha) = handler.get_pressed_modifier("left_right") {
|
||||||
|
debug!("'left_right': {alpha}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
game.world().insert_resource(action_handler);
|
||||||
|
game.with_plugin(InputActionPlugin);
|
||||||
|
game.with_system("test_actions", test_system, &[]);
|
||||||
|
};
|
||||||
|
|
||||||
Game::initialize().await
|
Game::initialize().await
|
||||||
.with_plugin(lyra_engine::DefaultPlugins)
|
.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
.with_startup_system(setup_sys)
|
.with_startup_system(setup_sys)
|
||||||
|
.with_plugin(action_handler_plugin)
|
||||||
//.with_plugin(fps_plugin)
|
//.with_plugin(fps_plugin)
|
||||||
.with_plugin(jiggle_plugin)
|
.with_plugin(jiggle_plugin)
|
||||||
.with_plugin(FreeFlyCameraPlugin)
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
|
|
@ -12,7 +12,7 @@ use tracing_subscriber::{
|
||||||
|
|
||||||
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
|
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
|
||||||
|
|
||||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, window::WindowOptions}, input_event::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, plugin::Plugin, change_tracker::Ct};
|
use crate::{render::{renderer::{Renderer, BasicRenderer}, window::WindowOptions}, input::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, plugin::Plugin, change_tracker::Ct};
|
||||||
|
|
||||||
pub struct Controls<'a> {
|
pub struct Controls<'a> {
|
||||||
pub world: &'a mut edict::World,
|
pub world: &'a mut edict::World,
|
||||||
|
|
351
src/input.rs
351
src/input.rs
|
@ -1,351 +0,0 @@
|
||||||
use std::{collections::{HashMap, hash_map::DefaultHasher}, hash::{Hash, Hasher}};
|
|
||||||
|
|
||||||
use glam::Vec2;
|
|
||||||
use tracing::debug;
|
|
||||||
use winit::event::{ElementState, MouseScrollDelta};
|
|
||||||
|
|
||||||
use crate::{ecs::{SimpleSystem, EventQueue}, input_event::InputEvent, plugin::Plugin,};
|
|
||||||
|
|
||||||
pub type KeyCode = winit::event::VirtualKeyCode;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum ButtonEvent<T: Clone + Hash + Eq + PartialEq + 'static> {
|
|
||||||
Pressed(T),
|
|
||||||
Released(T),
|
|
||||||
JustPressed(T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + Hash + Eq + PartialEq + 'static> ButtonEvent<T> {
|
|
||||||
fn take_val(self) -> T {
|
|
||||||
match self {
|
|
||||||
ButtonEvent::Pressed(t) => t,
|
|
||||||
ButtonEvent::JustPressed(t) => t,
|
|
||||||
ButtonEvent::Released(t) => t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_val(&self) -> T {
|
|
||||||
self.clone().take_val()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct MouseMotion {
|
|
||||||
pub delta: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct MouseWheel {
|
|
||||||
pub delta: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct MouseExact {
|
|
||||||
pub pos: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
|
||||||
pub enum TouchPhase {
|
|
||||||
Started,
|
|
||||||
Moved,
|
|
||||||
Ended,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<winit::event::TouchPhase> for TouchPhase {
|
|
||||||
fn from(value: winit::event::TouchPhase) -> Self {
|
|
||||||
match value {
|
|
||||||
winit::event::TouchPhase::Started => TouchPhase::Started,
|
|
||||||
winit::event::TouchPhase::Moved => TouchPhase::Moved,
|
|
||||||
winit::event::TouchPhase::Ended => TouchPhase::Ended,
|
|
||||||
winit::event::TouchPhase::Cancelled => TouchPhase::Cancelled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translated `Force` from `winit` crate
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Force {
|
|
||||||
Calibrated {
|
|
||||||
force: f64,
|
|
||||||
max_possible_force: f64,
|
|
||||||
altitude_angle: Option<f64>,
|
|
||||||
},
|
|
||||||
Normalized(f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<winit::event::Force> for Force {
|
|
||||||
fn from(value: winit::event::Force) -> Self {
|
|
||||||
match value {
|
|
||||||
winit::event::Force::Calibrated { force, max_possible_force, altitude_angle } => Self::Calibrated { force, max_possible_force, altitude_angle },
|
|
||||||
winit::event::Force::Normalized(v) => Self::Normalized(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translated `WindowEvent::Touch` from `winit` crate
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Touch {
|
|
||||||
pub phase: TouchPhase,
|
|
||||||
pub location: Vec2,
|
|
||||||
pub force: Option<Force>,
|
|
||||||
pub finger_id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Touches {
|
|
||||||
pub touches: Vec<Touch>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Touches {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Touches {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
touches: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A mouse button event
|
|
||||||
///
|
|
||||||
/// Translated `WindowEvent::MouseButton` from `winit` crate
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
|
||||||
pub enum MouseButton {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Middle,
|
|
||||||
Other(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum MouseScrollUnit {
|
|
||||||
Line(Vec2),
|
|
||||||
Pixel(Vec2)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct MouseScroll {
|
|
||||||
pub unit: MouseScrollUnit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct CursorEnteredWindow;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct CursorLeftWindow;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct InputButtons<T: Clone + Hash + Eq + PartialEq + 'static> {
|
|
||||||
// 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<u64, ButtonEvent<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + Hash + Eq + PartialEq + 'static> Default for InputButtons<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + Hash + Eq + PartialEq + 'static> InputButtons<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
button_events: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_button_hash(button: &T) -> u64 {
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
button.hash(&mut hasher);
|
|
||||||
hasher.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a button event to this input type
|
|
||||||
pub fn add_input(&mut self, button: ButtonEvent<T>) {
|
|
||||||
let hash = Self::get_button_hash(&button.clone_val());
|
|
||||||
|
|
||||||
self.button_events.insert(hash, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_input_from_winit(&mut self, button: T, state: ElementState) {
|
|
||||||
let event = match state {
|
|
||||||
ElementState::Pressed if self.is_pressed(button.clone()) => ButtonEvent::Pressed(button),
|
|
||||||
ElementState::Pressed => ButtonEvent::JustPressed(button),
|
|
||||||
ElementState::Released => ButtonEvent::Released(button),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.add_input(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the button is pressed (or was just pressed)
|
|
||||||
pub fn is_pressed(&self, button: T) -> bool
|
|
||||||
{
|
|
||||||
let hash = Self::get_button_hash(&button);
|
|
||||||
match self.button_events.get(&hash) {
|
|
||||||
Some(button_event) => match button_event {
|
|
||||||
// this if statement should always be true, but just in case ;)
|
|
||||||
ButtonEvent::Pressed(b) | ButtonEvent::JustPressed(b) if button == *b => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
None => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the button was just pressed this tick
|
|
||||||
pub fn was_just_pressed(&self, button: T) -> bool {
|
|
||||||
let hash = Self::get_button_hash(&button);
|
|
||||||
match self.button_events.get(&hash) {
|
|
||||||
Some(button_event) => match button_event {
|
|
||||||
// this if statement should always be true, but just in case ;)
|
|
||||||
ButtonEvent::JustPressed(b) if button == *b => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
None => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait InputStorage {
|
|
||||||
fn update_just_pressed(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + Hash + Eq + PartialEq + 'static> InputStorage for InputButtons<T> {
|
|
||||||
fn update_just_pressed(&mut self) {
|
|
||||||
self.button_events.retain(|_hash, button| {
|
|
||||||
match button {
|
|
||||||
// remove released, no need to keep those around.
|
|
||||||
ButtonEvent::Released(_) => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
ButtonEvent::JustPressed(b) => {
|
|
||||||
*button = ButtonEvent::Pressed(b.clone());
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InputSystem;
|
|
||||||
|
|
||||||
impl InputSystem {
|
|
||||||
pub fn update(&mut self, event: &InputEvent, world: &mut edict::World) -> bool {
|
|
||||||
let event_queue = world.get_resource_mut::<EventQueue>();
|
|
||||||
if event_queue.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let mut event_queue = event_queue.unwrap();
|
|
||||||
|
|
||||||
match event {
|
|
||||||
InputEvent::KeyboardInput { input, .. } => {
|
|
||||||
if let Some(code) = input.virtual_keycode {
|
|
||||||
drop(event_queue);
|
|
||||||
let e = world.with_resource(InputButtons::<KeyCode>::new);
|
|
||||||
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
|
|
||||||
e.add_input_from_winit(code, input.state);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
InputEvent::MouseMotion { delta, .. } => {
|
|
||||||
let delta = MouseMotion {
|
|
||||||
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
event_queue.trigger_event(delta);
|
|
||||||
},
|
|
||||||
InputEvent::CursorMoved { position, .. } => {
|
|
||||||
let exact = MouseExact {
|
|
||||||
pos: Vec2::new(position.x as f32, position.y as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
event_queue.trigger_event(exact);
|
|
||||||
},
|
|
||||||
InputEvent::CursorEntered { .. } => {
|
|
||||||
let event = CursorEnteredWindow {};
|
|
||||||
event_queue.trigger_event(event);
|
|
||||||
},
|
|
||||||
InputEvent::CursorLeft { .. } => {
|
|
||||||
let event = CursorLeftWindow {};
|
|
||||||
event_queue.trigger_event(event);
|
|
||||||
},
|
|
||||||
InputEvent::MouseWheel { delta, .. } => {
|
|
||||||
let event = match delta {
|
|
||||||
MouseScrollDelta::LineDelta(x, y) => MouseScroll {
|
|
||||||
unit: MouseScrollUnit::Line(Vec2::new(*x, *y)),
|
|
||||||
},
|
|
||||||
MouseScrollDelta::PixelDelta(delta) => MouseScroll {
|
|
||||||
unit: MouseScrollUnit::Pixel(Vec2::new(delta.x as f32, delta.y as f32)),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
event_queue.trigger_event(event);
|
|
||||||
}, //MouseButton
|
|
||||||
InputEvent::MouseInput { button, state, .. } => {
|
|
||||||
let button_event = match button {
|
|
||||||
winit::event::MouseButton::Left => MouseButton::Left,
|
|
||||||
winit::event::MouseButton::Right => MouseButton::Right,
|
|
||||||
winit::event::MouseButton::Middle => MouseButton::Middle,
|
|
||||||
winit::event::MouseButton::Other(v) => MouseButton::Other(*v),
|
|
||||||
};
|
|
||||||
|
|
||||||
event_queue.trigger_event(button_event);
|
|
||||||
drop(event_queue);
|
|
||||||
|
|
||||||
let e = world.with_resource(InputButtons::<MouseButton>::new);
|
|
||||||
e.add_input_from_winit(button_event, *state);
|
|
||||||
},
|
|
||||||
InputEvent::Touch(t) => {
|
|
||||||
drop(event_queue);
|
|
||||||
|
|
||||||
let touch = Touch {
|
|
||||||
phase: TouchPhase::from(t.phase),
|
|
||||||
location: Vec2::new(t.location.x as f32, t.location.y as f32),
|
|
||||||
force: t.force.map(Force::from),
|
|
||||||
finger_id: t.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let touches = world.with_resource(Touches::new);
|
|
||||||
touches.touches.push(touch);
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimpleSystem for InputSystem {
|
|
||||||
fn execute_mut(&mut self, world: &mut edict::World) -> anyhow::Result<()> {
|
|
||||||
let queue = world.get_resource_mut::<EventQueue>()
|
|
||||||
.and_then(|q| q.read_events::<InputEvent>());
|
|
||||||
|
|
||||||
if queue.is_none() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut events = queue.unwrap();
|
|
||||||
|
|
||||||
while let Some(event) = events.pop_front() {
|
|
||||||
self.update(&event, world);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Plugin that runs InputSystem
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InputPlugin;
|
|
||||||
|
|
||||||
impl Plugin for InputPlugin {
|
|
||||||
fn setup(&self, game: &mut crate::game::Game) {
|
|
||||||
game.with_system("input", InputSystem, &[]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,429 @@
|
||||||
|
use std::{collections::HashMap, cell::RefCell, borrow::BorrowMut, ops::Deref};
|
||||||
|
|
||||||
|
use edict::{action, World};
|
||||||
|
|
||||||
|
use crate::{castable_any::CastableAny, plugin::Plugin};
|
||||||
|
|
||||||
|
use super::{Button, KeyCode, InputButtons};
|
||||||
|
|
||||||
|
#[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, PartialEq)]
|
||||||
|
pub enum ActionState {
|
||||||
|
/// No input is being given by the user for the button/axis.
|
||||||
|
Idle,
|
||||||
|
/// The button is pressed
|
||||||
|
Pressed(f32),
|
||||||
|
/// The button was just pressed
|
||||||
|
JustPressed(f32),
|
||||||
|
/// The button was just released
|
||||||
|
JustReleased,
|
||||||
|
|
||||||
|
//Released,
|
||||||
|
/// The axis moved
|
||||||
|
Axis(f32),
|
||||||
|
/// Some other state with a modifier
|
||||||
|
Other(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub enum ActionKind {
|
||||||
|
Button,
|
||||||
|
Axis
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Action {
|
||||||
|
pub kind: ActionKind,
|
||||||
|
pub state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Action {
|
||||||
|
pub fn new(kind: ActionKind) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
state: ActionState::Idle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct LayoutId(u32);
|
||||||
|
|
||||||
|
impl From<u32> 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<ActionMappingId, ActionMapping>,
|
||||||
|
active_mapping: ActionMappingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
mappings: HashMap::new(),
|
||||||
|
active_mapping: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u32> 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<String, Vec<Binding>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ActionHandler {
|
||||||
|
pub actions: HashMap<String, Action>,
|
||||||
|
pub layouts: HashMap<LayoutId, Layout>,
|
||||||
|
pub current_layout: LayoutId,
|
||||||
|
pub current_mapping: ActionMappingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionHandler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
actions: HashMap::new(),
|
||||||
|
layouts: HashMap::new(),
|
||||||
|
current_layout: LayoutId::default(),
|
||||||
|
current_mapping: ActionMappingId::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_layout(mut self, id: LayoutId) -> Self {
|
||||||
|
self.layouts.insert(id, Layout::new());
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_action(mut self, label: &str, action: Action) -> 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) -> Self {
|
||||||
|
let layout = self.layouts.get_mut(&mapping.layout).unwrap();
|
||||||
|
layout.add_mapping(mapping);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the action is pressed (or was just pressed).
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn is_action_pressed(&self, action_name: &str) -> bool {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::Pressed(_) | ActionState::JustPressed(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the action was just pressed.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn was_action_just_pressed(&self, action_name: &str) -> bool {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::JustPressed(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the action was just released.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn was_action_just_released(&self, action_name: &str) -> bool {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::JustReleased => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an action's state.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn get_action_state(&self, action_name: &str) -> ActionState {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
action.state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the action's modifier if it is pressed (or was just pressed).
|
||||||
|
/// Returns `None` if the action's state is not `ActionState::Pressed` or `ActionState::JustPressed`.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn get_pressed_modifier(&self, action_name: &str) -> Option<f32> {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::Pressed(v) | ActionState::JustPressed(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the action's modifier if it was just pressed.
|
||||||
|
/// Returns `None` if the action's state is not `ActionState::JustPressed`.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn get_just_pressed_modifier(&self, action_name: &str) -> Option<f32> {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::JustPressed(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the action's modifier if its an updated axis.
|
||||||
|
/// Returns `None` if the action's state is not `ActionState::Axis`.
|
||||||
|
///
|
||||||
|
/// This will panic if the action name does not correspond to an action.
|
||||||
|
pub fn get_axis_modifier(&self, action_name: &str) -> Option<f32> {
|
||||||
|
let action = self.actions.get(action_name)
|
||||||
|
.expect(&format!("Action {action_name} was not found"));
|
||||||
|
|
||||||
|
match action.state {
|
||||||
|
ActionState::Axis(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
||||||
|
let keys = world.get_resource::<InputButtons<KeyCode>>()
|
||||||
|
.map(|r| r.deref().clone());
|
||||||
|
|
||||||
|
if let Some(keys) = keys {
|
||||||
|
let mut handler = world.get_resource_mut::<ActionHandler>()
|
||||||
|
.expect("No Input Action handler was created in the world!");
|
||||||
|
|
||||||
|
//handler
|
||||||
|
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
|
||||||
|
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
|
||||||
|
|
||||||
|
for (action_name, binds) in mapping.action_binds.clone().iter() { // TODO: dont clone
|
||||||
|
let mut new_state = None;
|
||||||
|
|
||||||
|
for bind in binds.iter() {
|
||||||
|
match bind.source {
|
||||||
|
ActionSource::Keyboard(key) => {
|
||||||
|
// JustPressed needs to be first, since is_pressed includes buttons that
|
||||||
|
// were just pressed.
|
||||||
|
if keys.was_just_pressed(key) {
|
||||||
|
new_state = Some(ActionState::JustPressed(bind.modifier));
|
||||||
|
} else if keys.is_pressed(key) {
|
||||||
|
new_state = Some(ActionState::Pressed(bind.modifier));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ActionSource::Gamepad(_, _) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!");
|
||||||
|
|
||||||
|
if let Some(new_state) = new_state {
|
||||||
|
action.state = new_state;
|
||||||
|
} else {
|
||||||
|
match action.state {
|
||||||
|
ActionState::Idle => {},
|
||||||
|
//ActionState::Pressed(_) => todo!(),
|
||||||
|
//ActionState::JustPressed(_) => todo!(),
|
||||||
|
ActionState::JustReleased => action.state = ActionState::Idle,
|
||||||
|
//ActionState::Released => action.state = ActionState::I,
|
||||||
|
_ => action.state = ActionState::JustReleased,
|
||||||
|
}
|
||||||
|
//action.state = ActionState::JustReleased;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub struct InputActionPlugin;
|
||||||
|
|
||||||
|
impl Plugin for InputActionPlugin {
|
||||||
|
fn setup(&self, game: &mut crate::game::Game) {
|
||||||
|
game.with_system("input_actions", actions_system, &[]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
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<T: Clone + Hash + Eq + PartialEq + 'static> Button for T {} */
|
||||||
|
|
||||||
|
pub trait Button = Clone + Hash + Eq + PartialEq + 'static;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum ButtonEvent<T: Button> {
|
||||||
|
Pressed(T),
|
||||||
|
Released(T),
|
||||||
|
JustPressed(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Button> ButtonEvent<T> {
|
||||||
|
fn take_val(self) -> T {
|
||||||
|
match self {
|
||||||
|
ButtonEvent::Pressed(t) => t,
|
||||||
|
ButtonEvent::JustPressed(t) => t,
|
||||||
|
ButtonEvent::Released(t) => t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_val(&self) -> T {
|
||||||
|
self.clone().take_val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InputButtons<T: Button> {
|
||||||
|
// 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<u64, ButtonEvent<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Button> Default for InputButtons<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Button> InputButtons<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
button_events: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_button_hash(button: &T) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
button.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a button event to this input type
|
||||||
|
pub fn add_input(&mut self, button: ButtonEvent<T>) {
|
||||||
|
let hash = Self::get_button_hash(&button.clone_val());
|
||||||
|
|
||||||
|
self.button_events.insert(hash, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_input_from_winit(&mut self, button: T, state: ElementState) {
|
||||||
|
let event = match state {
|
||||||
|
// TODO: there is a pause between pressed events of a key when holding down, so it stays in a just pressed state for too long
|
||||||
|
// When a button is held down, this would change a just pressed button into a pressed button.
|
||||||
|
ElementState::Pressed if self.is_pressed(button.clone()) => ButtonEvent::Pressed(button),
|
||||||
|
ElementState::Pressed => ButtonEvent::JustPressed(button),
|
||||||
|
ElementState::Released => ButtonEvent::Released(button),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.add_input(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the button is pressed (or was just pressed)
|
||||||
|
pub fn is_pressed(&self, button: T) -> bool
|
||||||
|
{
|
||||||
|
let hash = Self::get_button_hash(&button);
|
||||||
|
match self.button_events.get(&hash) {
|
||||||
|
Some(button_event) => match button_event {
|
||||||
|
// this if statement should always be true, but just in case ;)
|
||||||
|
ButtonEvent::Pressed(b) | ButtonEvent::JustPressed(b) if button == *b => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the button was just pressed this tick
|
||||||
|
pub fn was_just_pressed(&self, button: T) -> bool {
|
||||||
|
let hash = Self::get_button_hash(&button);
|
||||||
|
match self.button_events.get(&hash) {
|
||||||
|
Some(button_event) => match button_event {
|
||||||
|
// this if statement should always be true, but just in case ;)
|
||||||
|
ButtonEvent::JustPressed(b) if button == *b => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct MouseMotion {
|
||||||
|
pub delta: glam::Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct MouseWheel {
|
||||||
|
pub delta: glam::Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct MouseExact {
|
||||||
|
pub pos: glam::Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
|
||||||
|
pub enum TouchPhase {
|
||||||
|
Started,
|
||||||
|
Moved,
|
||||||
|
Ended,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<winit::event::TouchPhase> for TouchPhase {
|
||||||
|
fn from(value: winit::event::TouchPhase) -> Self {
|
||||||
|
match value {
|
||||||
|
winit::event::TouchPhase::Started => TouchPhase::Started,
|
||||||
|
winit::event::TouchPhase::Moved => TouchPhase::Moved,
|
||||||
|
winit::event::TouchPhase::Ended => TouchPhase::Ended,
|
||||||
|
winit::event::TouchPhase::Cancelled => TouchPhase::Cancelled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translated `Force` from `winit` crate
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Force {
|
||||||
|
Calibrated {
|
||||||
|
force: f64,
|
||||||
|
max_possible_force: f64,
|
||||||
|
altitude_angle: Option<f64>,
|
||||||
|
},
|
||||||
|
Normalized(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<winit::event::Force> for Force {
|
||||||
|
fn from(value: winit::event::Force) -> Self {
|
||||||
|
match value {
|
||||||
|
winit::event::Force::Calibrated { force, max_possible_force, altitude_angle } => Self::Calibrated { force, max_possible_force, altitude_angle },
|
||||||
|
winit::event::Force::Normalized(v) => Self::Normalized(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translated `WindowEvent::Touch` from `winit` crate
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Touch {
|
||||||
|
pub phase: TouchPhase,
|
||||||
|
pub location: glam::Vec2,
|
||||||
|
pub force: Option<Force>,
|
||||||
|
pub finger_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Touches {
|
||||||
|
pub touches: Vec<Touch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Touches {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Touches {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
touches: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mouse button event
|
||||||
|
///
|
||||||
|
/// Translated `WindowEvent::MouseButton` from `winit` crate
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||||
|
pub enum MouseButton {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Middle,
|
||||||
|
Other(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum MouseScrollUnit {
|
||||||
|
Line(glam::Vec2),
|
||||||
|
Pixel(glam::Vec2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct MouseScroll {
|
||||||
|
pub unit: MouseScrollUnit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct CursorEnteredWindow;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct CursorLeftWindow;
|
|
@ -0,0 +1,14 @@
|
||||||
|
pub mod system;
|
||||||
|
pub use system::*;
|
||||||
|
|
||||||
|
pub mod input_event;
|
||||||
|
pub use input_event::*;
|
||||||
|
|
||||||
|
pub mod events;
|
||||||
|
pub use events::*;
|
||||||
|
|
||||||
|
pub mod buttons;
|
||||||
|
pub use buttons::*;
|
||||||
|
|
||||||
|
pub mod action;
|
||||||
|
pub use action::*;
|
|
@ -0,0 +1,125 @@
|
||||||
|
use glam::Vec2;
|
||||||
|
use winit::event::MouseScrollDelta;
|
||||||
|
|
||||||
|
use crate::{ecs::{SimpleSystem, EventQueue}, plugin::Plugin,};
|
||||||
|
|
||||||
|
use super::{events::*, InputButtons, InputEvent};
|
||||||
|
|
||||||
|
pub type KeyCode = winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputSystem;
|
||||||
|
|
||||||
|
impl InputSystem {
|
||||||
|
pub fn process_event(&mut self, world: &mut edict::World, event: &InputEvent) -> bool {
|
||||||
|
let event_queue = world.get_resource_mut::<EventQueue>();
|
||||||
|
if event_queue.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut event_queue = event_queue.unwrap();
|
||||||
|
|
||||||
|
match event {
|
||||||
|
InputEvent::KeyboardInput { input, .. } => {
|
||||||
|
if let Some(code) = input.virtual_keycode {
|
||||||
|
drop(event_queue);
|
||||||
|
let e = world.with_resource(InputButtons::<winit::event::VirtualKeyCode>::new);
|
||||||
|
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
|
||||||
|
e.add_input_from_winit(code, input.state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InputEvent::MouseMotion { delta, .. } => {
|
||||||
|
let delta = MouseMotion {
|
||||||
|
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
|
||||||
|
};
|
||||||
|
|
||||||
|
event_queue.trigger_event(delta);
|
||||||
|
},
|
||||||
|
InputEvent::CursorMoved { position, .. } => {
|
||||||
|
let exact = MouseExact {
|
||||||
|
pos: Vec2::new(position.x as f32, position.y as f32)
|
||||||
|
};
|
||||||
|
|
||||||
|
event_queue.trigger_event(exact);
|
||||||
|
},
|
||||||
|
InputEvent::CursorEntered { .. } => {
|
||||||
|
let event = CursorEnteredWindow {};
|
||||||
|
event_queue.trigger_event(event);
|
||||||
|
},
|
||||||
|
InputEvent::CursorLeft { .. } => {
|
||||||
|
let event = CursorLeftWindow {};
|
||||||
|
event_queue.trigger_event(event);
|
||||||
|
},
|
||||||
|
InputEvent::MouseWheel { delta, .. } => {
|
||||||
|
let event = match delta {
|
||||||
|
MouseScrollDelta::LineDelta(x, y) => MouseScroll {
|
||||||
|
unit: MouseScrollUnit::Line(Vec2::new(*x, *y)),
|
||||||
|
},
|
||||||
|
MouseScrollDelta::PixelDelta(delta) => MouseScroll {
|
||||||
|
unit: MouseScrollUnit::Pixel(Vec2::new(delta.x as f32, delta.y as f32)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
event_queue.trigger_event(event);
|
||||||
|
}, //MouseButton
|
||||||
|
InputEvent::MouseInput { button, state, .. } => {
|
||||||
|
let button_event = match button {
|
||||||
|
winit::event::MouseButton::Left => MouseButton::Left,
|
||||||
|
winit::event::MouseButton::Right => MouseButton::Right,
|
||||||
|
winit::event::MouseButton::Middle => MouseButton::Middle,
|
||||||
|
winit::event::MouseButton::Other(v) => MouseButton::Other(*v),
|
||||||
|
};
|
||||||
|
|
||||||
|
event_queue.trigger_event(button_event);
|
||||||
|
drop(event_queue);
|
||||||
|
|
||||||
|
let e = world.with_resource(InputButtons::<MouseButton>::new);
|
||||||
|
e.add_input_from_winit(button_event, *state);
|
||||||
|
},
|
||||||
|
InputEvent::Touch(t) => {
|
||||||
|
drop(event_queue);
|
||||||
|
|
||||||
|
let touch = Touch {
|
||||||
|
phase: TouchPhase::from(t.phase),
|
||||||
|
location: Vec2::new(t.location.x as f32, t.location.y as f32),
|
||||||
|
force: t.force.map(Force::from),
|
||||||
|
finger_id: t.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let touches = world.with_resource(Touches::new);
|
||||||
|
touches.touches.push(touch);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleSystem for InputSystem {
|
||||||
|
fn execute_mut(&mut self, world: &mut edict::World) -> anyhow::Result<()> {
|
||||||
|
let queue = world.get_resource_mut::<EventQueue>()
|
||||||
|
.and_then(|q| q.read_events::<InputEvent>());
|
||||||
|
|
||||||
|
if queue.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut events = queue.unwrap();
|
||||||
|
|
||||||
|
while let Some(event) = events.pop_front() {
|
||||||
|
self.process_event(world, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plugin that runs InputSystem
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputPlugin;
|
||||||
|
|
||||||
|
impl Plugin for InputPlugin {
|
||||||
|
fn setup(&self, game: &mut crate::game::Game) {
|
||||||
|
game.with_system("input", InputSystem, &[]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
#![feature(hash_extract_if)]
|
#![feature(hash_extract_if)]
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
|
#![feature(trait_alias)]
|
||||||
|
|
||||||
pub mod game;
|
pub mod game;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod input_event;
|
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod ecs;
|
pub mod ecs;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use winit::{window::{Window, Fullscreen}, dpi::{LogicalPosition, LogicalSize, Ph
|
||||||
|
|
||||||
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
|
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
|
||||||
|
|
||||||
use crate::{plugin::Plugin, change_tracker::Ct, ecs::EventQueue, input_event::InputEvent};
|
use crate::{plugin::Plugin, change_tracker::Ct, ecs::EventQueue, input::InputEvent};
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub enum WindowMode {
|
pub enum WindowMode {
|
||||||
|
|
Loading…
Reference in New Issue