-
-
Notifications
You must be signed in to change notification settings - Fork 24.2k
Add support for joypad haptics (force feedback) #114642
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
base: master
Are you sure you want to change the base?
Add support for joypad haptics (force feedback) #114642
Conversation
42740b3 to
0360006
Compare
It's used in conditional effects (named center in SDL3) together with the deadband parameter to set a dead zone for this kind of effects. Wheels in real vehicles have a dead zone where the wheel is resting when driving straight. These parameters can simulate that dead zone, and when changing the offset it can also simulate a broken steering that has its center off. It's also used in periodic effects to the set the mean value.
The periodic effects should be included, they can be used to simulate vibrations on the wheel without having to update the effects periodically. We're missing parameters like direction, saturation, deadband, envelope, repetition, etc. I like better the SDL3 approach with generic functions for all effect types, and passing the effect as a parameter. It makes it easier to add many effects without duplicating code. It also has structs with all parameters that are standard nowadays for force-feedback. It's probably tedious to use, but it gives way more control over the effects, kinda professional versus a hobbyist implementation. I appreciate its simplicity but it would turn fast into excessive rigidity when using it seriously. It might be a bit tedious to implement but not much work since it's just mirroring what SDL3 (and all APIs) already does. We should have functions to handle this flow:
The reason to do it separately like this is because effects are uploaded into device slots. Creating an effect can have a performance cost depending on the device, so we want to create the effect once and maybe play it many times, update it many times, then remove it when it will be no longer used for a while to free the slot. Start and Stop can actually be handled in the same call by setting repetition to zero, or just updating the effect with length zero. We have to also make sure we're handling infinite duration and infinite repetition correctly. |
0360006 to
16377ae
Compare
16377ae to
9621efe
Compare
|
Although this PR is draft, I played a bit with this (using only constant_force_feedback). Seems that the force is likely set to 0 for a while every time it is set, at least when the previous value is still in use. Video (with sound): First tested this with another project, but then made this tiny test-project (as seen in the video) to understand the behavior better: In https://github.com/Dechode/Godot-FFB-SDL (that I originally used in the "another project" mentioned) there are no these kind of glitches. Steering wheel used: Logitech Driving Force GT (yes, it's old and noisy, but at least the glitches can be easily heard ;) ), OS: Linux (Mint) BTW: Compiling this seemed to fail due to some undefined references. Commented these lines out from core/register_core_types.cpp to make it compilable (don't know if worth mentioning as this is draft, though...): |
|
Looking at the code, the behaviour (the wheel rattling) GNSS-Stylist said is because calling constant_force_feedback() starts and plays a new ffb effect, always. When the code is updated to work as Berarma said (and the description of the commit now), we can start, update, stop and remove the effect as we please. Then it should work as intended, as long as the process()/physics_process() only updates the already uploaded effect. |
|
What about GameInput API? Idk much about APIs |
We thought about using GameInput for SDL, but the license looks incompatible with Godot's MIT license: #107967 (comment) |
|
I see, thx! |
72b01e1 to
f125636
Compare
f125636 to
49de134
Compare
|
I have just overcome my biggest problem with this PR: my laziness to write the boilerplate code. This evening (for me) I wrote a Python script that would read a file in a special format I came up with and it would transform this simple file into a This script will be useful later when I decide to add I open-sourced this script in case anyone would find it useful too :D |
| } | ||
|
|
||
| void InputHapticEffect::set_direction_radians(float p_direction_radians) { | ||
| direction_radians = Math::fmod(p_direction_radians, (float)Math::PI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| direction_radians = Math::fmod(p_direction_radians, (float)Math::PI); | |
| direction_radians = Math::fmod(p_direction_radians, (float)Math::PI*2); |
For other places too
| class InputHapticEffectCondition : public InputHapticEffect { | ||
| GDCLASS(InputHapticEffectCondition, InputHapticEffect); | ||
|
|
||
| Type type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Type type; | |
| Type type = HAPTIC_EFFECT_SPRING; |
| void set_period(int p_period); | ||
| int get_period() const; | ||
|
|
||
| void set_samples(int p_samples); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in SDL samples is the amount of bytes in the data, so I should remove this property.
Closes godotengine/godot-proposals#8309
TODO:
What is anoffsetparameter mentioned in the original proposal exactly?Is this approach good enough or should we expose the entirety of the haptic subsystem? (WithInputHapticEffectclasses, constants and such, which sounds quite tedious to both use and implement 😅)Other effect types?M_PIwithMath::PIInput.create_joy_haptic_effect()Input.start_joy_haptic_effect()Input.update_joy_haptic_effect()Input.stop_joy_haptic_effect()Input.remove_joy_haptic_effect()