Some insight on Object key accessing in lua - lua

Im just alittle curious, as well as a bit confused. In my lua code im setting a new object like so from the start.
enemy = {};
enemy.__index = enemy;
function enemy.new(args)
Obj = {};
setmetatable(Obj,enemy);
Obj.name = "bullet";
Obj.x = args.x;
Obj.y = args.y;
Obj.spriteTexFile= "Invader.png";
Obj.sprite = display.newImage( Obj.spriteTexFile);
Obj.sprite:setReferencePoint ( display.TopLeftReferencePoint );
Obj.sprite.x = Obj.x;
Obj.sprite.y = Obj.y;
Obj.sprite.alpha = 0;
Obj.health = 100;
Obj.activeBul = false;
Obj.bullet = Bullet.new({x=Obj.sprite.x,y=Obj.sprite.y});
return Obj;
end
...
return enemy;
end
So when instantiating a new Enemy obj I call the new function above. NOW in the same file, a function in the Enemy Object I have the following function for example which allows me to acces the "self.bullet", a Bullet Object created when the Enemy is created. It also allows me to call the function trajectBullet in this Bullet instants.
function enemy:shoot()
local Bullet = require "Bullet";
local DEFAULTTIME = 5;--Movement time per space
self.bullet:trajectBullet({x=self.sprite.x,y=display.contentHeight, time =
DEFAULTTIME*display.contentHeight-self.sprite.y)});
end
My Question comes with a call like the following. If I try setting a property of a Bullet in this case, the owner property, i get a nil error and wont let me change it. If someone could help me understand alittle how accessing keys and properties really works that would help me out alot.
function enemy:setBulletOwner()
self.bullet.owner = self;
end
UPDATE:
bullet = {};
bullet.__index = bullet;
function bullet.new(arg)
local Obj = {};
setmetatable ( Obj, bullet );
Obj.sprite = display.newRect( 0, 0, 3, 7 );
Obj.sprite.x = arg.x;
Obj.sprite.y = arg.y;
Obj.sprite:setFillColor ( 255, 255, 255 );
Obj.sprite:setReferencePoint ( display.TopLeftReferencePoint );
Obj.owner = nil;
return Obj;
end
function bullet:trajectBullet(arg)
self.sprite.tween = transition.to(self.sprite,{ tansistion = easing.outExpo, y = arg.y, x=arg.x,time= arg.time,onComplete = function() bullet:cancelTween(self.sprite);
self.owner.sprite:dispatchEvent( {name = "canShootAgain"} ); end});
end
Keep in mind Obj.owner should be getting set from the function below.
function enemy:setBulletOwner()
print("BULLET MADE");
self.bullet.owner = self;
end

You should have your classes set up like this
Bullet
Bullet = {}
Bullet_mt = { __index = Bullet }
function Bullet:new(co_ordinates)
local obj = {x=co_ordinates[1],y=co_ordinates[2]}
obj.owner = "You" --etc...
return setmetatable(obj,Bullet_mt)
end
Enemy
Enemy = {slogan="Gettm!'"}
Enemy_mt = {__index = Enemy;}
function Enemy:new(args)
local obj = {}
--etc..
obj.bullet = Bullet:new({0,0})
return setmetatable(obj,Enemy_mt)
--alert return setmetatable(obj,getmetatable(self))
end
function Enemy:getBulletOwner()
return self.bullet.owner;
end
You shouldn't be requiring "Bullet" each time the enemy shoots in enemy:shoot. When you want to create a bullet for an enemy if you only want the enemy to have one bullet you should
create a new 'instance' of the bullet class and associate it with the key bullet like you've been doing obj.bullet= Bullet.new(...) but also introduce this functionality into a method of Enemy (so you can add a new bullet after the old one goes out of range etc...).
If a index doesn't exist within a table, it will go looking for the index in the table associated with __index in the metatable assigned to the table in question. As an example say a = Enemy:new(), and we wanted to find out the slogan of the enemy, via a.slogan we would look for the index slogan in a but not find it. So we would then go check what __index was associated with in the metatable of a, in this case Enemy. So we look for slogan in Enemy, it exists so we end up with `"Gettm!'".
Adding the following code beneath the class definitions
en = Enemy:new()
print(en:getBulletOwner())
print(en.slogan)
Produces
You
Gettm!'
Also be weary of the difference between a:b(arg1,arg2) and a.b(arg1,arg2). a:b(arg1,arg2) is essentially equivalent to a.b(a,arg1,arg2) where a is bound to self within the function. An example of this would be:
print(en.getBulletOwner())
produces
lua: l.lua:22: attempt to index local 'self' (a nil value)
while
print(en:getBulletOwner())
produces
You

Related

attempt to call method 'func' (a nil value)

No matter how I approach Lua, I run into this error all the time, so I must not understand something inherit to the language:
attempt to call method 'func' (a nil value)
I've seen the error here a few times as well but the problem doesn't seem clear to me.
Here's my module:
actor.lua
Actor = {
x = 0,
mt = {},
new = function()
local new_actor = {}
new_actor.x = Actor.x
new_actor.mt = Actor.mt
return new_actor
end,
test = function(self, a, b)
print(a, b)
end
}
I'm using Löve.
main.lua
require "game/actor"
local a = Actor:new() --works fine
function love.load()
a.x = 10
print(a.x) --output: 10
a:test(11, 12) --error: attempt to call method 'test' (a nil value)
end
I'm also not sure when it's appropriate to use the previous styling over this in a module.
Actor = {
x = 0
}
Actor.mt = {}
function Actor.new()
print(42)
end
I'm honestly not sure what is more correct than the other but considering I run into a simple error either way, there's probably something I'm missing entirely?
It looks like you're trying to instance a kind of class made of metatables. You basically need to assign new_actor's metatable with Actor.mt. (Resuming the problem: when you're indexing new_actor you're not indexing Actor in this case)
setmetatable(new_actor, Actor.mt);
Even if the metatable is being added, it won't work until you put the meta "__index" event to index a table containing your class methods/values, in this case:
Actor.mt = {
__index = Actor
};
I'd suggest moving your class methods/values into a new table, like Actor.prototype, Actor.fn, etc... avoiding conflicts:
Actor.fn = {
test = function(self, a, b)
print(a, b)
end
};
Actor.mt = {
__index = Actor.fn
};
More about metatables in Lua 5.3 manual.

Division of metatable

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.

How to Automatically Add and Rename Objects in Lua?

I am trying to construct a method that automatically adds and renames objects in Lua. I have the add method for adding the object, but I am not sure how to make it so that it renames each object. I'm thinking of adding an if statement, but I don't know how to construct it in a way that it will rename the object each type it loops.
Here is what I have so far:
frogBody = {density = .8, friction = 0.3, bounce = 0.1, radius = 10} -- body Type
local onPlayerSpawnObject = function(object) -- method to spawn object
local layer = map:getTileLayer("Enemies")
local frog = movieclip.newAnim{ "FrogMini.png", "frogMiniRed.png" } -- object that spawns
frog.x = object.x ; frog.y = object.y
frog.myName = "frog"
frog.isHit = false
physics.addBody(frog, frogBody)
end
Thank you for all of your help!
I think that what you mean is that you would like to access the frogs objects from your example distinctively ?
If that is the case you could either assign the return of the add function to a lua table, or directly assign to the table inside your function.
local frogs = {}
frogBody = {density = .8, friction = 0.3, bounce = 0.1, radius = 10} -- body Type
local onPlayerSpawnObject = function(object) -- method to spawn object
local layer = map:getTileLayer("Enemies")
local frog = movieclip.newAnim{ "FrogMini.png", "frogMiniRed.png" } -- object that spawns
frog.x = object.x ; frog.y = object.y
frog.myName = "frog"
frog.isHit = false
physics.addBody(frog, frogBody)
table.insert(frogs, frog)
end
-- To print a distinct frog from the collection, replace 1 by your index
print (frogs[1].myName)

Issues with setting a property

Below is the gist of the system i'm having issues with. I seem to understand self, ., and :. I just seem to be missing something. What's happening is that when it calls "Object:setSomeObjectIsAttachedTo()" if I simply print "self" i'll get a table address printed. If i go a step further and try to print "self.someObject" i get nil, which shouldn't happen because in Object it has a key someObject which was created at the start in "Object.new(args)" of course if tried to go a step further it wouldn't even be able to go there since its nil. Please Help!!
Object File
Object = {};
ObjectMeta = {__index = Object};
function Object.new(args)
Obj = {};
Object.someObject = OtherObject.new(args)
return setmetatable(Obj,ObjectMeta );
end
function Object:setSomeObjectIsAttachedTo()
--OtherObject instance Should set its attached property to
--This instance of Object
self.someObject.ObjectImAttachedTo = self;
end
--Calls after new to set the ObjectImAttachedTo Property, So it isnt nil
Object:setSomeObjectIsAttachedTo();
return Object;
OtherObject File
OtherObject = {};
OtherObjectMeta = {__index = OtherObject};
function OtherObject.new(args)
Obj = {};
Obj.ObjectImAttachedTo =nil;
return setmetatable(Obj,ObjectMeta );
end
return Object;
UPDATE
Scene
Scene = {};
ObjectContainer = {};
function Scene.new()
end
function Scene.addObjects()
local Object= require "Object"
local StartX = 50;
local StartY = 20;
local counter = 0;
for i=0, 17 do
ObjectContainer[i] = Object.new({x=StartX,y=StartY});
end
end
Scene.addObjects();
return Scene
end
The table Object does not have a field named someObject, though instances returned by Object.new() do have that field. These are two different tables, roughly corresponding to a class and one of its instances.
ADDENDUM
With the revised code, Object doesn't have the field someObject until you call Object.new(). So, you must call Object.new() before you call Object:setSomeObjectIsAttachedTo();. Note that OtherObject.new() must be defined before you can call Object.new().
ADDENDUM2 in answer to question "so what would you suggest i do to fix this?"
function Object.new(args)
Obj = {};
Obj.someObject = OtherObject.new(args)
Obj.someObject.ObjectImAttachedTo = Obj;
return setmetatable(Obj,ObjectMeta );
end
and get rid of Object:setSomeObjectIsAttachedTo();

reference to parent class in Lua (Corona)

in my game.lua file I have this:
function new()
local obj = display.newGroup();
currentLevel = Level.new(1);
currentLevel.game = obj; //also tried currentLevel.game = self;
function obj:replay()
print("game - replay")
end
return obj;
end
In the Level lua file, I try to call the replay function in game.lua:
game = {};
...
game:replay();
But I get this error:
attempt to call method 'replay' (a nil value)
How can I keep a reference to the game file in level.lua?
Don't you mean game = new(), instead of game = {}? If you create game with {} then it is an empty table.

Resources