Hover car physics stuttering?

Yesterday I was curious to see what would happen if my character wasn’t sitting on the vehicle to see if it helped it or not. I found that it made the stuttering 2x worse… I’m still unsure of what the issue could be but I’m definitely leaning on the side of it being a physics bug or something of the sort.

That’s funny, seems to be the opposite case for my tram.

Can you verify whether it’s the car stuttering or the camera?

Can make a video of the car driving down a straight tunnel with nearby walls? That would make it easier to see whether it is camera OR vehicle issues.

Also how are you driving this car? Do you have a link to the place?

I think I can verify that it’s the car stuttering rather than the camera. I made a video of the car driving in between 2 walls with 4 examples.

You can watch the video by clicking here. (Sorry for the bad text resolution, I provide a more in depth explanation of each of the clips within the video below)

In the first clip/example it has the smooth camera enabled and the “Detail Hook” I explained in the OP is being statically set to the car’s Base part’s CFrame (basically just as if it’s welded to it) and as you can see from the video it stutters quite a bit. The 2nd clip also has the smooth camera enabled but this time the Detail Hook’s CFrame is being interpolated to the base part’s CFrame (the base part is what is having the physics applied to) which you can see how it’s being done near the end of the OP. You can see that as a result the stuttering is not as bad and appears smoother. The 3rd clip is where things get interesting. The smooth camera is disabled this time, and like the 1st clip the Detail Hook is essentially welded to the base. I noticed 2 things while recording this and looking back over the recording. Surrounding objects seemed to stutter ever so slightly at times and most importantly, at the end of the first run down the corridor when I turn around you can see the car visibly stutter while rotating. Now onto the last clip. In this clip the smooth camera is still disabled, but like the 2nd clip the Detail Hook’s interpolation is turned on rather than it being ‘welded’ to the base of the car. As you can see it stutters very badly.

I explained quite a bit of how the physics of the car works in the OP, it uses vector forces on 4 parts that are welded to the base to push up away from the ground depending on the distance they are from the ground. I didn’t explicitly mention that I use a vehicle seat for reading the car’s throttle and steer inputs. The car can also do flips and rolls but it isn’t relevant to this matter. Currently the car is set up in this way: The server get’s a copy of the car from ServerStorage and generates the hover parts, detail hook, etc. then welds everything together and then spawns the car onto one of the car spawns. While no one is driving one of the cars the server handles the car’s physics by pushing away from the ground but isn’t as advanced as when the client is handling it. When a player hops in the car vehicle seat the server stops doing physics updates to it and give’s the player network ownership of the car and then it also gives the client full control over the car and at that point a local script does all the physics/movements of the car.

I don’t have a link to a place you can try out at the moment, but if you need to, let me know and I’ll prepare a place you can drive it for yourself.

@Khanovich I went back to the thread where this started and found a repro and modified it a bit to include one more example where it uses the critically damped spring model from the OP above and the results were interesting.

Here’s a place file for it:
JudderRepro.rbxl (414.6 KB)

You can press Q and E to switch between using lerp or the spring for the camera movement. Press WASD to move forward, side to side, etc. and point mouse to look around. When using lerp with a static alpha you’ll notice the judder from the red cube but the camera is smooth. But when you’re using the spring you’ll see that not only does the object judder, the camera judders in sync with the object very clearly.
(Script inside StarterPlayerScripts)

Sorry this took 10 days to respond to.

Been doing some investigation and here is what I found so far:

  1. RunService:BindToRenderStep(‘Controller’, Enum.RenderPriority.Camera.Value, function(dt)… this bings RenderStep to the same priority as Camera, which means that sometimes it will fire before, and other times AFTER the camera. You actually want to have HIGHER priority than Camera.Value, so that this update happens before Camera runs.

  2. Now, once I did that there was still some stuttering, but it was more random (rather than constant). I started printing out velocity of the part and noticed that occasionally RenderStep would fire 2 times before physics would simulate (and cause a velocity change). Using this information I cached “lastCFrame” and stopped camera logic if lastCFrame was part.CFrame this time. Once I did this I noticed that the part jumps forward every now and then.

This means that sometimes the engine is rendering twice, and then has a follow-up physics step does double work. I’m trying to investigate how this is happening, and seeing if I can think of a workaround.

4 Likes

Found a bug related to engine TaskScheduler. For some reason it was sometimes running Render 2 times between Physics. Which is a bug…

Investigating further.

4 Likes

Okay. It looks like number 2 is ONLY an issue with Studio. This is probably because of how Rendering hooks into Qt (which is the framework we use for the basic application UI).

It seems like if I upload the following level as a game (after having fixed problem number 1), there is no jittering when running the critically damped spring. Can you please verify?

JudderRepro (1).rbxl (414.8 KB)

3 Likes

I uploaded the level as a game and still found that the cube jitters with the critically damped spring but the camera doesn’t jitter along with it like it does in studios. There’s definitely still noticeable jittering going on with the cube even online. Could it be due to the bug you found for the TaskScheduler engine?

Do you have a video of the jittering? And did you upload my place or your place?

I uploaded your place, here’s a video of it happening:
https://i.gyazo.com/fea5529d371a4b7b6c3135c13eef29a2.mp4
It’d be strange if it’s not happening for you but is for me…

Here was my result.

Although after playing a bit more I think I did see some micro-stutter which probably had more to do with physics doing a variable amount of work to try and stay at 60Hz. (One render frame may do 3/4th physics work, and then 5/4th physics work the following frame).

This is my testing place:

I tried your testing place and the result was the same as my place, for some reason it jutters a lot more violently for me.

How about this place?

JudderRepro-UseRunServiceSteppedDtInsteadOfRenderStepped.rbxl (414.9 KB)

The only change is that in the spring equation instead of using RenderStepped’s dt, I use RunService.Stepped:wait() dt, which should actually track the different amount of work physics step does from frame to frame.

Oh wow, it completely eliminated the jutter for me

Yeah so here is what is happening:

ROBLOX has internal physics steps (lets call these WorldSteps) running at 240Hz. A higher level physics job (lets call it PhysicsStep) runs at 60Hz.

When PhysicsStep runs, we try to see how much time has passed between now and last time PhysicsStep ran. Most of the time it will be 0.0166666 seconds or so. (Smooth 60Hz). When that happens, we say that for every 0.00416666 seconds of time we want to do 1 world step. For a smooth 60Hz step this will end up 4 world steps, which ends up running at 240Hz.

Occasionally what happens is some frame-rate fluctuates (Render is actually the thing that sets the timestamps for frames), and because of how frames got pushed around (due to rendering taking a bit longer, maybe), you notice that the time between the current and last PhysicsStep is only 0.015 seconds. This isn’t enough time to fit 4 x 0.004166665 second steps, so instead we will only run 3, which means we only do about 0.012499995 seconds of simulation. This usually means that next PhysicsStep will also do 5 world steps to compensate for the missed one, doing another small discrepancy between physics simulation dt and render dt.

This system is responsible for Physics/Game Logic staying real time when FPS drops below 60.

Since you’re using RenderStepped dt you can be seeing 0.0166666 second spring simulations for only 0.012499995 seconds of physics simulation.

So summary, we had 3 sources of Jitter:

  1. RenderStepped priority was set to the same as Camera, which means sometimes camera script updates before and sometimes after the Camera renders.
  2. Studio bug with Engine scheduling of Tasks.
  3. Simulating spring-dragged camera using Rendering dt instead of Physics dt.

EDIT: By the way I disabled the feature that was causing the Engine Scheduling issue (on Studio only). So you should be able to have the same smooth behavior in Studio and when running with Player.

29 Likes

Very interesting and in depth explanation and also very much appreciated! Thanks for looking into this and providing a solution. I’ve been using a ‘hacky’ method of getting the camera to follow my car and it’s been quite a pain due to the latency and other issues it produces but now with this information I’ll be able to use the critically damped spring to pull it off much more efficiently. I also notice that the result is the same in studio now, most likely since you disabled the feature that was causing the scheduling issue in studios.

Thanks again, this was all super helpful and I’m sure others will find it helpful when making a similar spring-dragged camera!

1 Like

These files are all locked now, however the solution code of the thread problem lies in there, please reupload or make the Roblox place available, will be a massive help.

1 Like

All the solution files have been taken private. Would it be possible for you to share them again?

I downloaded the file back when it was available:
JudderRepro-UseRunServiceSteppedDtInsteadOfRenderStepped.rbxl (414.9 KB)

6 Likes