Solotaire
|
  |
| Joined: 30 Jul 2009 |
| Total Posts: 30356 |
|
|
| 02 Mar 2014 10:25 AM |
To my understanding, metatables are the closest thing RBX.Lua has to allowing us to create classes. I'm trying to figure out how to instantiate new objects with it. If I assign a metatable, the values for the table appear to remain nil. This might be an error with my setmetatable() use. Thanks for the help. I've provided a short piece of code to explain what I'm talking about.
local Fighter = { Name = "Fighter"; Level = "1"; TotalExperience = 0; }
local myfighter = setmetatable({}, Fighter)
myfighter.Level = 99
for k,v in pairs(myfighter) do print(k, v) -- only prints "Level 99", not name or experience end
print(myfighter.Name) -- returns nil |
|
|
| Report Abuse |
|
|
|
| 02 Mar 2014 10:33 AM |
You're going about it a little wrong. Metatables are no good for data. They'll only work for functions.
local FighterDataMemberDefaults = { Name = "Fighter"; Level = 1; TotalExperience = 0; } local FighterMetatable = { __index = function(t, i) return FighterDataMemberDefaults[i]; end; }
local myfighter = setmetatable({}, FighterMetatable); myfighter.Level = 99;
for k, v in pairs(myfighter) do print(k, v); --Still only prints "Level 99", but... end
print(myfighter.Name); --prints Fighter print(myfighter.TotalExperience); --prints 0
Metatables are a little strange a concept. I'll follow up this post with another method of doing classes in Lua using metatables. I just don't wanna make the post overwhelmingly long. |
|
|
| Report Abuse |
|
|
Solotaire
|
  |
| Joined: 30 Jul 2009 |
| Total Posts: 30356 |
|
|
| 02 Mar 2014 10:54 AM |
| Hm, alright. I think I can deal with having to access each value independently. Thanks for the help. |
|
|
| Report Abuse |
|
|
|
| 02 Mar 2014 10:58 AM |
--Create scope level. Once this "do" block is left, no one --can access FighterDataDefaults or FighterMetatable. do
--All data for all classes is stored here. local Data = {}; local FighterDataDefaults = { Name = "Fighter"; Level = 1; Experience = 0; } local FighterMetatable = { __index = function(t, i) --Return the data if the object has it. --If the object doesn't have it, return the default. return Data[t][i] or FighterDataDefaults[i]; end; __newindex = function(t, i, v) Data[t][i] = v; --Set the data in the data table. end; }
function NewFighter() local o = {}; Data[o] = {}; -- Create a new entry for this instance's data. return setmetatable(o, FighterMetatable); end end
local f = NewFighter(); f.Name = "Brick Hardpec"; local y = NewFighter(); y.Name = "Punches Malone"; print(f.Name); --Brick Hardpec print(y.Name); --Punches Malone print(f.Level); --1 print(f.Experience); --0
for i, v in pairs(f) do print(i, v); --prints nothing. The data members are hidden. end
So! Why is this a good approach? Mainly, data sanitation.
If you want to make sure that people don't submit numbers into your "Name" field, you can do that in the __newindex function. Same goes for strings into your experience field.
Furthermore, if someone types NewFighter().BicepSize = 4, you can totally make that just return an error that says "BicepSize is not a valid member of Fighter".
One final point, which I didn't want to include in my code because it's a little confusing: the Data table should have weak keys. This prevents a ton of Fighter instances from building up when you've already gotten rid of them in other parts of your code. If you wanna take my word for it, just replace the Data={} line with this: Data = setmetatable({}, {__mode="k"}); It might be worth googling "Lua weak tables" to figure out what's going on when you do that. |
|
|
| Report Abuse |
|
|
Solotaire
|
  |
| Joined: 30 Jul 2009 |
| Total Posts: 30356 |
|
|
| 02 Mar 2014 12:59 PM |
Wow, that's pretty helpful.
I should be able to metatables to create subclasses, correct? Or is there a problem with including metatables when I want to create them? If so, is there a way to use multiple inheritance instead?
Something like this, I guess:
local classDataDefaults = { -- things all classes will have ClassName = ""; Level = 1; Experience = 0; } local classDataMetatable = { -- index/newindex functions } local mageDataDefaults = { -- "subclass"; includes class defaults + specifics setmetatable({}, classDataMetatable); Mana = 100; }
local mageMetatable = { --index/newindex functions }
|
|
|
| Report Abuse |
|
|
|
| 02 Mar 2014 01:52 PM |
| Your syntax is a little off, but yeah, that's the general idea. If you can't find the field/function in Mage, then you'd search the class above using metatables, namely Fighter (or class, or whatever this is a subclass of). |
|
|
| Report Abuse |
|
|
Solotaire
|
  |
| Joined: 30 Jul 2009 |
| Total Posts: 30356 |
|
|
| 02 Mar 2014 04:40 PM |
What should I do to fix it? I went to the lua-users site and found their way of implementing a subclass, but I'm not sure I understand what they have.
Things I noticed on the page were:
local newclass = {} setmetatable(newclass, {__index = baseclass})
and this:
function newclass:create() local new_inst = {} -- the new instance setmetatable( new_inst, newclass_mt) -- all instances share the same metatable return new_inst end
The function is similar to the "NewFighter" function you made, so I think this is just another way to implement that. For the first part, is that the syntax I'm looking for?
I think that would give me this: local mageDataDefaults = { -- "subclass"; includes class defaults + specifics setmetatable({}, {__index = classData}; Mana = 100; }
The site also didn't have different areas for default values and the index/newindex functions (in fact, they appear to just send in __index as a value at random places), so part of my confusion might be due to that.
|
|
|
| Report Abuse |
|
|