Hello, my name is Ideal, i’ve made few tutorials about scripting in the past, but they mostly contained basic to intermediate knowledge and were discountinued
Because i never saw really more advanced topics on youtube, or at least not in single tutorial series, i decided to make this one
With time i’ll edit this post and add tutorials, it’s also easier to see how this doc is formatted
1. Module Scripts
In the first tutorial, you’ll learn about the key asset used to do anything, which is module script
Module Script is script which only runs when we require it, also it have to return something, otherwise it will error!
Example:
print("hi")
return nil --/ you need to add nil if you don't want to return anything!
require(script.ModuleScript) --> "hi"
As you can see, this is very usefull, because we can call the module from different scripts, without need of bindable events or events!
What else it can do?
Another thing it help us with, is practically the only way everyone else uses it, thanks to how table references work in Luau, we can use it to create something powerfull!
Because when we create a table, and try to create new variable based off that table, it wouldn’t copy it, it will give reference, this is why this:
local t1 = {Oranges = 5}
local t2 = t1
print(t1) --> {Oranges = 5}
print(t2) --> {Oranges = 5}
t1.Lemons = 2
print(t1) --> {Oranges = 5, Lemons = 2)
print(t2) --> {Oranges = 5, Lemons = 2}
As you can see, although we added lemons to table1, table2 also have lemons, because both variables point to the same reference, this is also why when we set t1 to nil t2 will still hold value, because those variables point to the table in memory, not copy it’s value!
And? What can we do with it?
Now we can use this to create shared tables, without Roblox’s global or shared, the only thing we will need to do is require module and return same table each time
local Table = {}
Table.Lemons = 2
return Table
local Table = require(workspace.ModuleScript)
print(Table) --> {Lemons = 2}
other script
local Table = require(workspace.ModuleScript)
print(Table) -> {Lemons = 2}
Now it’s very usefull i think
But we can not only read values from two different scripts, but we can also set them!
Table.Apples = 7
task.wait(1)
print(Table.Apples) --> 7
This is game changer, now not only can we share data between our scripts super fast, but we can also store important information outside of our main script, making it shorter and more redable
Note: Module Scripts share context with script that required them, this mean you can’t share data between client and server with them or to use parrarel Luau without main script and actors!
Example use case:
local LevelData = require(ReplicatedStorage.LevelData)
--/ Loading levels
Level.Timer.Value = LevelData.MaxTime
Level.Difficulty.Value = LevelData.Difficulty
Level.Reward.Value = LevelData.Reward
We assigned values to data saved in module, if we wanted to change it, we can simply change data in module, making it easy and fast
Now you understand that module scripts gave us a lot of opportunities, we’ll use them later to make your work go from impossible task to an relaxing and easy job
Thank you for reading, have a nice day, bye!
2. Code Organization
In the second tutorial, you’ll learn about simple tricks to make your code more redable on a logic level
Code Organization, combined with modular programming makes coding few times faster, refactoring became very easy, apart of that your game might benefit from lack of errors and performance boost
Example of bad code:
local RP = game.ReplicatedStorage
local c = game:GetService("CollectionService")
local scr = script
local p = script.Parent
local p1 = game.Workspace.Part
p1.Touched:Connect(function(p2)
if p2 then
if p2:FindFirstChild("Humanoid") then
if p2.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
end
end
elseif p2.Parent:FindFirstChild("Humanoid") then
if p2.Parent.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
else
print("")
end
end
end
end
end)
It must be very complex, this is why it looks like this
No, it’s not very complex, what this code does is set player’s health to 0 if he touched part in workspace, it works, but if we wanted to change anything, it’s impossible
First thing we’ll use is formatting, Luau does it automatically for you for most of the time, but if you still don’t use it, you need to press TAB after each scope opens and ends
Refactored code:
local RP = game.ReplicatedStorage
local c = game:GetService("CollectionService")
local scr = script
local p = script.Parent
local p1 = game.Workspace.Part
p1.Touched:Connect(function(p2)
if p2 then
if p2:FindFirstChild("Humanoid") then
if p2.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
end
end
elseif p2.Parent:FindFirstChild("Humanoid") then
if p2.Parent.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
else
print("")
end
end
end
end
end)
Why is there so many stuff to change one property?
This time we can at least read what this code does, but we came across another problem, most variable names don’t tell us anything, this is common mistake beginners make, use of shortcuts and their own naming, see having very long variables name isn’t that bad, it makes your script a lot better in the long run
Refactor:
local ReplicatedStorage = game.ReplicatedStorage
local CollectionService = game:GetService("CollectionService")
local Script = script
local Parent = script.Parent
local Part = game.Workspace.Part
Part.Touched:Connect(function(Hit)
if Hit then
if Hit:FindFirstChild("Humanoid") then
if Hit.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(Hit.Parent) then
game.Players:GetPlayerFromCharacter(Hit.Parent).Character.Humanoid.Health = 0
end
end
elseif Hit.Parent:FindFirstChild("Humanoid") then
if Hit.Parent.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(Hit.Parent) then
game.Players:GetPlayerFromCharacter(Hit.Parent).Character.Humanoid.Health = 0
else
print("")
end
end
end
end
end)
Now at least i know what instances are used
After giving our variables redable names, we can see that half of them are not even used, this is called You Ain’t Gonna Need That rule
Another thing we really don’t need is elseif statement, because Roblox’s characters have parts always under the main model, checking if any of those parts find Humanoid makes no sense
Last thing we don’t need is longer versions of getting some variables
Note: You should always aim to use same methood of getting variables, i couldn’t show it here that much, for instance ReplicatedStorage and CollectionService are required through different methoods, you should search which is best (In this case GetService is better)
Refactor:
local Part = workspace.Part
Part.Touched:Connect(function(Hit)
if Hit then
if Hit.Parent:FindFirstChild("Humanoid") then
if Hit.Parent.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(Hit.Parent) then
game.Players:GetPlayerFromCharacter(Hit.Parent).Character.Humanoid.Health = 0
end
end
end
end
end)
It’s 2x shorter now
But because we made it more redable, we can see more things we don’t need, like checking if Health is above 0 makes no sense, core scripts do it for you, or checking if hit exist, if you don’t destroy it, you can remove it too
Refactor:
local Part = workspace.Part
Part.Touched:Connect(function(Hit)
if Hit.Parent:FindFirstChild("Humanoid") then
if game.Players:GetPlayerFromCharacter(Hit.Parent) then
game.Players:GetPlayerFromCharacter(Hit.Parent).Character.Humanoid.Health = 0
end
end
end)
Good job!
It’s not the end, we can make it a lot more cleaner and simplier, how you may ask?
We will use guard clausess instead of if statements, they are one line statements that return if something is false, another thing we’ll do is replace PlayerFromCharacter in changing property to reduce complexity
Refactor:
local Part = workspace.Part
Part.Touched:Connect(function(Hit)
if not Hit.Parent:FindFirstChild("Humanoid") then
return
end
if not game.Players:GetPlayerFromCharacter(Hit.Parent) then
return
end
Hit.Parent.Humanoid.Health = 0
end)
Note: If your code is very simple, there is no need to make special variable for character or player, if there were more uses for them, you should definitively go for that
Good job now
We have last thing to polish, which is annonymous function, in more advanced systems those kind of functions might only help in very few scenarios, here we can create function and name it, this way we can eventually re-use it later
Another thing we’ll add is type check in function, this makes coding easier due to auto-fill and also tells you exacly what the variable should be
Polishing:
local Part = workspace.Part
local function onTouched(Hit: BasePart)
if not Hit.Parent:FindFirstChild("Humanoid") then
return
end
if not game.Players:GetPlayerFromCharacter(Hit.Parent) then
return
end
Hit.Parent.Humanoid.Health = 0
end
Part.Touched:Connect(onTouched)
Great job, now i can read this function!
Thanks
Note: I used camelCase for function name and PascalCase for other names, this makes code look better as every variable don’t have random upper and lower cases in them
Before:
local RP = game.ReplicatedStorage
local c = game:GetService("CollectionService")
local scr = script
local p = script.Parent
local p1 = game.Workspace.Part
p1.Touched:Connect(function(p2)
if p2 then
if p2:FindFirstChild("Humanoid") then
if p2.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
end
end
elseif p2.Parent:FindFirstChild("Humanoid") then
if p2.Parent.Humanoid.Health > 0 then
if game.Players:GetPlayerFromCharacter(p2.Parent) then
game.Players:GetPlayerFromCharacter(p2.Parent).Character.Humanoid.Health = 0
else
print("")
end
end
end
end
end)
After:
local Part = workspace.Part
local function onTouched(Hit: BasePart)
if not Hit.Parent:FindFirstChild("Humanoid") then
return
end
if not game.Players:GetPlayerFromCharacter(Hit.Parent) then
return
end
Hit.Parent.Humanoid.Health = 0
end
Part.Touched:Connect(onTouched)
Organizing your code, especially in bigger projects is a must, it boosts your coding speed and efficiency by a lot, making bugs rare and allowing you to quickly change it
Thanks for reading, have a nice day, bye!
3. Metatables
In the third tutorial you’ll learn about metatables
Metatable is a table attached to another table, it can contain special events called metamethoods, those can run the code when action is performed on a table
But before we will dive into metamethoods, you need to know how to create those metatables
local Table = {}
local MetaTable = {}
setmetatable(Table, MetaTable)
--/ Alternative: local Table = setmetatable({}, MetaTable)
Note: remember that table can have only one metatable!
Another Note: always remove metatable by using setmetatable(Table, nil) to avoid memory leaks!
To get metatable you can use this function:
local MetaTable = getmetatable(Table)
What are those metamethoods you’ve talked about?
Metamethoods are like events, they fire when
Here is the list of all metamethoods: Metatables | Documentation - Roblox Creator Hub
In this tutorial i’ll only give you example based off mostly used one, which is __index
local MetaTable = {__index = function(Table, Key)
print("Key:", Key, "not found in table:", Table)
end}
local Table = setmetatable({}, MetaTable)
local a = Table.a --> Key a not found in table {}
what happened?
See, when we perform any action on a table, it goes in this direction:
- Action is performed
- Compiler checks if action can be performed
- Compiler checks if there is metamethood that use this action
- Compiler returns value or an error
Each metamethood have __ prefix, and can be set to function or value
Note: __index fires when we index table with non-existant key, it can call function or return us value
In our case, we used a function, but you can also return value, another bonus is that if value is a table, it will perform a check on it, and if there is another __index, it will also fire if conditions are met
Next thing we need to talk about is rawset, rawget, rawequal and rawlen, those are lua functions that allow us to change or retrieve table’s index without firing metamethoods
Example:
local Table = setmetatable({}, {__index = 4}) --/ will return 4 if __index is called
local a = Table.a
local b = rawget(Table, "b")
print(a) --> 4
print(b) --> nil, __index wasn't fired and didn't returned 4
Is it usefull?
The use case is occasional, mostly for __index you’ll use set and get, but if you use other metamethoods 2 other might come in handy
Example:
local MetaTable = {__add = function(Table1, Table2)
local Sum = 0
for _, number in ipairs(Table1) do
Sum += number
end
for _, number in ipairs(Table2) do
Sum += number
end
return Sum
end}
local Table1 = setmetatable({4,5,2}, MetaTable)
local Table2 = setmetatable({1,3,8}, MetaTable)
print(Table1 + Table2) --> 23
Metatables are one of the things we will use later, to create more advanced stuff and make tables better tool to work with
Thanks for reading, have a nice day, bye!
4. Object Oriented Programming - Basics
In the 4th tutorial you’ll learn about Object Oriented Progragramming paradigm
Object Oriented Programming (OOP) is paradigm (Sort of a pattern, way of coding) in which all logic is hiden behind objects
What does it mean?
To make it simple, you can look at instances in Roblox, for example a Part
Part have propertiess like color, transparency, material, position, ect… but also behaviors like :Destroy(), :FindFirstChild(), :Resize() and so on
But what Part is?
it’s an Object
Because Luau isn’t OOP based langugae like C++ or Java, you don’t need to use it, it’s also not a magic tool to fix your code, it can help a lot, but only when used correctly!
So, if Part is an Object, what defines it, what really makes Part being object?
Each object is like a container, each methood and property that belongs to it is encapsulated (isolated) within the object
???
To make it cleaner for beginners, imagine very large storage with control panel, you can’t enter it because there are too many machines and all of that technical stuff, but you can access items via panel, and also perform actions
Let’s say you want to place crate of basketballs inside this storage, you place crate on conveyor belt and then press Store() on panel, crate is stored on some shelf, and is saved in panel’s database
Now let’s say you want to get this crate back, you press Get() on panel and enter Basketballs crate, after a while you can see it on conveyor belt next to you
But maybe you want to count all items of the same type? you press Count() on a panel and after a while you see number on display
Ok, what does it really mean?
We used methoods to interact with warehouse, we also changed it’s data by adding and removing crate of basketballs, apart of that we used methood to use the data inside our storage
Finally, it’s time to understand another concept
if Objects have propertiess and methoods, how do we create them?
Classes is an answer, they are patterns we use to create Objects
Important Note: Objects’ propertiess can vary, but behaviors of every object related to a single class are always the same!
How to make Object based off pattern? it’s probably bad to make thousands of new functions
Indeed it’s very bad to make a lot of new functions, because even the same two functions are separate things, we can use inheritance
Inheritance
First base for OOP is inheritance, it means that classes or objects can inherit propertiess or behaviors from other classes, to understand it better let’s look at Part and BasePart
Every Part is a BasePart, but not every BasePart is a Part, it’s like square and rectangle
Another thing they share in common, is that everything we can do with BasePart, we also can do with Part, MeshPart and Part share commonalities like Position, Color, Transparency and more!
Why Is A was highlighted?
Because this is what inheritance means, that something is a some other thing
And because Part is a BasePart, it should have all methoods and propertiess each BasePart have, this is what we can do, each Object should have Behaviors of it’s Class, and this way we can also later set propertiess via some special init methood
Best way to create inheritance, is to use metatables with __index
but how?
There is interesting behavior called Table cyclic reference, it means that table1 references table2 that references table1 that…
Because we can return any value via __index, we can return MetaTable itself
local MetaTable = {}
MetaTable.__index = MetaTable
If you remember, in last tutorial we’ve learned that if returned value is a table, compiler will search through that table to check if there is index or if there’s not, and even firing __index if we added it!
So we can return Class table with methoods, and when we index Object with a methood it will search through Class, and find it?
local Class = {}
Class.__index = Class
function Class.test()
print("hi")
end
local Object = setmetatable({}, Class)
Object.test() --> "hi"
Now it seems very usefull
In OOP we can use special function called constructor that creates Object, each Class have this kind of methood, usually it’s called .new() or .create()
Example:
local Class = {}
Class.__index = Class
function Class.create(Name: string)
local Object = {}
Object.Name = Name
setmetatable(Object, Class) --/ We inherit methoods
return Object
end
function Class:printName()
print(self.Name)
end
return Class
local Class = require(script.Class) --/ Module scripts are best way to store classes
local Object = Class.create("Special Part")
Object:printName() --> "Special Part"
What is self and why not dot but semicolon?
This isn’t strict OOP, but what semicolon makes, is that if function is stored in a table, and we call Table:Function(), we can use self argument, which is simply Table that references this function, in our case Object is this table, because we used it to reference printName()
Non-OOP Example:
local Table = {}
function Table.test(self) --/ If you defined function via dot, you need to put self as first argument
print(self.Text)
end
Table.Text = "Object Oriented Programming"
Table:test() --> "Object Oriented Programming"
function Table:test() --/ You don't need to put self here
--/ Code
end
--/ Code
Table:test() --> "Object Oriented Programming"
OOP is great way to organize your code, it may cost a little bit more memory to use, but really this isn’t the case, in next tutorial i’ll dive deeper into when and how to use it correctly
Thanks for reading, have a nice day, bye!
5. Object Oriented Programming - Concepts
In the 5th tutorial, you’ll learn about OOP principles
Note: This tutorial can be skipped as it’s not required for OOP to work, but it may benefit you with usefull knowledge to make your code better
Abstraction:
Abstraction is process of simplifying your interaction with code by hiding unnecesary stuff
What does it mean?
It means that everything you don’t need to know, is not visible when you use it
Example:
function Class:findFirstObject(Name: string): Object?
for _, Object in self.Objects do
if Object.Name ~= Name then
continue
end
return Object
end
end
local Car = Class:findFirstObject("Car")
Car:drive(10)
In this example, we want to find object with name of Car, but we really don’t need to know how we will find it, we only need to know that we give Name and it should return Object or nil
Polymorphism:
Polymorphism means that one thing can have many forms/variations of it
Umm?
This one is probably strangest one, but it’s also very usefull, let’s say we have 3 classes, one is Car, second is Tank, third is Vechicle
Vechicle class have methood called Drive(), but tank and vechicle need to use it differently, and by differently in behavior, not data like speed or something else
Let’s say that Tank can drive only when turret’s angle relative to movement direction need to be less than 20 degree
Note: Class from which other class inherits is called super-class
This is where we can use combo of Inheritance and Methood Overwriting to achieve polymorphism, see __index fires when index is nil, and this is how we can implement our inheritance, because if Class don’t have our methood, but it’s super-class have one, metamethood will return it
Example:
function Vechicle:drive(Distance: number)
self.Distance += Distance * self.Speed
end
function Tank:drive(Distance: number)
if self.TurretAngle > 20 then --/ Different behavior than in :drive() methood
return
end
self.Distance += Distance * self.Speed
end
And now we can create two vechicles
local Car = Car.new(5) --/ Speed 5
local Tank = Tank.new(4) --/ Speed 4
Tank:rotateTurret(25)
Car:drive(10) --/ Car moved 50 studs
Tank:drive(10) --/ Tank stayed in place
Nice
Important Note: Remember that polymorphism should only be used when behavior differs, not propertiess! if you have methood to add 10 points and other methood to simply add 20 points, simply make variable addAmount
Encapsulation:
Encapsulation means that we can’t directly access object, but rather use interface to do it
What? Like you can’t edit objects?
Not exacly, encapsulation tells that we shouldn’t directly change anything, rather use methoods to do that, in some languages, especially Java it’s even more advanced, as you have to create getters and setters for everything
Note: Getters and Setters are methoods that usually return or set value of our object, they are very good to have if we use object in many places, as one change require us to change one module instead of every script with reference to it
Example of getters and setters:
function Class:setA(Value: number?)
self.A = Value
end
function Class:getA(): number?
return self.A
end
Those 3 concepts + inheritance are called 4 principles of OOP, they are important as they allow programmers to create powerfull tools and make work a lot easier
Thanks for reading, have a nice day, bye!
6. Composition
In the 6th tutorial, you’ll learn about Composition
Composition means that code is composed off, can use something, has something
So what it does?
Composition means that we can use function/methood to process our code, but our code don’t rely on this function, making it a lot more flexible solution than alternatives
Example:
function openCarDoors(Car: Car)
for _, Door in Car.Doors do
Door:open()
end
end
Looks like OOP without self
Composition is somewhat detached OOP, but it can be combined with it, creating interesting effect
To understand it better, we’ll compare Composition to Inheritance
First of all, Inheritance works on metatable __index, when Composition is simply function detached from the Object we currently use
Because we can have only one metamethood per table, this mean we can have only one super-class
Let’s try to create 3 classes, Car, Tank and Vechicle
Vechicle:
- Can drive
- Have engine
- Need fuel
Car:
- Can drive
- Have engine
- Need fuel
- Have doors
Tank:
- Can drive
- Have engine
- Need fuel
- Have turret
It’s easy, use polymorphism + inheritance
Good point, but let’s say we had an update that added boats to our game
Boat:
- Can swim
- Have sail
Use inheritance, it wouldn’t be that bad if we called swimming function Drive()
And here you start seeing problem, all of those vechicles have different variations, both in core methoods and addons, of course inheritance and polymorphism can help but when you add more and more, it will become unredable and hard to change
This is why composition is mostly superior to inheritance, now imagine we do this:
Car:
- Can move
- Have engine
- Have doors
Tank:
- Can move
- Have engine
- Have turret
Boat:
- Can move
- Have sail
Engine:
- Need fuel
Doors:
- Can be opened
Sail:
- Can be unfurled or furled
Now we have 2x more classes
We created 3 new classes, they have their own functionalities, and now instead of inheriting engine or doors or sail from Vechicle class, we simply made each vechicle have needed parts
Note: I know i could have made it unclear, so to make it shorter, at the beginning each class inherited components from super-class Vechicle, but when we added more vechicles with different functionality it became harder to implement new methoods, this is why we created classes for
each component we needed, apart of that we turned Can sail and Can drive methoods into Can move to make it more redable
Note: You can merge inheritance and composition, it’s nothing wrong, i’ve did it here with using inheritance for Can move and composition for vechicle components
Example:
local Functions = require(script.Functions)
local Car = {}
Car.move = Functions.move
return Car
local Car = require(Modules.Car)
Car:move() --/ If move in Functions was declared with semicolon you can use self
Example:
function Car.new(Speed: number): Car
local self = setmetatable({}, Car}
self.Distance = 0
self.Speed = Speed
self.Engine = Engine.new()
self.Door1 = Doors.new()
self.Door2 = Doors.new()
self.Engine:refuel()
return self
end
function Car:move(Distance: number)
if not self.Engine:isOn() then
return
end
self.Distance += Distance * self.Speed
end
local Car = Car.new(5)
Car.Engine:turnOn()
Car:move(10) --/ Moved 50 studs
Composition is great tool to make your code a lot more scalable and easier to use
Thanks for reading, have a nice day, bye!
7. Memory Leaks
In the 7th tutorial, you’ll learn about Memory Leaks
Memory Leak is a piece of memory (data saved by program) that cannot be removed from registry (place where memory is stored, there are milions of them in each computer), causing performance issues
A lot of technical stuff
To make it simplier imagine you have a tank, and water is flowing into it, one day you forget to turn off tap, after a while water will simply fill the box and start to pour out
Of course we want to prevent water pouring out, so we will turn off water tap or make complex mechanism to remove water from a box, but the problem stays the same
Memory leak happens, when we create something and don’t remove it properly, by removing, i mean clearing all Strong References, in Luau case, there are only strong references, apart of weak tables made by using __mode metamethood
They usually happen with Roblox’s Objects, as they need to be properly removed, by calling :Destroy() or :Disconnect() and have no reference in table
Example:
local Part = workspace.Part
ModuleScript[1] = Part --/ Inserting Part to a module script
Part:Destroy() --/ Part is destroyed, but it still have reference in ModuleScript
--/ Scope ends, but Part cannot be Garbage Collected, reference still exists!
Ok so like we need to clear all references?
Now, let’s see another part of code
--/ Different script
task.wait(2)
ModuleScript[1] = nil --/ Part's reference is removed, not it can be freed
--/ Garbage Collector freeds part, memory leak will not happen
Note: Remember that when variable is going out of scope there is no reference to it in that scope, this mean that it can be freed by GC
Connections can also leak memory, here is example:
local Connection = game:GetService("RunService").Heartbeat:Connect(function()
--/ code
end)
Because connection isn’t disconnected, it will hang around, if there will be a lot of those connections, they’ll cause memory leak over time
Fix:
Connection:Disconnect()
Note: You can use :Once() to make function disconnect after being called once
Best practices
- Add :destroy() methood to OOP objects
function Object:destroy()
self.Property1 = nil
self.Property2 = nil
table.clear(self.Array)
self.Array = nil
setmetatable(self, nil)
end
- Always disconnect connections when not needed anymore
local Clicks = 0
local Connection
local function clickTenTimes()
if Clicks >= 10 then
if Connection then
Connection:Disconnect() --/ We don't need this connection anymore
end
return
end
Clicks += 1
end
Connection = workspace.Part.ClickDetector.MouseClick:Connect(clickTenTimes)
- Use :Destroy() on instances you don’t need anymore
- Destroy Automatically calls itself on every instance
- Destroy disconnects connections related to destroyed instance
- Test and measure, it’s the best way to find out about memory leaks
- Use Luau Heap
- Use Memory Category
Notes:
-
There are some under-the-hood features that might look like memory leak, usually they are optimizations, you shouldn’t worry about them much if you find one
-
Remember that tables are the hardest part to deal with if we talk about memory leaks, you should use modules like
-
If your game design makes long-term memory leaks impossible, then don’t try to fix them if it’s vey hard, games with separate places, like tower defenses shouldn’t have much problem handling small leaks
There are few topics that discuss more about Garbage Collector and Memory Leaks, links below:
A Beginner's Guide to Lua Garbage Collection
Garbage Collection and Memory Leaks in Roblox - What you should know
How to use a Maid class on Roblox to manage state
Memory leaks are usually hard to deal with and overtime can ruin your game, after this tutorial you should know what are they, and how to deal with them without shutting down servers or limiting experience
Thanks for reading, have a nice day, bye!
8. State Machines
In the 8th tutorial, you’ll learn about State Machines
State Machine is system in which logic is based off current states, states can change depending on given factors
So like if statements?
If statements are main part of those systems, but the idea is slighty different
function Class:use()
if self.State == "using" then
return
end
if self.State == "idle" then
self:doThat()
end
if self.State == "working" then
self:doThis()
end
end
function Class:doThat()
self.State = "using"
task.wait(1)
self.State = "working"
end
function Class:doThis()
self.State = "idle"
end
Oh it’s simple, like we check if we can do something and we decide based off state
States are very simple concept, they allow you to control your game, combined with OOP and Events, they become great to sort logic
Thanks for reading, have a nice day, bye!
Summary
Thank you for reading this tutorial, i wish you have learned a lot from it and now your code a lot better, i might be working on other tutorials in the future
Thank you again, have a nice day, and good luck!