use bevy::prelude::*;
#[derive(Debug, Clone, Reflect)]
pub struct VirtualAxis {
pub negative: VirtualButton,
pub positive: VirtualButton,
}
impl VirtualAxis {
#[must_use]
pub const fn new(negative: VirtualButton, positive: VirtualButton) -> Self {
Self { negative, positive }
}
#[must_use]
pub const fn from_keys(negative: KeyCode, positive: KeyCode) -> Self {
Self {
negative: VirtualButton::Key(negative),
positive: VirtualButton::Key(positive),
}
}
#[must_use]
pub const fn from_gamepad(negative: GamepadButton, positive: GamepadButton) -> Self {
Self {
negative: VirtualButton::Gamepad(negative),
positive: VirtualButton::Gamepad(positive),
}
}
#[must_use]
pub const fn horizontal_arrow_keys() -> Self {
Self::from_keys(KeyCode::ArrowLeft, KeyCode::ArrowRight)
}
#[must_use]
pub const fn vertical_arrow_keys() -> Self {
Self::from_keys(KeyCode::ArrowDown, KeyCode::ArrowUp)
}
#[must_use]
pub const fn ad() -> Self {
Self::from_keys(KeyCode::KeyA, KeyCode::KeyD)
}
#[must_use]
pub const fn ws() -> Self {
Self::from_keys(KeyCode::KeyS, KeyCode::KeyW)
}
#[must_use]
pub const fn horizontal_dpad() -> Self {
Self::from_gamepad(GamepadButton::DPadLeft, GamepadButton::DPadRight)
}
#[must_use]
pub const fn vertical_dpad() -> Self {
Self::from_gamepad(GamepadButton::DPadDown, GamepadButton::DPadUp)
}
#[must_use]
pub const fn horizontal_face_buttons() -> Self {
Self::from_gamepad(GamepadButton::West, GamepadButton::East)
}
#[must_use]
pub const fn vertical_face_buttons() -> Self {
Self::from_gamepad(GamepadButton::South, GamepadButton::North)
}
#[must_use]
pub fn value(&self, keyboard: &ButtonInput<KeyCode>, gamepads: &Query<&Gamepad>) -> f32 {
let neg = self.negative.is_pressed(keyboard, gamepads);
let pos = self.positive.is_pressed(keyboard, gamepads);
match (neg, pos) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
}
}
}
#[derive(Debug, Clone, Reflect)]
pub struct VirtualDPad {
pub up: VirtualButton,
pub down: VirtualButton,
pub left: VirtualButton,
pub right: VirtualButton,
}
impl VirtualDPad {
#[must_use]
pub const fn new(
up: VirtualButton,
down: VirtualButton,
left: VirtualButton,
right: VirtualButton,
) -> Self {
Self {
up,
down,
left,
right,
}
}
#[must_use]
pub const fn from_keys(up: KeyCode, down: KeyCode, left: KeyCode, right: KeyCode) -> Self {
Self {
up: VirtualButton::Key(up),
down: VirtualButton::Key(down),
left: VirtualButton::Key(left),
right: VirtualButton::Key(right),
}
}
#[must_use]
pub const fn from_gamepad(
up: GamepadButton,
down: GamepadButton,
left: GamepadButton,
right: GamepadButton,
) -> Self {
Self {
up: VirtualButton::Gamepad(up),
down: VirtualButton::Gamepad(down),
left: VirtualButton::Gamepad(left),
right: VirtualButton::Gamepad(right),
}
}
#[must_use]
pub const fn wasd() -> Self {
Self::from_keys(KeyCode::KeyW, KeyCode::KeyS, KeyCode::KeyA, KeyCode::KeyD)
}
#[must_use]
pub const fn arrow_keys() -> Self {
Self::from_keys(
KeyCode::ArrowUp,
KeyCode::ArrowDown,
KeyCode::ArrowLeft,
KeyCode::ArrowRight,
)
}
#[must_use]
pub const fn dpad() -> Self {
Self::from_gamepad(
GamepadButton::DPadUp,
GamepadButton::DPadDown,
GamepadButton::DPadLeft,
GamepadButton::DPadRight,
)
}
#[must_use]
pub const fn face_buttons() -> Self {
Self::from_gamepad(
GamepadButton::North,
GamepadButton::South,
GamepadButton::West,
GamepadButton::East,
)
}
#[must_use]
pub fn axis_pair(&self, keyboard: &ButtonInput<KeyCode>, gamepads: &Query<&Gamepad>) -> Vec2 {
let up = self.up.is_pressed(keyboard, gamepads);
let down = self.down.is_pressed(keyboard, gamepads);
let left = self.left.is_pressed(keyboard, gamepads);
let right = self.right.is_pressed(keyboard, gamepads);
let x = match (left, right) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
};
let y = match (down, up) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
};
Vec2::new(x, y)
}
#[must_use]
pub fn axis_pair_normalized(
&self,
keyboard: &ButtonInput<KeyCode>,
gamepads: &Query<&Gamepad>,
) -> Vec2 {
let value = self.axis_pair(keyboard, gamepads);
if value.length_squared() > 0.0 {
value.normalize()
} else {
Vec2::ZERO
}
}
}
#[derive(Debug, Clone, Reflect)]
pub struct VirtualDPad3D {
pub up: VirtualButton,
pub down: VirtualButton,
pub left: VirtualButton,
pub right: VirtualButton,
pub forward: VirtualButton,
pub backward: VirtualButton,
}
impl VirtualDPad3D {
#[must_use]
pub const fn new(
up: VirtualButton,
down: VirtualButton,
left: VirtualButton,
right: VirtualButton,
forward: VirtualButton,
backward: VirtualButton,
) -> Self {
Self {
up,
down,
left,
right,
forward,
backward,
}
}
#[must_use]
pub fn axis_triple(&self, keyboard: &ButtonInput<KeyCode>, gamepads: &Query<&Gamepad>) -> Vec3 {
let up = self.up.is_pressed(keyboard, gamepads);
let down = self.down.is_pressed(keyboard, gamepads);
let left = self.left.is_pressed(keyboard, gamepads);
let right = self.right.is_pressed(keyboard, gamepads);
let forward = self.forward.is_pressed(keyboard, gamepads);
let backward = self.backward.is_pressed(keyboard, gamepads);
let x = match (left, right) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
};
let y = match (down, up) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
};
let z = match (backward, forward) {
(true, false) => -1.0,
(false, true) => 1.0,
_ => 0.0,
};
Vec3::new(x, y, z)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
pub enum VirtualButton {
Key(KeyCode),
Gamepad(GamepadButton),
Mouse(MouseButton),
}
impl VirtualButton {
#[must_use]
pub fn is_pressed(&self, keyboard: &ButtonInput<KeyCode>, gamepads: &Query<&Gamepad>) -> bool {
match self {
Self::Key(key) => keyboard.pressed(*key),
Self::Gamepad(button) => gamepads.iter().any(|gamepad| gamepad.pressed(*button)),
Self::Mouse(_) => false, }
}
#[must_use]
pub fn is_pressed_with_mouse(
&self,
keyboard: &ButtonInput<KeyCode>,
mouse: &ButtonInput<MouseButton>,
gamepads: &Query<&Gamepad>,
) -> bool {
match self {
Self::Key(key) => keyboard.pressed(*key),
Self::Gamepad(button) => gamepads.iter().any(|gamepad| gamepad.pressed(*button)),
Self::Mouse(button) => mouse.pressed(*button),
}
}
}
impl From<KeyCode> for VirtualButton {
fn from(key: KeyCode) -> Self {
Self::Key(key)
}
}
impl From<GamepadButton> for VirtualButton {
fn from(button: GamepadButton) -> Self {
Self::Gamepad(button)
}
}
impl From<MouseButton> for VirtualButton {
fn from(button: MouseButton) -> Self {
Self::Mouse(button)
}
}
pub(crate) fn register_virtual_input_types(app: &mut App) {
app.register_type::<VirtualAxis>()
.register_type::<VirtualDPad>()
.register_type::<VirtualDPad3D>()
.register_type::<VirtualButton>();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_virtual_axis_wasd() {
let axis = VirtualAxis::ad();
assert!(matches!(axis.negative, VirtualButton::Key(KeyCode::KeyA)));
assert!(matches!(axis.positive, VirtualButton::Key(KeyCode::KeyD)));
}
#[test]
fn test_virtual_dpad_wasd() {
let dpad = VirtualDPad::wasd();
assert!(matches!(dpad.up, VirtualButton::Key(KeyCode::KeyW)));
assert!(matches!(dpad.down, VirtualButton::Key(KeyCode::KeyS)));
assert!(matches!(dpad.left, VirtualButton::Key(KeyCode::KeyA)));
assert!(matches!(dpad.right, VirtualButton::Key(KeyCode::KeyD)));
}
#[test]
fn test_virtual_dpad_arrow_keys() {
let dpad = VirtualDPad::arrow_keys();
assert!(matches!(dpad.up, VirtualButton::Key(KeyCode::ArrowUp)));
assert!(matches!(dpad.down, VirtualButton::Key(KeyCode::ArrowDown)));
assert!(matches!(dpad.left, VirtualButton::Key(KeyCode::ArrowLeft)));
assert!(matches!(
dpad.right,
VirtualButton::Key(KeyCode::ArrowRight)
));
}
#[test]
fn test_virtual_dpad_gamepad() {
let dpad = VirtualDPad::dpad();
assert!(matches!(
dpad.up,
VirtualButton::Gamepad(GamepadButton::DPadUp)
));
assert!(matches!(
dpad.down,
VirtualButton::Gamepad(GamepadButton::DPadDown)
));
}
#[test]
fn test_virtual_button_from() {
let key: VirtualButton = KeyCode::Space.into();
assert!(matches!(key, VirtualButton::Key(KeyCode::Space)));
let button: VirtualButton = GamepadButton::South.into();
assert!(matches!(
button,
VirtualButton::Gamepad(GamepadButton::South)
));
let mouse: VirtualButton = MouseButton::Left.into();
assert!(matches!(mouse, VirtualButton::Mouse(MouseButton::Left)));
}
}