Using DeltaTime

I haven’t seen anything about this, so I will make this my first tutorial.

What is DeltaTime?

DeltaTime is the time between events. This is extremely useful, often used to make sure frame rate makes no difference on what happens. The focus of this thread will be on DeltaTime with RunService events, wait, spawn, and delay.

Why use DeltaTime?

Since DeltaTime is the time in between events, this can be used with RunService events/wait when timing is important.

RunService events are tied directly to frame rate, so when the frame rate drops, they fire less often. To counteract this, DeltaTime is used to adjust what is changed based on how long it has been since the previous frame (sometimes this isn’t helpful, and you only want something to happen every frame, without regard for DeltaTime).

Example: Typewriter effect not using DeltaTime

local Goal = "Some text that should appear after some time."
for Index=1,#Goal do
	wait()
	TextElement.Text = string.sub(Goal,1,Index)
end

When the time it takes to resume wait() changes, text will appear slower. Under normal circumstances this will work fine, but if the frame rate drops or there are too many threads to be resumed in one frame the wait() will take longer to resume, making the text appear slower.

Choosing to wait for a RunService event (Heartbeat:Wait(),Stepped:Wait(),RenderStepped:Wait()), will still make it susceptible to a drop in frame rate.

Example: Typewriter effect using DeltaTime

local RunService = game:GetService"RunService"
local Heartbeat = RunService.Heartbeat
local Goal = "Some text that should appear after some time."
local GoalLength = #Goal
local TimeToTake = GoalLength/30
local Accumulated = 0
while TimeToTake > Accumulated do
	Accumulated += Heartbeat:Wait()
	TextElement.Text = string.sub(Goal,1,math.floor((Accumulated/TimeToTake)*GoalLength))
end

With this, whenever the frequency of Heartbeat firing changes, the rate at which the text appears won’t change.

Getting DeltaTime

All of the RunService events provide a method to get DeltaTime.

  • Heartbeat - DeltaTime
  • Stepped - RunTime, DeltaTime
  • RenderStepped - DeltaTime

Functions passed to RunService:BindToRenderStep will be called with DeltaTime.

wait returns DeltaTime, ElapsedTime.

spawn and delay call their functions with DeltaTime, ElapsedTime.

(ElapsedTime is similar to the result of the elapsedTime function)
(RunTime is the time RunService has been running for, similar to the result of the time function)

Examples
local RunService = game:GetService"RunService"
local Heartbeat,Stepped,RenderStepped = RunService.Heartbeat,RunService.Stepped,RunService.RenderStepped
local DeltaTime = Heartbeat:Wait()
local RunTime,DeltaTime = Stepped:Wait()
local DeltaTime = RenderStepped:Wait()
RunService:BindToRenderStep(name,level,function(DeltaTime)
end)
local DeltaTime,ElapsedTime = wait(Time)
spawn(function(DeltaTime,ElapsedTime)
end)
delay(Delaying,function(DeltaTime,ElapsedTime)
end)

The RunService events DeltaTime are the times since the events fired, not the time since the events were used.

local StartingTime = os.clock()
local DeltaTime1 = Heartbeat:Wait() -- time since previous heartbeat
local DeltaTime2 = os.clock()-StartingTime  -- time since Heartbeat:Wait was called

os.clock() gets the time since an arbitrary point, and can be used to get DeltaTime.

local Function do
	local Last = os.clock()
	function Function()
		local DeltaTime = os.clock()-Last
		Last = os.clock()
		--...
	end
end

This is especially useful with debounces.

local Function do
	local Last = 0
	local Debounce = 0
	function Function(--[[...]])
		if os.clock()-Last >= Debounce then
			Last = os.clock()
			--...
		end
	end
end

This is also useful for getting how long a block of code took to run (benchmarking).

local Start = os.clock()
--...
local DeltaTime = os.clock()-Start
Some examples of using DeltaTime

An accumulator, to have block run every so often based on Rate.

local Rate = 0.1 -- 10 times a second
local Accumulated = 0
local RunService = game:GetService"RunService"
local Heartbeat = RunService.Heartbeat
Heartbeat:Connect(function(DeltaTime)
	Accumulated += DeltaTime
	while Accumulated >= Rate do
		Accumulated -= Rate
		--block
	end
end)

A timer, using wait.

local Excess = 0
local Interval = 0.1  -- how often it gets updated
local Total = 0
local Length = 60 -- length of timer
TextElement.Text = Total
while Total < Length do
	Total += wait(Interval-Excess)
	Excess = Total%Interval
	TextElement.Text = math.min(Total-Excess,Length)
end

Interpolating a part.

local RunService = game:GetService"RunService"
local Heartbeat = RunService.Heartbeat
local Goal = CFrame.new(...)
local Starting = Part.CFrame
local TimeToTake = 100/60
local Accumulated = 0
while TimeToTake > Accumulated do
	Accumulated += Heartbeat:Wait()
	Part.CFrame = Starting:Lerp(Goal,math.min(Accumulated/TimeToTake,1))
end

Typewriter effect, but working with unicode characters.

local Text = "a\u{2022}b\u{2022}c\u{2022}d"
local RunService = game:GetService"RunService"
local Heartbeat = RunService.Heartbeat
local Len = utf8.len(Text)
local ToTake = Len/30
local Accumulated = 0
local UPos = 1
local UChar = 0
while Accumulated < ToTake do
	Accumulated += Heartbeat:Wait()
	local Char = math.min(math.floor((Accumulated/ToTake)*Len),Len)
	if Char ~= UChar then
		UPos = utf8.offset(Text,Char-UChar+1,UPos)
		TextElement.Text = string.sub(Text,1,UPos-1)
		UChar = Char
	end
end
177 Likes

Great tutorial! I still don’t understand some parts of DeltaTime, especially the formulas to calculate the speeds of something. Do you have any recommendations to train this topic?

9 Likes

Can u explain me the mathematical part in text element variable in the typewriting effect by run service script??

1 Like

What do you mean by a thread? I’ve heard this term used a lot in the video game industry (both on Roblox and in other languages such as C++)

I used thread to refer to a Lua coroutine, as this is what the reference manual calls it.