How Can I Add Delta Time To Make Framerates The Same?

Hello, I have been following a tutorial on how to implement a physics-simulated spring for my game, but was unable to sync movements across multiple framerates. I have researched on how to do this but have had no luck in my attempts, as I am fairly new to using deltatime.

function Spring:Simulate(delta : number)
	self.velocity += ((-self.tension * (self.position - self.target)) - (self.dampening * self.velocity)) * delta
	self.position += self.velocity
end

Any help would be appreciated!

3 Likes

Using delta time is pretty easy, all you really have to do is have a base rate and multiply it by the delta time.

For example, to move a part at 1 stud per second using delta time, you’d do this:

local movementPerSecond = Vector3.new(0, 1, 0)
runservice.PreSimulation:Connect(function(dt)
    part.Position += movementPerSecond  * dt
end)

Pretty much, all it is is multiplying calculations and lessening them so their speed will be the same across different delta times.

2 Likes

A previous attempt I did looked similar to this:

self.velocity += (-self.tension * (self.position - self.target)) - (self.dampening * self.velocity) * time

However, this did not work. A higher framerate appeared to be much more harsh. Is there any visible problem in the logic of this statement? The time variable is listed above.

You don’t want to be multiplying the delta time value.

Also, remember, multiplication comes first in calculations, no matter where it is in the function.
Just try wrapping the whole thing in brackets and then multiplying outside.

I attempted this previously by setting the expression in parentheses, then multiplying by delta, but it didn’t make movement sync between framerates. Prior research has shown examples like yours, but they haven’t seemed to work for me.

Why are you multiplying by 60? What does that number represent in your formula?

When I was researching someone indicated that you should multiply or divide by the framerate that you want. I have since changed this though.

What object are you changing the properties for? Are you directly setting a new position for the players primary part or setting a velocity for a bodymover like a linear velocity?

If they haven’t worked, you’re doing something very wrong.
For example:

--// Right
local num = ((13 * (4 + 8)) - (72 / 14)) * delta
--// Entire calculation is multiplied by delta

--// Wrong
delta *= desiredFps
local num = (13 * (4 + 8)) - (72 / 14) * delta
--// Only the second half (after subtraction) is multiplied by delta

You should only ever multiply the delta time in few calculations, like lerp functions.

And you should only ever divide it to get the clients current FPS.

DeltaTime is the time between frames, so multiplying it does nothing but throw math off.

Yes I did the first version before but it didn’t solve anything. Not really sure what the problem is as other people have shown solutions similar to that.

Did you use the raw delta time or your weird multiplied version? The multiplication would be causing major differences between FPS.

I tried just the delta time after you said not to do that.

My example said not to multiply delta time.

Do not do this:

delta *= desiredFPS
--// Above is same as below
local newDelta = delta * desiredFPS

Yes I got rid of that. I can update the example.

So, dumb question, what’s actually calling the simulate function? Is it in a while loop, or connected to a runservice function?

Have you considered using ticks?

local TICK_RATE = 20 -- ticks per second
local TICK_INTERVAL = 1 / TICK_RATE -- seconds per tick

local deltaTime = 0
while true do
	while deltaTime >= TICK_INTERVAL then -- in case deltaTime is > 2 * TICK_INTERVAL
		signalOut() -- Do something on the tick
		deltaTime -= TICK_INTERVAL -- account for error
	end
	deltaTime += task.wait() -- Using TICK_INTERVAL here is an option, but beware for error
end

This is a simple way to make it so actions runs at roughly the same rate for everyone, although it falls apart if the user is already struggling to make the target tickrate.

But to more accurately address your question in OP exactly, though, we could use a busy wait :slight_smile:

-- starting with some external deltaTime
local previousTime = time() - deltaTime
while deltaTime < DESIRED_FRAME_TIME do
	local currentTime = time()
	deltaTime = time() - previousTime
	previousTime = currentTime
end

It is connected to a runservice heartbeat function.

	game:GetService("RunService").Heartbeat:Connect(function(delta)
		spring:Simulate(delta)
	end)
velocity += acceleration * deltaTime
position += velocity * deltaTime

I believe rates of change per second need to be changed to per frame, so both acceleration and velocity need to be multiplied by deltaTime

I had previously tried this but it didn’t resolve the issue.