0
Answered

Physics frame rate dependence problem

Crystalius 3 years ago updated 3 years ago 8

Hello,

I am probably missing something very obvious here, but can't figure it out.

While in air — I am pushing character to the last direction it was moved. 

Problem is that when frame rate is low, the distance jumped is slightly shorter than when fps is high.

I use acceleration for both jumping and moving on vertical axis.

I managed to achieve frame rate independence for jumping (height) using FixedUpdate and * delta.time but for movement, the best result I got is using just Update without * delta.time.

For simple movement on ground this works good; you can't notice any difference, but for jumping front or back — this small difference is is really bad for designing distances between stuff player will jump on.


Unsuccessfully tried many different combinations. Using FixedUpdate and * delta.time or just FixedUpdate with no * delta.time results in faster movement while fps is low. Both in air or on ground.

Also played with Time settings.
Setting Maximum Allowed Timestep close to Fixed Timestep solves the problem, but then char actions are in slow motion..

Push in air code:
(Movement var is made of Get Vertical Axis + Speed var)


Jumping up code:

Googled, but didn't help..


Thanks!


Bolt Version:
Unity Version:
Platform(s):
Scripting Backend:
.NET Version (API Compatibility Level):
+1

Is the trouble in jump height, or in horizontal displacement during the jump?  (Following answer presumes vertical)

I suspect that your animation curve is part of your problem, because the idea that FixedUpdate always occurs at a fixed frequency is, sadly, a lie.  For the most part, the unity core is single threaded (there are exceptions, more and more as unity develops).  FixedUpdate runs in this same thread, at the same interval as Update, except for being called multiple times to simulate a fixed timestep or delaying until the fixed step has elapsed (but still just before Update).  You can test this for yourself by doing something like the following in an empty scene:




This resulted in some output that looks like this (with a 20 fps target rate):



As you can see, Unity does try to make FixedUpdates happen the correct number of times, but it is limited to the frame rate, despite our best efforts  (You can see a jump from 4.135684 to 4.178488, a gap 2.5 times larger than we want).  The physics system tries to correct for this by calling the fixed update multiple times with the DeltaTime returning FixedDeltaTime of the correct timestep value.  But WHEN it does this is NOT fixed in time.  If your timer variable is using actual time, then your animation curve is evaluating real time at arbitrary intervals and you need to switch to maintaining your timer variable with deltaTime from within the Fixed Update, manually.  Then you will update the physics simulation on a fixed timestep, regardless of your framerate.

If you've already looked into that possibility, sorry!    I'm not sure what that timer input to your animation curve is hooked up to!

Thank you very much for your reply.
Sorry for not being perfectly clear in my post, but I've already perfected jump height before posting; it's frame rate independent with the code I've provided. Timer Super Unit simply multiplies variable by delta.time inside.

The problem is horizontal push front or back. Since it doesn't have a timer, it pushes the character the same amount every Fixed Timestep (because all physics stuff always happens in Fixed Timestep)
But when fps is low, Fixed Timestep lags too, because of Maximum Allowed Timestep. 

My goal is to achieve what is achievable by lowering the Maximum Allowed Timestep, to be near Fixed Timestep. Physics becomes more precise, hence solving my problem, but character behaves in slow motion what I don't want.

I tried to apply same method as jump up (FixedUpdate and * delta.time) for pushing forward/backwards while in air (or ground for walk/run), but it becomes worse; the amount of push is much bigger when fps is low compared to when fps is high. 

I am not good in math so I can't come up with some good, difficult calculations to overcome this obstacle, but I feel there must be simple solution to this problem.

If no one's going give me great solution — I will try to simply increase the push force when fps is very low. Like if fps <= 25 but >= 20 increase the push force by x and so on, until it goes to 1 fps :D
And simply return the normal push force when fps is high.

The reason I am doing this is because I want for players with slow PC's to still have everything precise. 

+1

Oh, okay, I misunderstood you.  Sorry!


Turns out you have the same cause, though expressed in a slightly different way.  Add Force has a couple of different modes, and you're using Acceleration, which divides whatever value you pass in by deltaTime (it is still a one time pulse, it just assumes you're going to be adding the force over many frames, so it tries to get this frame's slice of the value).

In your graph, you are adding this force in an Update, which means it is actually doing a frame dependant time calculation (why physics calculations don't all use fixedDeltaTime, I don't know, I really don't).  


I made a little sample animation where you can see this (top cube is Update-based, bottom is FixedUpdate-based):


I do this at 20fps target and then at 60fps target.  As you can see, even at 60 fps they aren't exactly the same due to the editor, presumably.

Anyway, the upshot of this is that AddForce should be in a FixedUpdate, because otherwise the node itself is making you time-dependant. Now, because you are using acceleration mode, you don't need to multiply by deltaTime, Unity is doing this for you!


Also, I wouldn't mess with Maximum Allowed Timestep, as that basically causes your physics simulation to lie to itself and the slowdown associated with it is unavoidable.  Instead, I'd move everything over to FixedUpdate, get rid of the * deltaTime (if you are using acceleration), as the node is doing it for you, so you are essentially doing deltaTime ^ 2.

Thanks for your time!

I thought I've tested all combinations for jumping (height) but looks like it's frame rate independent without multiplying by time.deltatime too. This will be a little bit more effective:)

But for jumping backward/forward this doesn't work. I've tested this combination some time ago.


This code pushes way too much when fps is low and okay when fps is high. 

Looks like it does more FixedUpdates when fps is low which causes faster speed. I don't know.. 

By the way, I simulate slow PC mode more practically, by cloning hundreds of animated enemies, instead of setting target frame rate. This feels more realistic to me.

Pushes way too much also with these combinations:

Acceleration FixedUpdate * time.deltatime
Velocity Change  FixedUpdate * time.deltatime and without * time.deltatime

---

Acceleration/Velocity Change in Update without * time.deltatime gives best results, with only very slight shorter push when fps is low, which is still not precise enough.
When * by time.deltatime, the push becomes too strong when fps is low again.

I gave up understanding logically this stuff as it looks so chaotic to me.. Only experimenting randomly.. Which I am giving up too and just going to implement fps detection system (mentioned in third post) to make jumping precise on all fps situations..

If there is a better solution — I would love to know it, so I can make my code more effective.


Thanks!

Turns out it didn't work because "Apply Root Motion" in Animator settings was on!

Turned it off and it's almost totally frame independent now. Only when it's <10 fps the forwards/backwards jumps are a little larger. Made it better by decreasing the amount of force applied forwards/backwards while in air and when fps <10 and it's good enough.

It's not perfect because of Maximum Allowed Timestep. When fps drops a lot, FixedUpdates becomes less accurate or something like that. 
When Maximum Allowed Timestep is close to Fixed Timestep — precision is great but slow motion starts..

+1
Answered

I didn't have time to read the whole thread, but I would recommend multiplying by Time.deltaTime (or the Per Second unit, which does exactly that) even when in Fixed Update. This is the only way to ensure framerate independence. As the Unity documentation states, Time.deltaTime is adjusted for the fixed step when called from within a FixedUpdate event, which holds true for Bolt. 

Tried  * Time.deltaTime, it doesn't make it better. Still got to manually adjust AddForce Acceleration when fps is low to go as frame independent as possible