STOP USING WHILE TRUE DO LOOPS FOR PATTERN CHANGE!
folks I think this is basic optimization and coding knowledge
Foreword
“What are you talking about?” Well, here it is.
What's the Problem??
First, while true do loops takes up resources. A while true do loop with nothing can crash your game and your PC due to the fact that it is running without exhaustion, and it is just dying.
So what do most ELS developers do? Simple!
-
while true do wait(0) end
or -
while wait() do
end loops
What are the problems with those two now?
From a plain view, they are innocent (or so would you think) because you added a delay, you prevented the exhaustion. However, you have now further exhausted server resources by adding that wait()
. Let’s take a look at the script performance from a singular car with no ELS on:
It looks innocent at first glance, however, even though activity is low, it is definitely not good to have a script running over the rate of 10.0/s for something like ELS. Once you are in an actual game taking into consideration of every other things running (ex. more cars, guns, shops, etc), it will become apparent that ELS scripts is taking up way too much script rate and in some cases, performance..
Second, it is worthy to state that wait(), delay(), and spawn() (not to be confused with wait(n)
and delay(n)
, or RBXScriptSignal:Wait()
) run on ROBLOX’s 30 Hz pipeline. The minimal time to have it waited is 1/30th of a second, but in practice, they sometimes don’t achieve that, which leads to instability, tremendous delay, or sometimes doesn’t even run at all. This practice is known as code smell, an indication that there’s a deeper problem into this practice, and for the ELS developers, the “deeper problem” means performance issues and more.
“But less code is more readable!”
“But less code runs faster!”
These statements you have heard may be true in some cases. For the way it is used in ELS, however, it is actually more resource-intensive and will impact overall game performance more than it should.
More explanations can be found here. I may be inaccurate or have worded myself incorrectly on some of my statements, and these folks who wrote the article below did a better job than I have.
How can I solve it?
Well, my dear friend, do you know that RBXScriptSignal (also known as an event) exists???
Let’s take a look at this code below:
local ELS_State = script:FindFirstChild('ELS')
while true do -- Initiates the loop
wait() -- Puts a 'delay' so that resource 'does not exhaust'
if ELS_State.Value == 0 then
-- Idle
elseif ELS_State.Value == 1 then
-- Pattern 1
elseif ELS_State.Value == 2 then
-- Pattern 2
end
end
The first thing we can do is to switch out while true do
with an event so that it doesn’t constantly run:
local ELS_State = script:FindFirstChild('ELS')
ELS_State:GetPropertyChangedSignal('Value'):Connect(function()
--[[
We have switched out the while true do to an event
Now it doesn't constantly run and
It is much better for the overall game performance
]]
-- wait() is not needed anymore here
if ELS_State.Value == 0 then
-- Idle
elseif ELS_State.Value == 1 then
-- Pattern 1
elseif ELS_State.Value == 2 then
-- Pattern 2
end
end)
Now, you may say “Oh my pattern only runs once and it doesn’t repeat!” There’re two fixes to that, both are plausible depending on whichever one you like the most.
Method 1: repeat until
local ELS_State = script:FindFirstChild('ELS')
ELS_State:GetPropertyChangedSignal('Value'):Connect(function()
--[[
We have switched out the while true do to an event
Now it doesn't constantly run
It is much better for the overall game performance
]]
-- wait() is not needed anymore here
if ELS_State.Value == 0 then
-- Idle.
-- We don't need a loop here since this is idle.
elseif ELS_State.Value == 1 then
repeat
-- Pattern 1
until ELS_State.Value ~= 1 -- Exits the loop and moves on
-- Function to reset all lights to zero
elseif ELS_State.Value == 2 then
repeat
-- Pattern 2
until ELS_State.Value ~= 2 -- Exits the loop and moves on
-- Function to reset all lights to zero
end
end)
Method 2: while condition do
local ELS_State = script:FindFirstChild('ELS')
ELS_State:GetPropertyChangedSignal('Value'):Connect(function()
--[[
We have switched out the while true do to an event
Now it doesn't constantly run
It is much better for the overall game performance
]]
-- wait() is not needed anymore here
if ELS_State.Value == 0 then
-- Idle.
-- We don't need a loop here since this is idle.
elseif ELS_State.Value == 1 then
while ELS_State.Value == 1 do
game["Run Service"].Heartbeat:Wait() -- We used heartbeat:Wait() to prevent exhaution and prevented issues caused by wait()
-- Pattern 1
end -- Exits the loop if the condition is not met and move on
-- Function to reset all lights to zero here
elseif ELS_State.Value == 2 then
while ELS_State.Value == 2 do
game["Run Service"].Heartbeat:Wait() -- We used heartbeat:Wait() to prevent exhaution and prevented issues caused by wait()
-- Pattern 2
end -- Exits the loop if the condition is not met and move on
-- Function to reset all lights to zero here
end
end)
It’s totally not necessary to use a while true do break end
loop for the ELS patterns as if the condition is not met at the end of the loop, it exits automatically. cmon folks this is basic scripting knowledge
You might now say "oh I’ve heard that you can use the same while true do method but instead of using wait()
you can use heartbeat:wait()
or I can use coroutine.wrap()
". Now even though that will work better than wait()
, it will not be ideal for the sake of maximizing performance, and it is totally unnecessary to use those Heartbeat:Wait()
or coroutine.wrap
as the ELS script does not need to run 24/7, and is only required to actually display a pattern. Furthermore, overusing RunService.AnyEvent:Wait() can sometimes backfire on you in actual games, and will barely be any good from wait() as it is almost the same thing.
Conclusion
Is it a little more coding than while true() do
method that the ELS community is used to? By all means, I can’t disagree, it is, indeed, less code to type out, and less copying and pasting so on and so forth. Not only is while true do wait() end
loop a design issue, but it is also a performance issue. Pick whichever one you want: Continuing using while true do wait() end
or while wait() do end
loop and a laggy & inaccurate experience for you game, or take a little more time to set up the listeners such as ScriptSignal:Connect(callback)
or ScriptSignal:Wait()
, and have your game be a much smoother ride and less inaccurate simulation & less laggy experience. The choice is yours.
Update at Sept. 19, 2022
Read the added content at your will
If you really want a comprehensive solution towards game performance with ELS, since you don’t necessarily need ELS to be extremely-sharp-accurate, what you can do is to simply have the client do all the rendering lights work. How it essentially works is client fires control, server recognizes it, then fires all client to let players render the lights themselves.
“Well shouldn’t you not trust the client?”
Even though I’m not going to have a debate on it, here’re the things you can consider:
-
One, client-to-client interaction will regardless go through server, what’s malicious about server firing something back to client?
-
If we can let client do the rendering, we are going to open the server up for more demanding tasks such as firearm bullet casting, other security, etc.
-
ELS is such a small asset in your roleplay or driving game. Would you really consider putting ELS as a priority for your game and eating up a huge percentage of your task scheduler, or would you consider an asynchronous operation that has a difference of 0.1-1 second which will barely make any difference?
There’re many different ways to tackle this issue, but at the very least, stop using arbitrary waits.