Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rumble support to bevy_gilrs #3868

Closed
wants to merge 2 commits into from
Closed

Conversation

nicopap
Copy link
Contributor

@nicopap nicopap commented Feb 5, 2022

This adds the RumbleRequest event and a system to read them and rumble
controllers accordingly.

It gives users two ways of controlling controller rumble:

  1. A very primitive API with RumbleIntensity that is easy to
    understand and use.
  2. A direct access to the girls ff::Effect system for complete
    fine-grained control over how the gamepad rumbles.

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Feb 5, 2022
Comment on lines +21 to +22
Medium => ff::BaseEffectType::Strong { magnitude: 40_000 },
Weak => ff::BaseEffectType::Weak { magnitude: 40_000 },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This magnitudes shouldn't be the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the gilrs example They use the same magnitude with a different BaseEffectType (you'll notice here we use Strong for Medium and Weak for Weak) to demonstrate a periodic change in force feedback intensity. Testing it myself with two different controllers, I find all different RumbleIntensity to be distinct enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't notice the different Types here. Then everything is fine here

This adds the `RumbleRequest` event and a system to read them and rumble
controllers accordingly.

It gives users two ways of controlling controller rumble:
1. A very primitive API with `RumbleIntensity` that is easy to
   understand and use.
2. A direct access to the girls `ff::Effect` system for complete
   fine-grained control over how the gamepad rumbles.
@nicopap
Copy link
Contributor Author

nicopap commented Feb 5, 2022

Notes:

Re-export of gilrs::ff

This implementation re-exports gilrs::ff so that user can create their own rumbling patterns (as demonstrated in the examples) however, we could be more selective in what we re-export. Would it be better this way?

Or is it not even OK to re-export and should instead create our own wrapper types. I really don't want to do that, because the EffectBuilder API of gilrs seems very expensive, and would be difficult to replicate.

Error handling in gilrs_rumble_system

I don't like how that function look (so much error handling diluting the actual logic, yuk!) But what's the alternative here? I thought that:

  • Define a RumbleError enum
  • implement From<ff::Error> for RumbleError
  • Extract the inner body of the for loop into it's own funciton that returns Result<(), RumbleError>
  • match the function call

This separates the error handling from core logic handling, but it's much more code. What's the best choice?

@mockersf mockersf added A-Input Player input via keyboard, mouse, gamepad, and more C-Enhancement A new feature and removed S-Needs-Triage This issue needs to be labelled labels Feb 6, 2022
@nicopap
Copy link
Contributor Author

nicopap commented Feb 11, 2022

There is still an open question: how do we want this to play out with mobile rumble? (and by extension, web)

It might or might not be wishable to have a unified interface to interact with mobile rumble. Having a unified API means the gilrs force feedback system needs to be encapsulated and not exposed to the end user.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label May 16, 2022
@@ -20,10 +23,13 @@ impl Plugin for GilrsPlugin {
{
Ok(gilrs) => {
app.insert_non_send_resource(gilrs)
.add_event::<RumbleRequest>()
.init_non_send_resource::<rumble::RumblesManager>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.init_non_send_resource::<rumble::RumblesManager>()
.init_non_send_resource::<RumblesManager>()

This should just be imported.

.add_startup_system_to_stage(
StartupStage::PreStartup,
gilrs_event_startup_system,
)
.add_system_to_stage(CoreStage::PostUpdate, rumble::gilrs_rumble_system)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.add_system_to_stage(CoreStage::PostUpdate, rumble::gilrs_rumble_system)
.add_system_to_stage(CoreStage::PostUpdate, gilrs_rumble_system)

use gilrs::GilrsBuilder;
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
pub use rumble::{RumbleIntensity, RumbleRequest};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub use rumble::{RumbleIntensity, RumbleRequest};
pub use rumble::{gilrs_rumble_system, RumbleIntensity, RumblesManager, RumbleRequest};

fn effect_type(&self) -> ff::BaseEffectType {
use RumbleIntensity::*;
match self {
Strong => ff::BaseEffectType::Strong { magnitude: 63_000 },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values seem super arbitrary and hard-coded.

}
}

/// Request `pad` rumble in `gilrs_effect` pattern for `duration_seconds`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrasing here is a little unclear.

/// # Notes
///
/// * Does nothing if `pad` does not support rumble
/// * If a new `RumbleRequest` is sent while another one is still executing, it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is always the correct behavior. Could we have an overwriting field perhaps?

}
}

struct RunningRumble {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs docs; I'm not immediately sure what it does.

.insert(pad_id, RunningRumble { deadline, effect });
Ok(())
}
pub(crate) fn gilrs_rumble_system(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub(crate) fn gilrs_rumble_system(
pub(crate) fn play_gilrs_rumble(

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really want this functionality and this API is a great start.

However, I feel pretty strongly that we should be wrapping this. I want this to work across platforms: vibration is a critical feature for web / console / mobile / VR, and gilrs isn't going to cover those.

.insert(pad_id, RunningRumble { deadline, effect });
Ok(())
}
pub(crate) fn gilrs_rumble_system(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should be pub?

log::debug!("Tried to rumble {pad:?} but an error occurred: {err}")
}
Err(RumbleError::GamepadNotFound) => {
log::error!("Tried to rumble {pad:?} but it doesn't exist!")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log::error!("Tried to rumble {pad:?} but it doesn't exist!")
log::warn!("Tried to rumble {pad:?} but it doesn't exist!")

@@ -194,6 +194,7 @@ Example | File | Description
`char_input_events` | [`input/char_input_events.rs`](./input/char_input_events.rs) | Prints out all chars as they are inputted.
`gamepad_input` | [`input/gamepad_input.rs`](./input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections
`gamepad_input_events` | [`input/gamepad_input_events.rs`](./input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events
`gamepad_rumble` | [`input/gamepad_rumble.rs`](./input/gamepad_rumble.rs) | Make a controller rumble
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`gamepad_rumble` | [`input/gamepad_rumble.rs`](./input/gamepad_rumble.rs) | Make a controller rumble
`gamepad_rumble` | [`input/gamepad_rumble.rs`](./input/gamepad_rumble.rs) | Makes a controller vibrate for force-feedback purposes

Keyword stuffing!

@@ -0,0 +1,70 @@
use bevy::{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a module-style doc comment explaining what the example does.

@nicopap nicopap marked this pull request as draft June 18, 2022 12:15
@nicopap nicopap closed this Sep 1, 2022
@alice-i-cecile alice-i-cecile added the S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! label Sep 1, 2022
@johanhelsing
Copy link
Contributor

Hey, I'm considering picking this one up. Anything I should know about @nicopap @alice-i-cecile ?

@nicopap
Copy link
Contributor Author

nicopap commented Apr 16, 2023

@johanhelsing nope. I think everything relevant should be in the diff and comments of this PR.

@johanhelsing
Copy link
Contributor

I merged main into this branch and got it working again. My branch is here https://github.com/bevyengine/bevy/compare/main...johanhelsing:bevy:rumble?expand=1 I'll create a PR when/if I have something I want feedback on, but that branch is probably a good starting point if I end up abandoning it and someone else wants to pick this up :)

alice-i-cecile added a commit that referenced this pull request Apr 24, 2023
# Objective

Provide the ability to trigger controller rumbling (force-feedback) with
a cross-platform API.

## Solution

This adds the `GamepadRumbleRequest` event to `bevy_input` and adds a
system in `bevy_gilrs` to read them and rumble controllers accordingly.

It's a relatively primitive API with a `duration` in seconds and
`GamepadRumbleIntensity` with values for the weak and strong gamepad
motors. It's is an almost 1-to-1 mapping to platform APIs. Some
platforms refer to these motors as left and right, and low frequency and
high frequency, but by convention, they're usually the same.

I used #3868 as a starting point, updated to main, removed the low-level
gilrs effect API, and moved the requests to `bevy_input` and exposed the
strong and weak intensities.

I intend this to hopefully be a non-controversial cross-platform
starting point we can build upon to eventually support more fine-grained
control (closer to the gilrs effect API)

---

## Changelog

### Added

- Gamepads can now be rumbled by sending the `GamepadRumbleRequest`
event.

---------

Co-authored-by: Nicola Papale <nico@nicopap.ch>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
Co-authored-by: Bruce Reif (Buswolley) <bruce.reif@dynata.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Input Player input via keyboard, mouse, gamepad, and more C-Enhancement A new feature S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants