Hello, as a Roblox Developer I believe that I should go above and beyond and learn Lua Functions or Roblox incorporated functions to boost my knowledge and to overall strengthen my learning abilities and concepts of scripting. I would like to know what types of uses Metatables can provide, what they’re used for and what type of scenarios (cars, etc) need them.
I personally use them for OOP (there’s a good tutorial of it on the dev forums here). There’s a lot of useful stuff to them specifically relating to that - like creating your own classes & defining how those should work.
The link to the OOP guide:
I mainly used them when I am scripting OOP classes that need to inherit some attributes from its super class for example:
local SubClass = {}
SubClass.__index = SubClass
setmetatable(SubClass,{
__index = SuperClass --This will allow the subclass to inherit functions from the superclass
})
A good example of using metatables: Parties (like in RPG games)
You have a “leader” variable, a “members” array, some methods such as RemoveMember, AddMember, ChangeLeader, etc.
You can do all of this and make each party an object.
Something like this:
local party = PartyModule.new()
party:SetLeader(player1)
party:AddMember(player2)
party:AddMember(player3)
for _, member in next, party:GetMembers() do
member.Character.Humanoid.Health = member.Character.Humanoid.MaxHealth
end
party:RemoveMember(player2)
You use them in OOP Lua all the time! Say I have a Vector4 class as defined below:
local Vector4 = {}
Vector4.__index = Vector4
function Vector4.new(x, y, z, w)
local self = setmetatable({}, Vector4)
self.x = x
self.y = y
self.z = z
self.w = w
return self
end
function Vector4:__tostring()
return ("Vector4 { %d, %d, %d, %d }"):format(self.x, self.y, self.z, self.w)
end
Every time I call Vector4.new, I get a table in return, with the Vector4 class as the metatable. This meams we can declare metamethods directly inside the class - for example how I did it with __tostring. Also, I declare __index at the top to point at the class, which allows us to access the functions of Vector4 through that returned object.
Thus, I can do this:
local vec = Vector4.new(1, 2, 3, 5)
print(vec) --> Vector4 { 1, 2, 3, 5 }
and it works as expected. It gets juicier when it comes to operator overloading, too!
function Vector4:__add(other)
-- code that creates a new vector4 by adding this vector4 and the given other vector4
end
...
local v1 = Vector4.new(1, 2, 3, 4)
local v2 = Vector4.new(2, 4, 6, 8)
print(v1 + v2) --> Vector4 { 3, 6, 9, 12 }
What? That has nothing to do with metatables.
Except it does.
I didn’t explain how the actual methods work but here’s an example:
function party.new(player)
local self = setmetatable({}, {
__index = party,
__tostring = function(self)
return self.leader
end;
});
self.leader = player
self.members = {[player] = true}
AddParty(self)
return self
end
--=========//=========//=========//=========//=========//=========//
-- Set methods
function party:ConvertMembersListToArray()
local tb = {}
for member, _ in next, self.members do
tb[#tb+1] = member
end
return tb
end
function party:AddMember(playerToAdd)
if not game:GetService("Players"):FindFirstChild(playerToAdd.Name) then
return "Player doesn't exist!", false
end
if self.members[playerToAdd] then
return "Player is already in party!", false
end
if self:GetMemberCount() >= 4 then
return playerToAdd.Name .. " tried to join. Party was full!", false
end
self.members[playerToAdd] = true
return playerToAdd.Name .. " joined the party!", true
end
function party:RemoveMember(playerToRemove)
if self.members[playerToRemove] then
self.members[playerToRemove] = nil
PartyRequester:FireClient(playerToRemove, "LeaveCurrentParty")
end
if self:GetMemberCount() == 1 then
self:RemoveMember(next(self.members))
return
end
if self.leader == playerToRemove then -- if the player who left is the leader of a party, we need to change the party's leader or delete the party
ServerComm:Fire("PartyLeaderLeave", playerToRemove)
if self:GetMemberCount() > 0 then -- if there are other members, need to change the leader
local newLeader, _ = next(self.members)
self.leader = newLeader
---- If there's only 1 member left, just remove from party ----
PartyRequester:FireClient(newLeader, "Notification", "You are the new party leader!", true)
else
self:Destroy()
return
end
end
end
Note: This is all inside a module
The __tostring
which you didn’t show at all in your code-sample, sure, but instead of the __index = party
you could just as easily use a deep copy function which is what lots of modules/classes do.
Yes, I know I didn’t explain it, but he specifically said:
I gave an example of what metatables could be used for, and the first thing that came to my mind was parties. I used metatables for my party system and I believe it’s a pretty straightforward example of how metatables can be used for OOP.
Hello, thank you for all of your great replies! It has caught my interest with some of the posts you guys made. Majority of the stuff I’m hearing is OOP (Object Orientated Programming) Which I have done very little programming in Java which is a OOP language so I think I have some knowledge around that topic, however, there are some things that I am still confused about like self.Value
I do not understand how or why you should use self
for, can someone please give an explanation on this?
So real quick, self
arguments are sugar syntax for including the table that the function came from as the first argument.
So this:
Object:Method(a, b)
Would be the same as this:
Object.Method(Object, a, b)
And whenever you are constructing functions, this:
function Class:Method(a, b)
-- code here
end
Would be the same as this:
function Class.Method(self, a, b)
-- code here
end
Metatables are preferable to deep copying. I’m not sure what you mean when you say most modules do it - deep copying would introduce a lot of overhead compared to metatables when you instantiate objects?
Isn’t it more memory efficient to use .__index instead of creating deep copies when creating classes in lua?
(also: @Elttob)
I wasn’t saying that it was a bad idea to use __index
for this, just that his original code was unclear where/why he would use metatables. In terms of performance, it doesn’t really matter which way you use if you don’t have very many functions (which this example didn’t).
Metatables in summary are great for OOP. In other words, you create an object with variables, functions but also special functions(metamethods(?)).
Scenarios include:
- Cars
- Parties
- Anything that is a template of something
Classes are somewhat like a template for things in non-technical terms.
the self parameter is just basically passing the entire table itself for example say you have a OOP class like this:
local CarClass = {}
CarClass.__index = CarClass
CarClass.new = function(Name,Speed)
CarClass.Name = Name
CarClass.Speed = Speed
end
CarClass.GetDeltaSpeed = function(self,newSpeed)
--the self argument means we are taking the table as a argument too
--so now we can simply do this
return newSpeed - self.Speed--the original speed was assigned in the new function above
end
Once again, thanks for all of the replies! By posting this thread I have learned some usages of Metatables, like OOP (Object Orientated Programming) and I have to say OOP is really useful! I have also learned from my experience (testing that i’ve done for some time) that self
is basically a value of the object you called the function on? Like the Speed Changer function used in the OOP Lesson in the first reply of this thread. Correct me if I’m wrong please.