Frage

got some problem with metatable. This is my simple metatable:

local mt = {}  
function mt:add(n)  
  return setmetatable({n = n}, {__index = mt})  
end

function mt:get() return self.n end

Now I want to add some division like:

mt.math
mt.effect

Which each one has some own methods like:

mt.math:floor() return math.floor(self:get()) end
mt.effect:show(args) onMapShowEffect(self:get(), {x = x + (args[1] ~= nil or 0), ...) end
mt.effect:get() return getCurrentPos() end

Any ideas?

OK, trying make all details to share my problem.

Player = {}  
function Player:add(this)
  return setmetatable({this = this}, {__index = Player})
end

Player:get() return self.this end

Above code works perfectly on this example

function enterToGame(player1, player2)
  local p1 = Player:add(player1)
  local p2 = Player:add(player2)
  print(p1:get()) -- ID1
  print(p2:get()) -- ID2

Now I want to create some helpfully methods(functions) for table Player. I want to make it more flexible, so I want divide it for classes. Example:

Player.info = {
  id = function() return Player:get() end,
}
Player.pos = {
  get = function() return getPosition(Player:get()) end,
  set = function(args) setPosition(Player:get(), args) end,
}
Player.speed = {
  get = function() return getSpeed(Player:get()) end,
  set = function(value) setSpeed(value) end,
  improve = function(value) setSpeed(Player.speed.get() + value) end,
}

But its not work exactly what I want:

function enterToGame(player1, player2)
  local p1 = Player:add(player1)
  local p2 = Player:add(player2)
  print(p1:get()) -- ID1
  print(p2:get()) -- ID2
  print(p1.info.id()) -- ID2 instead of ID1
  print(p2.info.id()) -- ID2

When I put Player:get() in my methods its return last object declaration.

War es hilfreich?

Lösung

Based on what you state, if you do

mt.math = mt:add(123)

You don't need themt:get() because mt is the metatable for mt.math. Then

mt.math.floor = function(self) return math.floor(self.n) end

will work as expected. For example,

print(mt.math:floor())

prints 123.

EDIT 1: So now that I have a better understanding of what you are trying to do: normally you would do

p1:id()
p1:getPos()
p1:setPos()
p1:getSpeed()
p1:improveSpeed()

Note the colon, this is important, so that each method gets a "self" as first parameter, thereby given them the table instance to operate on (p1, in the above example). Instead you want to group methods so

p1.info:id()
p1.pos:get()
p1.pos:set()
p1.speed:improve()
p1.speed:get()

These methods will get a self that points to p1.info, p1.pos, etc. But those sub-tables have no knowledge of the container table (p1). The info and pos tables are in the Player class: they are shared by all instances of Player (p1, p2 etc). You have to make the info and pos tables non-shared:

function Player:add(player) 
    local pN= setmetatable( {n = player, info={}, pos={}}, {__index = Player})  
    pN.info.id = function() return pN.n end 
    pN.pos.set = function(x) return setPosition(pN, x) end
    return pN
end

Then you get

> p1=mt:add(player1)
> p2=mt:add(player2)
> print(player1)
table: 0024D390
> print(p1.info.id())
table: 0024D390
> print(player2)
table: 0024D250
> print(p2.info.id())
table: 0024D250

All that said, I don't really like the idea of having to use closures like this, perhaps there are gotchas since not everything will be in Player.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top