Frame rate independent lerp?

Thank You for suggesting. These are the results when a = 0.99 ^ (1 / deltaTime):
30 fps
240 fps
It is not perfect but it does look more accurate. There might be a better way.

Possibly try replacing the exponent sign (^) with multiplication (*)?

Thanks,
Unfortunately, a * (1 / deltaTime) is greater than 1, therefore the lerp just breaks:
60 fps

Ah, missed that - if you just multiply by the delta time rather than 1 / delta time it might work :slightly_smiling_face:

1 Like

I am a colleague of @LeikaZ. And since he is sleeping… Because it is 03:00… Anyways. The lerp is very slow when multiplying deltaTime by values from 0 to 1. So the variable would end up something like a = 2 * deltaTime. This would mean that a could get higher than 1, therefore mess up the lerp. I have adjusted the variable to a = 0.5 * (1 - deltaTime). But even then the results were far from perfect:
https://gyazo.com/44bda1eddeda61a8f626b6167298fd32
https://gyazo.com/62fd180d29b5ff54f3821b306249ffa5

i had a similar problem but since im bad at math i just took a large number (like if i did .1 i’d do 100 or something) and then multiplied by delta time, it takes some experimenting but it might help??

I believe changing a to a / (1/deltaTime)/maxFPS should do the trick. If the game is running at 240 fps and deltatime is 1/240 this should yield 240/maxFPS so an a of 0.5/1. If the game is running at half speed (120/240) you’ll get an a of 0.5/0.5 so 1 (twice the default in twice the time). Now you can clamp a like this to make sure it doesn’t lerp too far: math.clamp(a, 0, 1)

1 Like

Thank You for replying, @Hexcede. Unfortunately, that does not seem to be the answer either… The lerp becomes too stiff on low FPS compared to mid/high FPS.

Here are the results when a = math.clamp(0.25 / ((1 / deltaTime) / 144), 0, 1):
https://gyazo.com/c54801c0e742b9e8985c217809129818
https://gyazo.com/a145987bc6a62b5baabc02b4b056c085

not into maths but, how about a = 0.2 * deltaTime * 30
meaning a would be around 0.2 when fps is 30, would be 0.1 when fps is 60, and 0.4 when fps is 15, lower the fps goes it would go towards 1, and eventualy exceed it, not sure how you’d keep the same effect under 8-10 fps but i’m sure nobody will be playing the game at that framerate… you can probably clamp it so it doesn’t go over 0.8 or 0.9 with a = math.min(0.2*deltaTime*30, 0.8).
otherwise i think lerp wouldn’t work.

2 Likes

Thank You for replying. This method works pretty well with any frame rate over 30. Here are the results when a = math.min(0.8 * deltaTime * 30, 0.9):

30 FPS
https://gyazo.com/fd3e249c62641b40e0e2388c02403ac3
60 FPS
https://gyazo.com/a12373f5965b7fb700c45667313ead53
240 FPS
https://gyazo.com/0016a454debeac128706004bac2238b8

We are still open for suggestions.

i found this on the internet, i suggest you read it, might help what you’re looking for, not in roblox LUA but it’s still understandable:

I don’t know the answer, but I think we’d all have a better chance of getting it if we discussed the issue, rather than just say “try X”.

How is DeltaTime usually used?

When moving a part to be frame rate independent, you define a Speed in StudPerSecond. Because you know how much time has passed, you know how many studs it’ll have moved between frames.

-- Typed on mobile

RS.Heartbeat:Connect(function(DeltaTime)
  Part.CFrame = Part.CFrame * CFrame.new(0,0,-Speed*DeltaTime)
end)

How can we apply this concept to lerps?

With OP’s current implementation, the lerps have no defined speed. DeltaTime therefore doesn’t give us useful information, because knowing how much time has passed doesn’t help if we don’t know how much it moves per time increment. They are therefore frame rate dependant.

The question is now how do we set a Speed for a lerp? OP clearly wants non-linear motion, so it gets even more complicated when you take that into account.
Let’s start with linear motion, and then we’ll figure out how we can implement an easing function.

1 Like

A very good article on this topic: USING LERP WITH DELTA-TIME,
and the answer is a = lerp(a, b, 1 - f ^ dt)

3 Likes

Hey, you might have linked a wrong link. What does a, b and f stand for? Thanks!

I will post the answer here.
a - start value
b - result value
f - time
a = lerp(a, b, 1 - f ^ dt)

It can be easily adjusted to a vector lerp:
local currentVector = currentVector:Lerp(targetVector, 1 - howFastItLerps ^ deltaTime)

PERFECT ACCURACY

The answer is not entirely obvious. It’s actually:

a = lerp(a, b, 1 - f ^ dt)

…where f is the factor between 0 and 1 deciding how quickly it catches up, e.g. 0.25. In fact it works out that this is the remaining factor per second, so if it’s 0.25 it means it covers 75% of the remaining distance every second - independent of the framerate!

So why does this work? Since we cover 1 - f^dt, the remaining distance is f^dt. Over n frames, the remaining distance is thus (f^dt)^n. Again we can assume a steady framerate and that dt is equal to 1 / n . This gives the remaining distance over one second at n frames per second as (f^(1 / n))^n. If you know your powers, you know that (x^a)^b = x^ab. So we can simplify and see that the remaining distance after one second is f^1 = f. So it’s covered the same distance in one second regardless of the framerate!

This has the nice quality of being able to easily decide the percentage of the distance it covers every second. As before using f = 0.25 means it covers 75% of the distance regardless of the framerate, whereas trying to figure out the multiplier when just using f * dt to get it to cover 75% of the distance per second is very tricky (actually mathematically impossible, since it depends on the framerate), and probably just means guessing a few numbers and eyeballing it. Also as long as 0 < f < 1 , it will never overshoot.

27 Likes

Completely off topic but how is your client surpassing 60fps? Unless you’ve done some sort of modifying the Roblox client to remove the frame cap, I can only suspect you are somehow hooked up to physics updates (which according to my recollection go up to 240hz)…

I’m using a Roblox FPS Unlocker by axstin. During the last RDC staff confirmed that it’s allowed.

3 Likes

Ah, I see. Good to know!

filler

This works! In order for this kind of sway lerp to look good I have to set the f to something really low like 1 / trillion which is a bit annoying.

I’m a bit late but the cleanest solution (not requiring 0.000000001 type numbers,) would be
current = current:lerp(target,1-math.exp(-speed*deltaTime))

26 Likes