A guide on how to create classes in Lua
An in-depth tutorial by @maddjames28.
Before we start, I would like to state that Lua is not meant to be an OOP language. Instead we implement custom classes with metatables.
Introduction
Hey! You probably came here because you saw some developers talking about ‘OOP’ or ‘Classes’ or even ‘Objects’. O.O.P. stands for Object Oriented Programming. A lot of programming languages use OOP as it is extremely useful in a lot of cases.
What are they, and what do they do?
Classes can be useful when you need data that does not persist throughout the running environment. Usually, you will have your script that makes local variables like this:
local var = 1
local var2 = "Hello, world!"
local var3 = true
But sometimes, this behavior of persisting variables is not always desired. This is where functions come in. Functions can take parameters (arguments) which are basically like variables but they get passed into the function for use like this:
local var = 1
local table1 = { }
local function myPrint(parameter)
print(parameter)
table.insert(table1, parameter)
end
local function findPrint(parameter)
return table.find(parameter)
end
myPrint(1)
findPrint(1)
Now this works, but it can start to be annoying to supply the same parameter every time for the same thing, especially if your module is open sourced. This is where classes/objects come in. Now, you are probably familiar with Roblox’s objects, for example, Parts
or Scripts
. Each one of these objects have properties and extra functions connected to them. In Luau, you can create your own custom objects with code (quite easily actually). Now, lets get to the main point of this tutorial: learn how to make classes.
How do I make them?
Making classes is quite simple actually, and the possibilities are almost endless. I will be covering more advanced topics at a different time, but here are the basics.
First to create a class, you need to insert a ModuleScript
via a plugin or the object explorer.
In your module, you first need to create a new function.
local module = {}
function module.new()
end
return module
Now, this is where metatables
come in. They are what make classes actually work.
local module = {}
function module.new()
local myClass = setmetatable({}, module)
return myClass
end
return module
Now, we need to add our __index
:
local module = {}
module.__index = module
function module.new()
local myClass = setmetatable({}, module)
return myClass
end
return module
Basically, metatables
have these things called metamethods
, which are basically functions that run when said thing has happened to the table. In classes, we use the metamethod __index
, which fires whenever you try to index anything from the table. Here, we have it returning the module
when we attempt to index it.
Now you can add properties to your object, just like how Roblox has a Name
property on every object. Here is how can add them:
local module = {}
module.__index = module
function module.new()
local myClass = setmetatable({}, module)
myClass.Property = "Hello, world!"
return myClass
end
return module
Now, if we call this function in our module, we can do this:
local module = require(path.to.module)
local myObject = module.new()
print(myObject.Property) -- output: "Hello, world!"
Pretty cool right? Well, the next part is even cooler!
So now we how to add properties to our classes, but how do we attach functions like Roblox does with their objects? Here’s how: So in Lua, there’s this variable called self
. It is basically they keyword that we use to access properties in our classes from functions that are attached to them. All we need to do is add another function!
local module = {}
module.__index = module
function module.new()
local myClass = setmetatable({}, module)
myClass.Property = "Hello, world!"
return myClass
end
function module.ObjectMethod()
print(self.Property)
end
return module
Now, if we call that function we should be able to print the property - but it doesn’t, and instead it errors. Why? This is because you must supply self as a parameter or else the function won’t know what object to look for. You can add it by doing this:
function module.ObjectMethod(self)
print(self.Property)
end
Now try doing this in your script:
local module = require(path.to.module)
local myObject = module.new()
myObject.ObjectMethod(myObject) -- output: Hello, world!
But, this can start to be annoying when you have a lot of methods. How can we fix this? Well, you simply call the function with a colon in your module like this:
local module = {}
module.__index = module
function module.new()
local myClass = setmetatable({}, module)
myClass.Property = "Hello, world!"
return myClass
end
function module:ObjectMethod()
print(self.Property)
end
return module
Now, you no longer need to supply self/the object because when using a colon, self is automatically supplied as an invisible parameter, and you no longer need to worry about supplying it yourself.
So, if we do this in our script:
local module = require(path.to.module)
local myObject = module.new()
myObject:ObjectMethod() -- output: Hello, world!
We should still get the same result as if we were doing the other method. Of course, you can add as many properties and methods as you want.
But sometimes, maybe you want to have different properties for each object. We can still use parameters as before with regular functions. Here is an example where we use parameters, and other methods to create a ‘People’ class with each object being a ‘Person’.
local module = {}
local people = {}
module.__index = module
function module.new(name, age, gender)
local People = setmetatable({}, module)
table.insert(people, name)
People.Name = name
People.Age = age
People.Gender = gender
return People
end
function module.GetPeople()
return people
end
function module:GetPerson()
local compiledPerson = {Name = self.Name, Age = self.Age, Gender = self.Gender}
return compiledPerson
end
function module:RemovePerson()
table.remove(people, table.find(people, self.Name))
setmetatable(self, nil)
end
return module
Here is one last piece of advice: always make sure to type check parameters to ensure safety. Type checking is basically doing this: local var: number = 1
.
What happens is if var
is not a number, the script will yell at you telling you it should be a number.
Here’s an example of it in a function:
local function myFunction(parameter: string)
return type(parameter) -- should always return "string" no matter what
end
What’s next?
You can do way more things with classes than I have mentioned here. Go checkout other tutorials about OOP and maybe you’ll learn.
Anyways, I hope you have learned something, and thanks for taking the time out of your day for reading my post!
Comment on what I can improve or what you liked about the tutorial.