What does that last bit even mean? Does it mean that the table will be assigned a new value at said index?
The reason why they are locked is very obvious, as section VII.
explains. They are locked so you canât add metamethods to them, that might change the way properties are displayed, and many many other things that might break if the developer plays with stuff heâs not supposed to play with. They were disabled for safety from exploiters as well, but that ended up not being successful as they have getrawmetatable
.
Second, good point! I didnât actually cover that in the article, but to put it simply, if you set __newindex
to a table, the new value that you wanted to set, will be set inside of the table __newindex
is set to instead of the normal table.
The string metatable and any other metatable by Roblox should be locked. All strings share the same metatable, even in different scripts. Including metatables for Instance
, Vector3
, etc. If Roblox didnât lock these metatables your games wouldnât function correctly because of malicious code. If you would like further explanation on this, PM me.
There is no __gt, __ge, and __ne. >
fires __lt with the order of arguments reversed (a>b = b<a), and >=
fires __le with the order of arguments reverse (a>=b = b<=a). ~=
simply reverses the result of ==
(a ~= b = not (a == b)).
Also might be worth mentioning __lt can be used to emulate __le, although this was removed in 5.4.
There are some special rules about the comparison operators, for __eq to fire both values must be of the same type and they must both have the same __eq metafield and the objects being compared canât be raw equal. For __lt and __le to fire they must both be of the same type and they must both have the same metafield. (__lt and __le restrictions appear to have been removed at some point, not sure which version exactly, but they exist in 5.1, the version which Roblox uses)
C and C++ use 0 based indexing, so str[4] should give you the null character.
Technically, if the string has a v
, it has weak values, and if the string has k
, it has weak keys. It doesnât have to be k
, v
, or kv
.
local tbl = setmetatable({{}},{__mode="WEAK vALUES"})
print(tbl[1]) --> table: 0xXXX
collectgarbage()
print(tbl[1]) --> nil
Since this is a tutorial about metatables, here is a list of all metamethods
__index __newindex -- index operators
__mode __gc -- weak tables and garbage collection
__close -- new to 5.4 with to be closed variables
__name -- added in 5.3, used when building debug data and also used by tostring
__metatable -- sandboxing
__tostring -- overloading tostring
__pairs -- added in 5.2, overloads pairs
__ipairs -- added in 5.2 and deprecated in 5.3, overloads ipairs
__len __unm __add __sub __mul __div __idiv __mod __pow __concat __band __bor __bxor __bnot __shl __shr __eq __le __lt __call
-- operator overloading, __idiv was added in 5.3 for integer division
-- bitwise operator overloads added in 5.3 for bitwise operators
Thank you for the great additional info! The first note was already fixed, but I really lost focus about the âarrays start at 0â, I will fix that!
Also, didnât know that lua doesnât care what the string is, as long as it has a âvâ in it, great to know!
I will also include a link to all of the metamethods the latest version of lua has.
Thanks again!
Metatables never replicate, ever. Not even if you have network ownership. Additionally, network ownership only covers physics, which is just the position and (rot/)velocity of parts.
At first I was scared to approach this advance topic, and not to mention this post looked very long and intimidating. But it was so interesting and informative that it never got boring to read. There arenât a lot of documentation on metas, so Iâm glad this post exists. Aside from the few code mistakes, I think this is an underrated community resource. Thank you, it was very helpful!
This was highly informative and easy to follow!
I have always struggled to understand metatables and metamethods, and I am thankful for this tutorial because it has greatly enhanced my understanding of those concepts.
This is by far the easiest explanation of metatables Iâve seen. Been trying to properly understand this for ages and I didnât realise it was quite that simple!
Now I understand why theyâre magical.
Thanks a bunch dude I have quite a good understanding of it now
Hmm is there a way to automatically update the real object or something if a property is updated in the table. For example,
Dragon.Health = 50, and the health automiatically becomes 50, basically a metamethod to detect a change in a index. So basically like you know if you change the position property on a part the part automiatically moves
Unfortunately no there are no metamethods that detect changes in indices. Best way to check for changes in an object is with Setters and Getters. Sure, you can use a while loop that constanly checks if a value inside of the table changed, but thatsâs not cool. If you use a Setter, meaning a function that sets a property to a value
Dragon:SetHealth(50)
What you can do is, inside of the :SetHealth()
function fire a bindable event and listen to it with .Event
.
Hmm, ok. So basically properties dont change until a function is called. So if we had a table property with an index called transparency, the only way to update it with the object would be using a function. But do you have any idea how roblox does it, for example if we change a property of the part it shows in real time without a function calling
Ok just a couple of corrections, itâs more appropriate to say âa key called transparencyâ, an index is a number. And really it doesnât mean you are forced to change properties using a function (a setter), you can support both facilities.
Dragon.Health = 50
Dragon:SetHealth(50)
I just mentionned that if you were implementing your own objects, it might be more convenient efficiency-wise to use the function instead, because that way you could just fire to the bindable event whenever the function is called, but with the object.property = value
way, you would need a while loop that constantly checks if a property changed, thatâs obviously not efficient. Roblox in thix context is not as lucky as you, since youâre the only one using the objects, you know that Setters are better ad you just use them for property changed events. Roblox is used by thousands of people, so you canât force all of them to use a setter when the object.property = value
way exists. Donât take my words on this, but I assume Roblox just handles property changes the while loop way, it constantly checks for changes and if a change happened it fires the event. Remember that Roblox implements most of its things with C++, especially the cpu expensive stuff, like looping forever checking for property changes, so checking constantly for property changes isnât as expensive as doing so in the lua side. Also Roblox starts checking for property changes, and really any other action thatâs gonna fire an event, if an event for that action is connected. For example it doesnât check constantly if a part touched another part and if so fire Touched
event if one was connected. It starts checking for touched parts if a Touched event was connected to one of those parts. This also proves that :GetPropertyChangedSignal()
is better than .Changed
, if .Changed
is connected, it checks constantly for any property change, :GetPropertyChangedSignal(property)
will check constantly for the property
you chose.
So tl;dr, just use setters because itâs more efficient and since youâre the only one using the objects you know that theyâre better and youâd always use them.
I am finally beginning to understand metatables more, they always kind of confused me.
So if Iâm correct, some functions like _index allows you to execute a function when a value doesnât exist in a table or such?
Also curious to, in what ways could using metatables improve performance in games?
With this knowledge my coding skills already feel a whole lot more powerful, Iâm still kind of figuring out where I could use these neat metatables though.
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!