use std::collections::HashSet;
use bevy_ecs::prelude::*;
use glam::Vec2;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyCode {
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
Key0, Key1, Key2, Key3, Key4,
Key5, Key6, Key7, Key8, Key9,
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
Space, Enter, Escape, Tab, Backspace, Delete,
Left, Right, Up, Down,
LShift, RShift, LControl, RControl, LAlt, RAlt,
}
impl KeyCode {
pub fn from_winit(key: winit::keyboard::KeyCode) -> Option<KeyCode> {
use winit::keyboard::KeyCode as WK;
match key {
WK::KeyA => Some(KeyCode::A), WK::KeyB => Some(KeyCode::B),
WK::KeyC => Some(KeyCode::C), WK::KeyD => Some(KeyCode::D),
WK::KeyE => Some(KeyCode::E), WK::KeyF => Some(KeyCode::F),
WK::KeyG => Some(KeyCode::G), WK::KeyH => Some(KeyCode::H),
WK::KeyI => Some(KeyCode::I), WK::KeyJ => Some(KeyCode::J),
WK::KeyK => Some(KeyCode::K), WK::KeyL => Some(KeyCode::L),
WK::KeyM => Some(KeyCode::M), WK::KeyN => Some(KeyCode::N),
WK::KeyO => Some(KeyCode::O), WK::KeyP => Some(KeyCode::P),
WK::KeyQ => Some(KeyCode::Q), WK::KeyR => Some(KeyCode::R),
WK::KeyS => Some(KeyCode::S), WK::KeyT => Some(KeyCode::T),
WK::KeyU => Some(KeyCode::U), WK::KeyV => Some(KeyCode::V),
WK::KeyW => Some(KeyCode::W), WK::KeyX => Some(KeyCode::X),
WK::KeyY => Some(KeyCode::Y), WK::KeyZ => Some(KeyCode::Z),
WK::Digit0 => Some(KeyCode::Key0), WK::Digit1 => Some(KeyCode::Key1),
WK::Digit2 => Some(KeyCode::Key2), WK::Digit3 => Some(KeyCode::Key3),
WK::Digit4 => Some(KeyCode::Key4), WK::Digit5 => Some(KeyCode::Key5),
WK::Digit6 => Some(KeyCode::Key6), WK::Digit7 => Some(KeyCode::Key7),
WK::Digit8 => Some(KeyCode::Key8), WK::Digit9 => Some(KeyCode::Key9),
WK::F1 => Some(KeyCode::F1), WK::F2 => Some(KeyCode::F2),
WK::F3 => Some(KeyCode::F3), WK::F4 => Some(KeyCode::F4),
WK::F5 => Some(KeyCode::F5), WK::F6 => Some(KeyCode::F6),
WK::F7 => Some(KeyCode::F7), WK::F8 => Some(KeyCode::F8),
WK::F9 => Some(KeyCode::F9), WK::F10 => Some(KeyCode::F10),
WK::F11 => Some(KeyCode::F11), WK::F12 => Some(KeyCode::F12),
WK::Space => Some(KeyCode::Space), WK::Enter => Some(KeyCode::Enter),
WK::Escape => Some(KeyCode::Escape), WK::Tab => Some(KeyCode::Tab),
WK::Backspace => Some(KeyCode::Backspace), WK::Delete => Some(KeyCode::Delete),
WK::ArrowLeft => Some(KeyCode::Left), WK::ArrowRight => Some(KeyCode::Right),
WK::ArrowUp => Some(KeyCode::Up), WK::ArrowDown => Some(KeyCode::Down),
WK::ShiftLeft => Some(KeyCode::LShift), WK::ShiftRight => Some(KeyCode::RShift),
WK::ControlLeft => Some(KeyCode::LControl), WK::ControlRight => Some(KeyCode::RControl),
WK::AltLeft => Some(KeyCode::LAlt), WK::AltRight => Some(KeyCode::RAlt),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseButton {
Left,
Right,
Middle,
}
impl MouseButton {
pub fn from_winit(button: winit::event::MouseButton) -> Option<MouseButton> {
match button {
winit::event::MouseButton::Left => Some(MouseButton::Left),
winit::event::MouseButton::Right => Some(MouseButton::Right),
winit::event::MouseButton::Middle => Some(MouseButton::Middle),
_ => None,
}
}
}
#[derive(Resource)]
pub struct InputState {
keys_pressed: HashSet<KeyCode>,
keys_just_pressed: HashSet<KeyCode>,
keys_just_released: HashSet<KeyCode>,
mouse_pressed: HashSet<MouseButton>,
mouse_just_pressed: HashSet<MouseButton>,
mouse_just_released: HashSet<MouseButton>,
mouse_position: Vec2,
mouse_delta: Vec2,
scroll_delta: f32,
}
impl InputState {
pub fn new() -> Self {
Self {
keys_pressed: HashSet::new(),
keys_just_pressed: HashSet::new(),
keys_just_released: HashSet::new(),
mouse_pressed: HashSet::new(),
mouse_just_pressed: HashSet::new(),
mouse_just_released: HashSet::new(),
mouse_position: Vec2::ZERO,
mouse_delta: Vec2::ZERO,
scroll_delta: 0.0,
}
}
pub fn press_key(&mut self, key: KeyCode) {
if self.keys_pressed.insert(key) {
self.keys_just_pressed.insert(key);
}
}
pub fn release_key(&mut self, key: KeyCode) {
if self.keys_pressed.remove(&key) {
self.keys_just_released.insert(key);
}
}
pub fn is_key_pressed(&self, key: KeyCode) -> bool {
self.keys_pressed.contains(&key)
}
pub fn is_key_just_pressed(&self, key: KeyCode) -> bool {
self.keys_just_pressed.contains(&key)
}
pub fn is_key_just_released(&self, key: KeyCode) -> bool {
self.keys_just_released.contains(&key)
}
pub fn press_mouse(&mut self, button: MouseButton) {
if self.mouse_pressed.insert(button) {
self.mouse_just_pressed.insert(button);
}
}
pub fn release_mouse(&mut self, button: MouseButton) {
if self.mouse_pressed.remove(&button) {
self.mouse_just_released.insert(button);
}
}
pub fn is_mouse_pressed(&self, button: MouseButton) -> bool {
self.mouse_pressed.contains(&button)
}
pub fn is_mouse_just_pressed(&self, button: MouseButton) -> bool {
self.mouse_just_pressed.contains(&button)
}
pub fn is_mouse_just_released(&self, button: MouseButton) -> bool {
self.mouse_just_released.contains(&button)
}
pub fn set_mouse_position(&mut self, position: Vec2) {
self.mouse_position = position;
}
pub fn add_mouse_delta(&mut self, delta: Vec2) {
self.mouse_delta += delta;
}
pub fn mouse_position(&self) -> Vec2 {
self.mouse_position
}
pub fn mouse_delta(&self) -> Vec2 {
self.mouse_delta
}
pub fn add_scroll_delta(&mut self, delta: f32) {
self.scroll_delta += delta;
}
pub fn scroll_delta(&self) -> f32 {
self.scroll_delta
}
pub fn end_frame(&mut self) {
self.keys_just_pressed.clear();
self.keys_just_released.clear();
self.mouse_just_pressed.clear();
self.mouse_just_released.clear();
self.mouse_delta = Vec2::ZERO;
self.scroll_delta = 0.0;
}
pub fn pressed_keys(&self) -> &HashSet<KeyCode> {
&self.keys_pressed
}
}
impl Default for InputState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_press_release() {
let mut input = InputState::new();
input.press_key(KeyCode::W);
assert!(input.is_key_pressed(KeyCode::W));
assert!(input.is_key_just_pressed(KeyCode::W));
assert!(!input.is_key_just_released(KeyCode::W));
input.end_frame();
assert!(input.is_key_pressed(KeyCode::W));
assert!(!input.is_key_just_pressed(KeyCode::W));
input.release_key(KeyCode::W);
assert!(!input.is_key_pressed(KeyCode::W));
assert!(input.is_key_just_released(KeyCode::W));
input.end_frame();
assert!(!input.is_key_just_released(KeyCode::W));
}
#[test]
fn test_mouse_buttons() {
let mut input = InputState::new();
input.press_mouse(MouseButton::Left);
assert!(input.is_mouse_pressed(MouseButton::Left));
assert!(input.is_mouse_just_pressed(MouseButton::Left));
input.end_frame();
assert!(input.is_mouse_pressed(MouseButton::Left));
assert!(!input.is_mouse_just_pressed(MouseButton::Left));
}
#[test]
fn test_mouse_delta() {
let mut input = InputState::new();
input.add_mouse_delta(Vec2::new(5.0, 3.0));
input.add_mouse_delta(Vec2::new(2.0, -1.0));
assert_eq!(input.mouse_delta(), Vec2::new(7.0, 2.0));
input.end_frame();
assert_eq!(input.mouse_delta(), Vec2::ZERO);
}
#[test]
fn test_scroll_delta() {
let mut input = InputState::new();
input.add_scroll_delta(1.5);
input.add_scroll_delta(-0.5);
assert_eq!(input.scroll_delta(), 1.0);
input.end_frame();
assert_eq!(input.scroll_delta(), 0.0);
}
#[test]
fn test_duplicate_press() {
let mut input = InputState::new();
input.press_key(KeyCode::A);
input.press_key(KeyCode::A); assert_eq!(input.pressed_keys().len(), 1);
}
}