Bookmarked! Now this is a useful post.
Yeah not really a word to take. Handling property changes in a loop is not efficient no matter what language you write it in. Roblox most certainly has some kind of internal system for queueing events or similar whenever something that is supposed to invoke them happens. .Changed
won’t be looping to constantly change for property changes.
I knew it. Who knows, maybe they have a proxy table setup?
Yes, you got the right idea. Some minor corrections: __index
is the metamethod, it can be set to a function, and yes it invokes, run or fires or any other word you find suitable, when you index a key or index inside of a table that didn’t exist, meaning they are nil.
I’m glad you find yourself more powerful knowing this knowledge! The thing is, metatables aren’t really used for a whole lot when it comes to common use. There a lot of controversial topics talking about how it’s misused. Performance-wise, I wouldn’t argue that they make a difference, at the end they’re just a feature of lua, they weren’t original written to make your code more performant. I guess the only example where I would thing metatables make a significant change in performance is when dealing with OOP. Some people most of the time say “metatables are the only way to implement object-oriented programming in lua”, this is wrong, OOP is just lua’s table magic. The only thing that metatables do is make inheriting methods from the class easier. Instead of having to re-create the methods each time we create an object.
local class = {}
function class.new()
local object = {name = "hi"}
function object.SayHi()
end
function object.SayBye()
end
return object
end
This is bad for performance, imagine creating a hundred of these objects, and having 2 functions for each object, that’s 200 functions created! With metatables, you simply keep the methods in the class, and you inherit them from the object, and no matter how many objects you make there is still 2 methods.
local class = {}
function class.SayHi()
end
function class.SayBye()
end
function class.new()
local object = {name = "hi"}
setmetatable(object, class)
return object
end
The thing is, as the topic that I linked by ScriptOn, don’t force yourself to use metatables just because they are cool and using them seems like a good idea. Just use them whenever you need to. That’s the thing.
When working on games, you’ll find youtself using metatables when dealing with modules sometimes.
Oh nice, honestly I think I could use metatables for player data and AI handling stuff.
I keep track of NPCs or custom humanoids in a table, figured out I can use metatables to turn my custom humanoid into some kind of Roblox object that I can call functions on.
And can also use metatables to return place-holder values in case player data is loading and doesn’t exist yet or such.
Little question, if I were to call object.SayHi()
in your last code sample.
Would the first param be a table, second the key and the third one be a optional/custom?
It’s not really super clear yet to what values/params are passed when a function is called.
local class = {}
function class.printthis(tab, key, val)
print(tab, key, val)
end
function class.new()
local object = {name = "NewObject"}
setmetatable(object, class)
return object
end
So if I now call object.printthis()
, is tab the table, key the name/index of the key and val is the value of that key, does it print that and any param that comes after is like extra or? How would this work or how could I make that work?
Sorry for the confusing code. Also I made a mistake of doing setmetatable(object, class)
instead of setmetatable(object, {__index = class})
.
From what I understand your question is, if extra values are passed beyond the 3 parameters, what happens? Well basically nothing! They are just ignored/discarded.
function class.printthis(tab, key, val)
print(tab, key, val)
end
local t = {x = 5}
class.printthis(t, "x", 5, true)
--that true there doesn't actually cause any errors or mess with anything!
What I actually mean is, when a function is called, does it automatically already pass on which table and key you called the function upon?
I just do class.printthis()
with nothing inbetween the ()
.
function class.printthis(tab, key, val)
print(tab, key, val) --Will this print the table, key and value
--even when nothing is passed through class.printthis()?
end
Does it automatically fill in the tab, key and val parameters?
Or would I have to add those myself? How would that work?
Of course you have to fill those yourself! If you’re looking for something that passes them for you, try __tostring
, this method actually triggers when you pass a table (with a metatable with __tostring in it) print
. And will pass you the table in the function __tostring
is set to, and that way you could also yoink it’s key and value. That way you won’t even need to make a function to print this, it just gets printed using print
.
local metatableOfObject = {__tostring = function(t) return t, t.key end}
Although this way you can’t know which key to return and also there may be occasions when you just want to tostring
a table (because obviously __tostring
fires for that too.
Honestly don’t do this and pass them on your own
Yea I kinda was trying to figure out how I could get a function to always know what table/key it is called from, it’s for a NPC module with custom humanoids.
Want to use metatables since they’re like custom objects (better performance instead of having 200 functions) and any script can require and use the module, but when a function like :moveto()
is called,
Example:
customhumanoids[workspace.NPC]:moveto(position)
I somehow have to figure out so the metatable/function knows which npc I am calling it on.
With that, I have a bit of trouble figuring out how to do that, I re-readed some parts of the post to make sure I didn’t miss anything on that.
I see what you want. You can simply have :moveto()
in the class, and inherit it the metatable way.
local class = {}
function class:moveto(position)
--stuff
end
function class.new()
local object = {some properties}
setmetatable(object, {__index = class})
return object
end
And to know which NPC you’re calling the method upon each time, you simply check for self
. self
would be the NPC you called the method on each time.
function class:moveto(position)
self.Position = Position --or however you're going to do it
--self would be the NPC each time
end
So self
in this case, basically is the key I called the function upon?
Let’s say I do this…
local humanoids = {}
local humanoidobject = {}
humanoidobject.moveto = function(position)
print("Function was called on ", self:GetFullName()) --Prints workspace.NPC??
--Magic
end
humanoidobject.new = function(model)
local newhuman = {
health = {100, 100};
movespeed = 16;
root = model.rootpart;
}
setmetatable(newhuman, {__index = humanoidobject})
humanoids[model] = newhuman
return newhuman
end
Would…
humanoids[workspace.NPC].moveto(position)
now print the model I called it on? (As the model/instance is the key.)
(Code may have some errors since I almost never use metatables but now see that they can be very useful in some of my cases.)
@CoolShank8 @Autterfly
I actually I forgot about this, sorry for pointing it out only now. Instances are userdata
, userdata
can’t be indexed normally, unless it has a metatable with an __index
, and also can’t have keys inside of them changed unless the metatable has a __newindex
. I’m fairly sure that instances have metatables with an __index
and __newindex
, othrewise how could you index them or mutate properties. That way, whenever an object has a property changed, __newindex
will run the function it’s bound to, and inside of that function Roblox probably informs the .Chagned
event.
Oh alright, but I have an other question, arent events just loops deep down, aren’t events just waiting for an action to trigger constantly
No. Just like starmaq said, when you set to an instance it will directly call __newindex
. No loop, no constant checks. __newindex
will then do all the work it needs and eventually queue or call the .Changed
events. It would be extremely inefficient to check for things on loop.
Yah but Im taking like the touched event. I get the methamethod ones.
That’s also not a loop, unless you consider it part of the program loop. I assume Touched
is queued to be fired after the physics step if any parts listening to it are reported to be touching.
Using functions from a table is kinda useful. Nice one !
Great tutorial! Thank you for this awesome tutorial that helped me understand metatables more deeply.
You a amazing at this. Thank you for writing this tutorial.