Apologies for the unhelpful title but I really don't know what to call this. Anyway, I can't tell why this works:
local entity = require "entity"
entity:new(5,10,15,6)
local test = entity
print(test.x,test.y)
...but this doesn't...
local entity = require "entity"
local test = entity:new(5,10,15,6)
print(test.x,test.y)
Entity.lua simple contains:
local Entity = {}
function Entity:new(x,y,w,h)
self.x = x
self.y = y
self.width = w
self.height = h
end
return Entity
Case 1:
variable entity gets table which is returned from Entity.lua.
When you call Entity:new() in Entity.lua all the variable initialization is performed on table(object) entity. So, entity has variables x, y, width and height. You assigned table to test and printed it.
It works.
Case 2:
local test = Entity:new().
Here variable test takes return value of method new(), which is nil in this case, because function doesn't return any value.
It prints an error because table test doesn't have any keys called x and y.
If you want to create a new table with x, y, w, h you can do :
function Entity.new(x,y,w,h)
local newEntity = {}
newEntity.x = x
newEntity.y = y
newEntity.width = w
newEntity.height = h
return newEntity
end
or (but less readable) :
function Entity.new(x,y,w,h)
return {x = x, y = y, width = w, height = h}
end
Related
How do I get the full path to the required value in the table? I want to track changes in another table through a proxy table.
I understand that I need to use metatables and __index in it. But I haven't been able to come up with a tracker yet.
Sample table structure:
Objects = {
Panel = { layer = 1, x = 600, y = 328, w = 331, h = 491;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'header' };
Window = { layer = 2, x = 400, y = 100, w = 100, h = 100;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'lorem ipsum dorem' };
};
};
};
};
};
Path: Objects.Panel.objects.Window.objects.label.text
I tried to create a metatable for each of the tables and collect the result of each call to __index into a table in order to roughly understand which key and value were retrieved or changed in order to synchronize these values with other tables.
This will prove itself to be horrendously slow and memory inefficient. Anyway, you were right on the track: proxy and handle __index and __newindex metamethods to your liking. This being said you also need to track the state of the proxy somehow.
You can try to hide it with some closures and upvalues but the easy way is to store the information directly in the proxy tables:
function make_tracker (o, name)
local mt = {}
mt.__index = function (proxy, key)
local path = {unpack(rawget(proxy, "__path"))} -- Stupid shallow copy
local object = rawget(proxy, "__to")
table.insert(path, key)
if type(object[key]) == "table" then
return setmetatable({__to = object[key], __path = path}, mt)
else
return table.concat(path, ".") .. " = " .. tostring(object[key])
end
end
return setmetatable({__to = o, __path = {name}}, mt)
end
__to fields indicates what proxy should point to and __path is there to cover fields we have trespassed so far. It does a shallow copy, so that one can use subproxies with local variables. name parameter is there to initialize the name of the first table, as you just simply can't know that. You use it like this:
local tObjects = make_tracker(Objects, "Objects")
local subproxy = tObjects.Panel.objects.Window
print(subproxy.objects.label.text)
print(tObjects.Panel.objects.label.text)
print(subproxy.x)
-- prints:
-- Objects.Panel.objects.Window.objects.label.text = lorem ipsum dorem
-- Objects.Panel.objects.label.text = header
-- Objects.Panel.objects.Window.x = 400
Of course, I doubt that appending the path to the original value is what you want. Modify insides of else block:
return table.concat(path, ".") .. " = " .. tostring(object[key])
according to your needs, e.g:
register_tracked_path(table.concat(path, "."))
return object[key]
If you want to handle modification of values you need to extend the metatable with similar __newindex.
I'm creating a simple matrix like follows:
for x = 0, 50 do
current_level[x] = {}
for y = 0, 50 do
current_level[x][y] = grabTile();
end
end
After that i try to read it, but somehow the x is now a object not a number, while y seems perfectly fine!
How i try reading it:
for x,value in pairs(self.map) do
if value == ni then print("none"); return;end;
for y,object in pairs(value) do
if object == ni then print("none"); return;end;
object:render(x,y); -- Here x is an object
end
end
I'm new to working with lua, so i might be doing something obvious terribly wrong.
How would i make this work?
What i get for x is something like: table: 0x07c8d530
This value stays the same along the complete iteration
object:render(x,y); -- Here x is an object
This line is using colon syntax. It is a syntactic sugar for object.render(object,x,y) call.
So your render() function must have the first self argument declared either explicitly as function render(self, x, y) or implicitly with another syntactic sugar for definition: function object:render(x,y).
Unrelated hint. The first loop will be faster/smaller if transformed to:
for x = 0, 50 do
local row = {}
for y = 0, 50 do
row[y] = grabTile();
end
current_level[x] = row
end
I had this code which simply displays a picture I need in my game.
DeckOfPlayingCards = display.newImageRect("Graphics/ImproveMenu/Wealth/Luxury/Buy/DeckOfPlayingCards.png", 96*6, 96)
DeckOfPlayingCards.x = centerX
DeckOfPlayingCards.y = -90
DeckOfPlayingCards.name = "DeckOfPlayingCards"
Now, since I have a lot of these pictures planned to be in my game menu, I don't want to copy-paste that code all over. I wanted to make a simple function that would do it for me. This is what I tried, but failed and don't know why.
function DisplayObject(object, x, y, name)
object = display.newImageRect("Graphics/ImproveMenu/Wealth/Luxury/Buy/" ..name..".png", 96*6, 96)
object.x = x
object.y = y
object.name = name
scrollView:insert(object)
end
DisplayObject(DeckOfPlayingCards, centerX, -90, "DeckOfPlayingCards")
This function can successfully show the picture, but whenever I try to do something with it (make it a button), it says the value is nil. After doing the function, I tried to do print the name like so:
function DisplayObject(object, x, y, name)
object = display.newImageRect("Graphics/ImproveMenu/Wealth/Luxury/Buy/" ..name..".png", 96*6, 96)
object.x = x
object.y = y
object.name = name
scrollView:insert(object)
end
DisplayObject(DeckOfPlayingCards, centerX, -90, "DeckOfPlayingCards")
print(DeckOfPlayingCards.name)
It crashes and says that the value is nil.
Now, how can I exactly make my object with function that will be as I tried to write it like the first code? I hope someone can help me out.
DisplayObject(DeckOfPlayingCards, centerX, -90, "DeckOfPlayingCards")
I think you expect this to act as passing the name DeckOfPlayingCards to DisplayObject, but it's just passing nil because it's an undefined variable.
One option is to simply create the object in DisplayObject, return it, then assign it to DeckOfPlayingCards:
function DisplayObject(x, y, name)
local object = display.newImageRect(
"Graphics/ImproveMenu/Wealth/Luxury/Buy/" ..name..".png",
96*6, 96)
object.x = x
object.y = y
object.name = name
scrollView:insert(object)
return object
end
DeckOfPlayingCards = DisplayObject(centerX, -90, "DeckOfPlayingCards")
print(DeckOfPlayingCards.name)
The error message tells you what is wrong. You cannot index a nil value.
Therefor DeckOfPlayingCards must be nil.
Where do you initialize DeckOfPlayingCards? I understand that the first block of code is not part of your current script as you moved it into the new function DisplayObject.
Assuming that DeckOfPlayingCards is nil when you call DisplayObject(object, x, y, name) object is also nil within your function.
As lua only creates a local copy of the function parameters you set the local variable object to the return value of display.newImageRect...
This does not change the value of DeckOfPlayingCards!!! It is still nil.
What you do is the same as:
function DisplayObject(x, y, name)
local object = display.newImageRect("Graphics/ImproveMenu/Wealth/Luxury/Buy/" ..name..".png", 96*6, 96)
object.x = x
object.y = y
object.name = name
scrollView:insert(object)
end
Return object and store it in DeckOfPlayingCards to solve your problem.
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.
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.
I want the key pairs to be tableToPopulate.width = 30 and tableToPopulate.Height = 20
They are currently tableToPopulate[1] = 30 and tableToPopulate[2] = 20
local function X ()
code, code...
return 30,20
end
local tableToPopulate = {
x()
}
Why don't you just return a table?
local function X ()
return {width=30, height=20}
end
You could pass in the table you want the values set on, like this:
function x(tbl)
tbl.Height = 20;
tbl.Width = 30;
end
local t={}
x(t)
print(t.Height, t.Width)
although it may make more sense to use nested tables depending on how complex the structure will be of whatever is in the table.
function x(tbl)
table.insert(tbl, {Height = 20, Width = 30})
end
local t={}
x(t)
print(t[1].Height, t[1].Width)
which would be equivalent to this:
function x()
return {Height = 20, Width = 30}
end
local t = {x()}
print(t[1].Height, t[1].Width)
So really, it depends on how you want to group the data and which syntax you prefer.