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 and wait (spawn and delay included).

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 i=1,#Goal do
	wait()
	TextElement.Text = string.sub(Goal,1,i)
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

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)

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 = tick()
		--...
	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

Getting how long a block of code took to run (used in 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
48 Likes