Function which creates objects that have global name - lua

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.

Related

Loop through matrix

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

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).

Love2D Lua no idea why this doesn't work

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

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

Giving coordinates to an object in constructor. Corona

I have a car class:
local car={};
local car_mt = { __index=car };
function car.new(_x, _y)
local ncar=
{
img=display.newImageRect("test_car.png",50,120,true);
}
function ncar:set()
self.img.xOrigin=_x;
self.img.yOrigin=_y;
end
ncar:set();
return setmetatable(ncar,car_mt);
end
return car;
When I call it by this:
local pcar=require("car")
local car1=pcar:new(200,200);
the x movement just doesn't work.
When I change xOrigin and yOrigin to x and y, it doesn't matter.
but when I call it like this:
local pcar=require("car")
local car1=pcar:new(200,200);
car1.img.x=200;
car1.img.y=200;
The result is ok.
Besides, car1.x is nil and I don't understand, how can img have its own coordinates without parent's ones.
What should I write in set() function to give stage x and y to my reference of car? And is it possible to really give it to reference, not to img?
You should call the constructor without a colon
local car1=pcar.new(200,200);

Resources