Skip to content

Commit

Permalink
feat!: Add gamepad haptics (gamepad rumble)
Browse files Browse the repository at this point in the history
  • Loading branch information
PraxTube committed Nov 5, 2023
1 parent a765399 commit 50237c5
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 132 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ace-of-the-heavens"
version = "0.2.4"
version = "0.2.5"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
157 changes: 44 additions & 113 deletions src/input.rs → src/input/gamepad.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,27 @@
use bevy::{app::AppExit, input::gamepad::*, prelude::*};
use std::time::Duration;

use bevy::{input::gamepad::*, prelude::*};
use bevy_ggrs::*;

use super::*;
use crate::player::Player;

const INPUT_FORWARD: u8 = 1 << 0;
const INPUT_BACKWARD: u8 = 1 << 1;
const INPUT_LEFT: u8 = 1 << 2;
const INPUT_RIGHT: u8 = 1 << 3;
const INPUT_FIRE: u8 = 1 << 4;
const INPUT_DODGE: u8 = 1 << 5;
const INPUT_ROCKET: u8 = 1 << 6;
const INPUT_REMATCH: u8 = 1 << 7;

pub fn input(
In(local_handle): In<ggrs::PlayerHandle>,
keys: Res<Input<KeyCode>>,
mouse_buttons: Res<Input<MouseButton>>,
gamepads: Res<Gamepads>,
button_inputs: Res<Input<GamepadButton>>,
button_axes: Res<Axis<GamepadButton>>,
axes: Res<Axis<GamepadAxis>>,
players: Query<(&Transform, &Player)>,
) -> u8 {
let mut input = 0u8;
#[derive(Resource, Default, Reflect)]
pub struct GamepadRumble {
just_added: bool,
intensity: f32,
duration: f32,
}

if keys.any_pressed([KeyCode::Up, KeyCode::W, KeyCode::K]) {
input |= INPUT_FORWARD;
}
if keys.any_pressed([KeyCode::Down, KeyCode::S, KeyCode::J]) {
input |= INPUT_BACKWARD;
}
if keys.any_pressed([KeyCode::Left, KeyCode::A]) {
input |= INPUT_LEFT;
}
if keys.any_pressed([KeyCode::Right, KeyCode::D, KeyCode::F]) {
input |= INPUT_RIGHT;
}
if keys.any_pressed([KeyCode::Space, KeyCode::Return]) {
input |= INPUT_FIRE;
}
if keys.any_pressed([KeyCode::E, KeyCode::L]) || mouse_buttons.pressed(MouseButton::Right) {
input |= INPUT_DODGE;
}
if keys.any_pressed([KeyCode::Q, KeyCode::Semicolon])
|| mouse_buttons.pressed(MouseButton::Left)
{
input |= INPUT_ROCKET;
}
if keys.pressed(KeyCode::R) {
input |= INPUT_REMATCH;
impl GamepadRumble {
pub fn add_rumble(&mut self, intensity: f32, duration: f32) {
self.just_added = true;
self.intensity = intensity.clamp(0.0, 1.0);
self.duration = duration;
}

let controller_input = get_gamepad_input(
&gamepads,
&button_inputs,
&button_axes,
&axes,
&players,
local_handle,
);
input |= controller_input;
input
}

fn get_gamepad_input(
pub fn get_gamepad_input(
gamepads: &Res<Gamepads>,
button_inputs: &Res<Input<GamepadButton>>,
button_axes: &Res<Axis<GamepadButton>>,
Expand Down Expand Up @@ -146,61 +104,6 @@ fn get_gamepad_input(
input
}

pub fn steer_direction(input: u8) -> f32 {
let mut steer_direction: f32 = 0.0;
if input & INPUT_LEFT != 0 {
steer_direction += 1.0;
}
if input & INPUT_RIGHT != 0 {
steer_direction -= 1.0;
}
steer_direction
}

pub fn accelerate_direction(input: u8) -> f32 {
let mut accelerate_direction: f32 = 0.0;
if input & INPUT_FORWARD != 0 {
accelerate_direction += 1.0;
}
if input & INPUT_BACKWARD != 0 {
accelerate_direction -= 1.0;
}
accelerate_direction
}

pub fn fire(input: u8) -> bool {
input & INPUT_FIRE != 0
}

pub fn dodge(input: u8) -> bool {
input & INPUT_DODGE != 0
}

pub fn rocket(input: u8) -> bool {
input & INPUT_ROCKET != 0
}

pub fn rematch(input: u8) -> bool {
input & INPUT_REMATCH != 0
}

pub fn quit(
mut exit: EventWriter<AppExit>,
keys: Res<Input<KeyCode>>,
gamepads: Res<Gamepads>,
button_inputs: Res<Input<GamepadButton>>,
) {
let mut pressed = keys.pressed(KeyCode::Q);
for gamepad in gamepads.iter() {
if button_inputs.pressed(GamepadButton::new(gamepad, GamepadButtonType::East)) {
pressed = true;
}
}
if pressed {
exit.send(AppExit);
}
}

pub fn configure_gamepads(mut settings: ResMut<GamepadSettings>) {
// add a larger default dead-zone to all axes (ignore small inputs, round to zero)
settings.default_axis_settings.set_deadzone_lowerbound(-0.2);
Expand All @@ -213,3 +116,31 @@ pub fn configure_gamepads(mut settings: ResMut<GamepadSettings>) {

settings.default_button_settings = button_settings;
}

pub fn rumble_gamepads(
gamepads: Res<Gamepads>,
mut rumble_requests: EventWriter<GamepadRumbleRequest>,
mut gamepad_rumble: ResMut<GamepadRumble>,
) {
if !gamepad_rumble.just_added {
return;
}
gamepad_rumble.just_added = false;

for gamepad in gamepads.iter() {
rumble_requests.send(GamepadRumbleRequest::Add {
gamepad,
intensity: GamepadRumbleIntensity {
// intensity low-frequency motor, usually on the left-hand side
strong_motor: gamepad_rumble.intensity,
// intensity of high-frequency motor, usually on the right-hand side
weak_motor: gamepad_rumble.intensity,
},
duration: Duration::from_secs_f32(gamepad_rumble.duration),
});

if gamepad_rumble.duration == 0.0 || gamepad_rumble.intensity == 0.0 {
rumble_requests.send(GamepadRumbleRequest::Stop { gamepad });
}
}
}
146 changes: 146 additions & 0 deletions src/input/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
pub mod gamepad;

pub use gamepad::GamepadRumble;

use bevy::{app::AppExit, input::gamepad::*, prelude::*};
use bevy_ggrs::*;

use crate::{player::Player, GameState, RollbackState};

pub const INPUT_FORWARD: u8 = 1 << 0;
pub const INPUT_BACKWARD: u8 = 1 << 1;
pub const INPUT_LEFT: u8 = 1 << 2;
pub const INPUT_RIGHT: u8 = 1 << 3;
pub const INPUT_FIRE: u8 = 1 << 4;
pub const INPUT_DODGE: u8 = 1 << 5;
pub const INPUT_ROCKET: u8 = 1 << 6;
pub const INPUT_REMATCH: u8 = 1 << 7;

pub fn input(
In(local_handle): In<ggrs::PlayerHandle>,
keys: Res<Input<KeyCode>>,
mouse_buttons: Res<Input<MouseButton>>,
gamepads: Res<Gamepads>,
button_inputs: Res<Input<GamepadButton>>,
button_axes: Res<Axis<GamepadButton>>,
axes: Res<Axis<GamepadAxis>>,
players: Query<(&Transform, &Player)>,
) -> u8 {
let mut input = 0u8;

if keys.any_pressed([KeyCode::Up, KeyCode::W, KeyCode::K]) {
input |= INPUT_FORWARD;
}
if keys.any_pressed([KeyCode::Down, KeyCode::S, KeyCode::J]) {
input |= INPUT_BACKWARD;
}
if keys.any_pressed([KeyCode::Left, KeyCode::A]) {
input |= INPUT_LEFT;
}
if keys.any_pressed([KeyCode::Right, KeyCode::D, KeyCode::F]) {
input |= INPUT_RIGHT;
}
if keys.any_pressed([KeyCode::Space, KeyCode::Return]) {
input |= INPUT_FIRE;
}
if keys.any_pressed([KeyCode::E, KeyCode::L]) || mouse_buttons.pressed(MouseButton::Right) {
input |= INPUT_DODGE;
}
if keys.any_pressed([KeyCode::Q, KeyCode::Semicolon])
|| mouse_buttons.pressed(MouseButton::Left)
{
input |= INPUT_ROCKET;
}
if keys.pressed(KeyCode::R) {
input |= INPUT_REMATCH;
}

let controller_input = gamepad::get_gamepad_input(
&gamepads,
&button_inputs,
&button_axes,
&axes,
&players,
local_handle,
);
input |= controller_input;
input
}

pub fn steer_direction(input: u8) -> f32 {
let mut steer_direction: f32 = 0.0;
if input & INPUT_LEFT != 0 {
steer_direction += 1.0;
}
if input & INPUT_RIGHT != 0 {
steer_direction -= 1.0;
}
steer_direction
}

pub fn accelerate_direction(input: u8) -> f32 {
let mut accelerate_direction: f32 = 0.0;
if input & INPUT_FORWARD != 0 {
accelerate_direction += 1.0;
}
if input & INPUT_BACKWARD != 0 {
accelerate_direction -= 1.0;
}
accelerate_direction
}

pub fn fire(input: u8) -> bool {
input & INPUT_FIRE != 0
}

pub fn dodge(input: u8) -> bool {
input & INPUT_DODGE != 0
}

pub fn rocket(input: u8) -> bool {
input & INPUT_ROCKET != 0
}

pub fn rematch(input: u8) -> bool {
input & INPUT_REMATCH != 0
}

pub fn quit(
mut exit: EventWriter<AppExit>,
keys: Res<Input<KeyCode>>,
gamepads: Res<Gamepads>,
button_inputs: Res<Input<GamepadButton>>,
) {
let mut pressed = keys.pressed(KeyCode::Q);
for gamepad in gamepads.iter() {
if button_inputs.pressed(GamepadButton::new(gamepad, GamepadButtonType::East)) {
pressed = true;
}
}
if pressed {
exit.send(AppExit);
}
}

pub struct AceInputPlugin;

impl Plugin for AceInputPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
gamepad::rumble_gamepads,
quit.run_if(
in_state(GameState::MainMenu)
.or_else(in_state(GameState::Matchmaking))
.or_else(
in_state(GameState::InRollbackGame)
.and_then(in_state(RollbackState::GameOver)),
),
),
),
)
.init_resource::<GamepadRumble>()
.add_systems(OnExit(GameState::AssetLoading), gamepad::configure_gamepads);
}
}
7 changes: 1 addition & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fn main() {
audio::GameAudioPlugin,
world::GameLogicPlugin,
network::AceNetworkPlugin,
input::AceInputPlugin,
camera::AceCameraPlugin,
ui::AceUiPlugin,
console::AceConsolePlugin,
Expand All @@ -108,11 +109,5 @@ fn main() {
.insert_resource(ClearColor(Color::BLACK))
.init_resource::<RoundStartTimer>()
.init_resource::<HideScreenTimer>()
.add_systems(OnExit(GameState::AssetLoading), input::configure_gamepads)
.add_systems(
Update,
input::quit.run_if(in_state(GameState::MainMenu)
.or_else(in_state(GameState::Matchmaking))
.or_else(in_state(GameState::InRollbackGame).and_then(in_state(RollbackState::GameOver)))))
.run();
}
13 changes: 13 additions & 0 deletions src/player/effect/bullet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bevy_hanabi::prelude::*;

use crate::{
camera::CameraShake,
input::GamepadRumble,
player::{
shooting::bullet::{BulletCollided, BulletFired},
LocalPlayerHandle,
Expand Down Expand Up @@ -122,3 +123,15 @@ pub fn add_camera_shake_bullet_fired(
}
}
}

pub fn add_gamepad_rumble_bullet_fired(
mut gamepad_rumble: ResMut<GamepadRumble>,
local_handle: Res<LocalPlayerHandle>,
mut ev_bullet_fired: EventReader<BulletFired>,
) {
for ev in ev_bullet_fired.iter() {
if ev.handle == local_handle.0 {
gamepad_rumble.add_rumble(0.1, 0.1);
}
}
}
Loading

0 comments on commit 50237c5

Please sign in to comment.