Earlier this year I was playing TrackMania Turbo, a fun game, which I enjoy quite a bit. The only thing that nagged me was the force feedback on my Xbox One Controller. At first everything seems normal, feedback is triggered upon impacting a wall, after a jump, and so on.
As you go on beating the highscore, you reset to the start point more and more quickly after an error, e.g. when hitting a wall. Suddenly after a reset the gamepad keeps vibrating even during the countdown and a good amount of the next level. It finally stopped after about 30 seconds of just vibrating and from there on it was normal again for a while, until it started happening again – and again, and so on. I switched to a Xbox One Elite Controller but the same thing kept happening time and time again. I tried different things, like plugging the controller in with a wire, re-installing the drivers, the typical things one does if something is not working correctly, but nothing helped. In the end, I disabled the force feedback and thought the game had a bug in its implementation.
Later on, the same thing started happening in a completely different game – Dirt Rally. However, it wasn’t so obvious at first. In this game, the force feedback is a far more integral part of the game, as it tells you how much your tires slip and so on. In this game, this happens only for short periods of time, e.g. half a second, until it fixes itself again. Nonetheless, it was still noticeable.
Same thing for Enter the Gungeon – the controller starts and keeps vibrating for longer than expected periods of time.
Now that I’ve experienced it in multiple different games, I thought this was a hardware or driver error and had nothing to do with the actual games.
Months later we started noticing this behaviour in our own game – Schacht. We tested it using all sorts of gamepads, including the PlayStation Dualshock 4 controller, the Steam controller, the Xbox 360 controller and the Xbox One controller. It all worked fine, except for the one controller again. At first, I didn’t investigate it further because of the assumption of the hardware error, but one thing kept me thinking: There are games where it works just fine. But I couldn’t see any connection between them, TrackMania Turbo, Dirt Rally and Enter the Gungeon, all different engines, different developers, and so on.
In our game I could then test more extensively. At first it works out fine, force feedback is triggered as expected, but then gets triggered delayed and then starts doing random things, like not doing anything at all, vibrating at random moments and so on. For me this seemed like some vibration events get lost after calling the functions. I changed the Unreal Engines XInput code so it only sends new force feedback values when we change them, and suddenly – it worked perfectly once again! I could’ve stopped there but sending the values only when they change includes sending no vibration at all only once, which in my opinion is not a good idea, because if this event gets lost for whatever reason we may wait a very long time until we resend another command, keeping the gamepad vibrating.
The solution was to send it in a regular interval, but how long should this interval be? 1ms? 5ms? 16ms? 100ms? To answer this question, I’ve created a small console application, which turns the motors on and off every 0.5 seconds, and allows me to enter the update rate in which XInputSetState gets called. And there I had it just in front of me: Everything above 8ms worked fine, the motors turned on and off in sync. With 8ms the motors randomly got out of sync, kept vibrating for a full second before turning off again or did not turn on at all. Reducing the update rate even further made the behaviour more apparent, setting it to 0ms (equals sending it as fast as possible) the gamepad started to vibrate for 30 seconds in succession or not at all.
You can try it for yourself:
Compile it, plug in a Xbox One Controller and start the program. It starts with an update rate of 33ms (~30fps) as a reference how it should vibrate (0.5 full vibration with both motors, 0.5 seconds no vibration at all, and so on). Enter a value in milliseconds and feel what happens (of course values above 500ms will miss updates since this is not the intended use for this application). This is only noticeable on the Xbox One controller, all others seem to work fine, independent of the update rate.
For Schacht, I’ve chosen an update rate of 33ms, anything faster would not be noticeable since the motors cannot start and stop this fast anyway.