How to Automatically Add and Rename Objects in Lua? - 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)

Related

Full path to the required value

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.

Lua - problems iterating through table

I'm trying to program a game.
The gist is that the game has the player and I will spawn zombies that will walk towards the player.
These are my files:
main.lua
local Player = require("player")
local Zombie = require("zombie")
love.window.setTitle("Shooter")
function love.load()
sprites = {}
sprites.background = love.graphics.newImage('sprites/background.png')
player1 = Player
player1.setPos(300, 300)
zombies = {}
end
function love.update(dt)
player1.move(dt)
player1.rotate()
for i, z in ipairs(zombies) do
z.rotate(player1)
z.move(dt)
end
end
function love.draw()
love.graphics.draw(sprites.background, 0, 0)
love.graphics.draw(player1.sprite, player1.position.x, player1.position.y, player1.angle, nil, nil, player1.sprite:getWidth()/2, player1.sprite:getHeight()/2)
for i,z in ipairs(zombies) do
love.graphics.draw(z.sprite, z.position.x, z.position.y, z.angle, nil, nil, z.sprite:getWidth()/2, z.sprite:getHeight()/2)
love.graphics.printf("order" ..i, 0, 50, love.graphics.getWidth(), "center")
end
end
function distance(player, enemy)
return math.sqrt((player.position.x - enemy.position.x)^2 + (player.position.y - enemy.position.y)^2)
end
function love.keypressed(key, scancode, isrepeat)
if key == "u" then
spawnZombie(zombies)
end
end
zombie.lua
local Zombie = {
position = {},
speed = 1,
angle = 0,
sprite = love.graphics.newImage('sprites/zombie.png')
}
function Zombie.setPos(x, y)
Zombie.position.x = x
Zombie.position.y = y
end
function Zombie.move(dt)
distance = Zombie.speed * dt * 60
Zombie.position.x = Zombie.position.x + math.cos(Zombie.angle) * distance
Zombie.position.y = Zombie.position.y + math.sin(Zombie.angle) * distance
end
function Zombie.rotate(player)
Zombie.angle = math.atan2(player.position.y - Zombie.position.y, player.position.x - Zombie.position.x)
end
function spawnZombie(zombieTable)
zombie = Zombie
zombie.setPos(math.random(0, love.graphics.getWidth()), math.random(0, love.graphics.getHeight()))
table.insert(zombieTable, zombie)
end
return Zombie
Finally, player.lua file for completion.
spawnZombie() should create a new zombie and insert it into the zombies table. love.draw() should iterate through the zombies table and draw them all.
There are two problems I'm encountering, very likely related:
Only one zombie ever spawns.
Each spawned zombie is faster than the last one.
This probably means that only one zombie (the first one in the table) is ever drawn and his speed is increased in the love.update() function. If that assumption is correct, then the problem is probably in the spawnZombie() function.
How do I fix my program?
Zombie is your one-and-only zombie. Each "new" zombie that you create is really just an alias for it:
zombie = Zombie does not create a copy, but says that zombie is just another name for the same object.
You need to create a new table for each new zombie. You probably want to use methods so that you can use the same functions for each instance.
A method is defined with :, and automatically gets an invisible parameter called self which is the object that the method was invoked on. Methods are called like :move(0.5) instead of .move(0.5):
function Zombie:move(dt)
local distance = self.speed * dt * 60
self.position.x = self.position.x + math.cos(self.angle) * distance
self.position.y = self.position.y + math.sin(self.angle) * distance
end
To make a new zombie instance, you need to make a new table with the initial properties that you want
local newZombie = {
position = {},
speed = 1,
angle = 0,
}
and then give it all of the methods that a Zombie should have. The briefest way to do that is to use metatables. The __index metamethod explains to Lua what to do when a field/method is asked for that hasn't been explicitly set. This lets us "copy" the Zombie "class" onto new instances:
setmetatable(newZombie, {__index = Zombie})
This can be wrapped up into a "constructor" method on the Zombie "class". Since this constructor doesn't act on an existing zombie object, you should define and call it with . instead of ::
local Zombie = {}
-- This property is shared between ALL zombies,
-- and changing it will change it for ALL zombies
Zombie.sprite = love.graphics.newImage('sprites/zombie.png')
-- RETURNS a freshly created Zombie instance
function Zombie.create()
local newZombie = {
position = {},
speed = 1,
angle = 0,
sprite = love.graphics.newImage('sprites/zombie.png')
}
return setmetatable(newZombie, {__index = Zombie})
end
-- MODIFIES the zombie that this is called on
function Zombie:move(dt)
local distance = self.speed * dt * 60
self.position.x = self.position.x + math.cos(self.angle) * distance
self.position.y = self.position.y + math.sin(self.angle) * distance
end
function Zombie:setPos(x, y)
self.position.x = x
self.position.y = y
end
-- MODIFIES zombies by adding a freshly created zombie to it
function spawnZombie(zombies)
local newZombie = Zombie.create()
newZombie:setPos(math.random(0, love.graphics.getWidth()), math.random(0, love.graphics.getHeight()))
table.insert(zombieTable, newZombie)
end
-- In update function:
for i, z in ipairs(zombies) do
z:rotate(player1)
z:move(dt)
end
You should get into the habit of using local variables in your functions, such as distance in Zombie:move. Global variables are likely to cause mistakes, and also take a (small) performance penalty, because sharing the variable with everyone takes more time than keeping it hidden (and also makes the JIT optimizer's job more difficult).

Corona SDK(LUA) - attempt to call upvalue 'spawnEnemy'(a nil value)

I just trying to add eventListener to a object, which should disappear when I tap on it. But I get error mentioned in the title. Here is my whole code at this point :
-- housekeeping stuff
display.setStatusBar(display.HiddenStatusBar)
local centerX = display.contentCenterX
local centerY = display.contentCenterY
-- set up forward references
local spawnEnemy
-- preload audio
-- create play screens
local function createPlayScreen()
local bg = display.newImage("background.png")
bg.y = 130
bg.x = 100
bg.alpha = 0
local planet = display.newImage("planet.png")
planet.x = centerX
planet.y = display.contentHeight +60
planet.alpha = 0
transition.to( bg, { time = 2000, alpha = 1, y = centerY, x = centerX } )
local function showTitle()
local gametitle = display.newImage("gametitle.png")
gametitle.alpha = 0
gametitle:scale (4, 4)
transition.to( gametitle, { time = 500, alpha = 1, xScale = 1, yScale = 1 })
spawnEnemy()
end
transition.to( planet, { time = 2000, alpha = 1, y = centerY, onComplete = showTitle } )
end
-- game functions
local function shipSmash(event)
local obj = event.target
display.remove( obj )
end
local function spawnEnemy()
local enemy = display.newImage("beetleship.png")
enemy.x = math.random(20, display.contentWidth - 20)
enemy.y = math.random(20, display.contentHeight - 20)
enemy:addEventListener ( "tap", shipSmash )
end
local function startGame()
end
local function planetDamage()
end
local function hitPlanet(obj)
end
createPlayScreen()
startGame()
And here is how error window looks like :
I'm kinda new in this area(LUA programming) so sorry for maybe dumb syntax error or something, but what I saw is that this error shows up after I write this line of code: enemy:addEventListener ( "tap", shipSmash )
Change local function spawnEnemy() to function spawnEnemy() as this variable was already declared earlier. Yes, this is typical Lua pitfall for beginners.
You've declared spawnEnemy as a local variable twice. That's allowed (the second one hides the first where ever the second one is in scope) but that's not what you wanted.
You have correctly declared a local variable and captured it in showTitle. To set that very same variable later, use an assignment statement without prefixing it with local. You can assign it a function definition using the "anonymous" function syntax:
spawnEnemy = function()
...
end
Actually, in Lua, all functions are anonymous since they are just values. But, for debugging, it is helpful to have a name associated with a function. In stack traces, the name of the variable used to call the function is used, where possible.

Some insight on Object key accessing in 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

Lua table variables the same even for new objects

I'm trying to create a Lua table that represents a matrix, however I keep running into a problem where if I create two Matrices, and initialize some values they both have the same values.
--Test.Lua
require"Matrix"
M1 = Matrix.Matrix:New()
M2 = Matrix.Matrix:New()
M1._11 = 2
print(M1._11) --Prints 2
print(M2._11) --Prints 2
--Matrix.lua
module("Matrix", package.seeall)
Matrix = {}
Matrix = { _11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
function Matrix:New()
object = object or {}
setmetatable(object, self)
self.__index = self
return object
end
object = object or {}
This is why that happens. You only ever create one Matrix object. There is only every one object table which you return, and there is only ever one self table that you use as a metatable.
So how can you expect different instances when Matrix:New will always return the exact same value on every call?
You need to return a new table for each New call; that's why we use that name ;) Because of the way you're using a metatable, you also have to return a new metatable; you can't return the same metatable attached to new tables and expect it to work.
As nicol is explaining, on one hand you are trying to "reuse the same object over and over" (probably to "make it faster") and on the other you want to have different objects.
The solution is - don't reuse object on New call.
local Matrix = {} -- don't use the module function. Make Matrix local ...
Matrix.__index = Matrix
function Matrix:New()
local object = { -- create one local variable on every call to New
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
setmetatable(object, self)
return object
end
return Matrix -- ... and return the Matrix local var at the end
A couple notes:
You really must learn how to use local
Usage of the module function is not recommended. Return a local table instead, as in my example.
Usage: assuming that that file is called "Matrix.lua":
local Matrix = require 'Matrix'
local M1 = Matrix:New()
local M2 = Matrix:New()
-- etc
As a sidenote, the Matrix:New() function can be made shorter (and faster). The following implementation works exactly as the one above, but it's slightly more efficient:
function Matrix:New()
return setmetatable({
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
},
self)
end
This works because setmetatable(t,m) returns t with m already set as its metatable.

Resources