The Book Of Optimisation


INTRODUCTION


This topic will go through optimising your game and will talk about 3 different things you need to optimise, these 3 things being:

  • Scripting

  • Building

  • Animating

Also, if your servers are crashing with obnoxious lag spikes, this is most likely a DDoS attack which I cannot prevent.


PROBLEMS & SOLUTIONS

Let’s start with the simple, connections.


1. PROBLEM - CONNECTIONS


Leaving connections running for too long will eventually impact your performance. This creates unnecessary data usage and is not very ideal.


SOLUTION


Though, a simple disconnect can end this problem easily and leave no trace behind. This is a highly recommended practice!
This, is an example I’ve created where this gets used:

local event -- Variable created.
event = game.Players.PlayerAdded:Connect(function(plr) -- Variable assigned to a connection, player added.
	script.Parent.Touched:Connect(function(hit) -- Touched Connection.
		if hit.Parent == plr.Character then
			print("epic")
			event:Disconnect() -- Disconnects the connection, leaving no trace.
		end
end)
end)

2. PROBLEM - UNIONS


The most obvious problem that pretty much everyone knows of, UNIONS. Now, I’m not telling you to outright never use them again, I’m just saying DO NOT RELY ON THEM! Firstly, they can corrupt very easily and completely get wiped off. Secondly, they have too much triangles which can stress the server and lead to degraded performance. Lastly(correct me if I’m wrong), unions are uploaded as an asset from the website, so too many is lots of data being sent and therefore, degrading your performance.


SOLUTION


Now, a solution to this would be using a plugin and/or using a software like blender to build. If you need a circle, a plugin is a lot more ideal than unions. Now, if you need complicated things, you may want to try learning how to build in other software. This, I know is either said than done, but you’ll eventually do it.


3. PROBLEM - TOO MANY ANIMATED PARTS


Too many animated parts at once will stress the server. A game I know that has this problem is this game. The problem with it is that there’s too many animated bees and public servers are awful in performance. Private servers or finding small servers are the only options the community can resort to. Too many animated parts at once will stress the server if performed on all clients. If your game has this issue, I have an obvious solution to propose.


SOLUTION


Obvious solution is to add a toggle whether to show other’s their animated parts. I’m referring to ‘pets’ or client’s animated parts in this instance. If your game has too many parts that belongs to the server, I will eventually update the topic to cover that.


4. PROBLEM - FORGETTING TO REMOVE PARTS


Now lets say you have a bow and arrow in your game and everybody shoots it at the incoming enemies. These parts can eventually be a problem as they rise in increments, but a solution to that is very obvious.


SOLUTION


After a few seconds, DESPAWN THE PARTS and just a reminder, please don’t do this to despawn a part:

part.Parent = nil


5. PROBLEM - INSTANCE.NEW PARENT PARAMETER


The problem with the parent parameter or using parent first thing before the properties is that the script has to check for the part to insert the property, a lot of these in your scripts can impact your performance.


SOLUTION


Solution to this is to set parent’s parameter after all other properties have been set.


6. PROBLEM - USING TOO MANY WHILE TRUE DO LOOPS

Using too many loops is a terrible coding practice + performance practice. Using too many loops can eventually impact your game performance! Always important to not use too many. I’m not saying you should fully stay away from while true do loops, they’re incredibly useful in some situations but if you don’t need to use it, don’t use it.


SOLUTION


If you’re using loops to spin a part, there’s an alternative to that and that’s Tween Service. If you’re using a loop to check if a part has changed, use .Changed or :GetPropertyChangedSignal. If you’re using loops for anything else, you could also use RunService along with it’s useful properties.


7. PROBLEM - GLOBAL VARIABLES


Global variables are a more memory intensive than local ones. Now don’t use a lot otherwise, this would only be a micro optimisation.


SOLUTION


Don’t use too many global variables.


8. PROBLEM - USING FINDFIRSTCHILD IN UNNECESSARY CASES!


FindFirstChild takes 20% longer than using dot operator and takes 8x longer for simply referring to the part, FindFirstChild shouldn’t be used in every case but rather where it needs to be used. These are 2 examples, example A where you don’t need FindFirstChild and example B where you can use FindFirstChild:

EXAMPLE A:

local part = workspace:FindFirstChild("optimisationpls")
print(part.name)

EXAMPLE B:


script.Parent.Touched.Connect:function(hit)
if hit.Parent:FindFirstChild("Humanoid") then
print("optimisationpls")
end
end)

SOLUTION


If you need to wait for a part to load in, use WaitForChild, if you need to reference a simple object, don’t always use FindFirstChild for it unless necessary.


BONUS TIPS


Running speed tests is essential for checking memory and such, and I will teach you how! First, add whatever code you want to this function:

local function TestFunction()
print("optimisation pls") -- my code example
end

Then, start the time:

local Start = tick() -- Starts the time in seconds

Next, put your for loop:

for i = 1,1000 do -- runs the function 1000 times in an increment of 1
TestFunction()
end

Finally, end the time:

print(tick())

Also, it’s quite important to mention to check memory, you can type /console in chat or press F9 for the dev console. You can also go to the Roblox menu - settings - performance stats - On to also check your memory.

Please give me suggestions and any problems for me to solve in this topic and also give feedback.

Rate my topic!
  • Rubbish, gave me no information
  • Bad, gave me little information
  • Decent, gave me quite a bit of information
  • Good, it has a good amount of information
  • Great, gave me so much information

0 voters

18 Likes

I think you need to be clearer about the optimization effects of each of these lines. For example, managing and cleaning up connections, at least in very large projects with lots of connections, going to yield a better performance impact than cleaning up the same number of FindFirstChild misusage. FindFirstChild might be 20% slower than using a dot operator - but if the time it takes for a dot operator is (example figure) 1e-6s then that’s a marginal performance enhancement. Again, this isn’t to say that it’s bad advice - people should use FindFirstChild when it’s necessary, but it would be helpful to differentiate the different performance impacts of each line item, since they do vary.


I think I get what you’re saying, but broadly speaking this isn’t accurate. Not all loops are equal, nor are their applications always correct.

If I’m using an extended pairs loop on a table, that cannot be replaced with TweenService or an event. Not everyone is going to know this, so it’s important to be specific about when those replacements should occur.

More broadly though, it’s important to be clear that loops are not a bad thing. There are all sorts of effective uses for them.

Yes, when people are doing

while task.wait() do
     --// Code
end

That should absolutely be replaced by an event like RenderStepped, Heartbeat, etc. But it would be a mistake to characterize all loops like this and to cast general solutions that, in reality, have specific conditions for which they ought to be used. Again, totally get where you are coming from, but less experienced programmers might see this and get really strung out, because the use-case isn’t specific enough.

I definitely should have depicted that in a better way and I totally understand what you mean by this.

I also completely get what you mean by this, I am looking forward to implement these suggestions.

1 Like

till you realize that those events don’t fire for certain instances like Humanoid because of Roblox’s spaghetti code

2 Likes

Does anyone have any problems I could add to this!

You can change that, to this:

local event = game.Players.PlayerAdded:Connect(function(plr) -- Variable assigned to a connection, player added.
	script.Parent.Touched:Connect(function(hit) -- Touched Connection.
		if hit.Parent == plr.Character then
			print("epic")
			event:Disconnect() -- Disconnects the connection, leaving no trace.
		end
end)
end)

Another performance degrading thing I commonly see is using spawn() or delay() functions to make and delay threads. While this DOES make a new thread it also degrades performance by a lot.

You are way better off using coroutine.wrap or task.spawn, which you can find more info on here:
https://developer.roblox.com/en-us/api-reference/lua-docs/coroutine
https://developer.roblox.com/en-us/api-reference/lua-docs/task

2 Likes

That’s weird, last time didn’t work.

Works fine for me.

local event = game.Players.PlayerAdded:Connect(function(plr)
	print(plr.Name)
end)

image

By the way, you didn’t mention that it’s bad to drop FindFirstChild , it could also be a good optimization.

1 Like

This example isn’t using the local event in the connection body.
I could see it being problematic as the variable isn’t created and assigned when the anonymous function(plr) is created, thus it cannot access the event variable.

try and you’ll see that event is nil

local event = game.Players.PlayerAdded:Connect(function(plr)
	print(event.Connected)
end)
1 Like

Ahh this makes more sense, thank you.

I have a cleanup function for events that is outside of the connection scope, so that’s why this didn’t cross my mind.