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