Lua - problems iterating through table - lua

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

Related

Loading modules from folders

My project structure is following:
--Shooter
----sprites
------background.png
------player.png
------zombie.png
----units
------player.lua
------zombie.lua
----main.lua
----load.lua
----update.lua
----draw.lua
And my main.lua will have the following:
local Bullet = require("units.bullet")
local Player = require("units.player")
local Zombie = require("units.zombie")
require("load")
require("update")
require("draw")
require("functions")
love.window.setTitle("Shooter")
function love.load()
Load()
end
function love.update(dt)
Update(dt)
end
function love.draw()
Draw()
end
For example, bullet.lua:
local Bullet = {}
bulletSprite = love.graphics.newImage('sprites/bullet.png')
function Bullet.create()
local newBullet = {
pos = {},
speed = 10,
sprite = bulletSprite,
direction = 0,
dead = false
}
return setmetatable(newBullet, {__index = Bullet})
end
function Bullet:setPos(x, y)
self.pos.x = x
self.pos.y = y
end
function Bullet:setDirection(angle)
self.direction = angle
end
function Bullet:move(dt)
local distance = self.speed * dt * 60
self.pos.x = self.pos.x + math.cos(self.direction) * distance
self.pos.y = self.pos.y + math.sin(self.direction) * distance
end
function spawnBullet(bullets, player)
local newBullet = Bullet.create()
newBullet:setPos(player.pos.x, player.pos.y)
newBullet.direction = player.angle
table.insert(bullets, newBullet)
end
The problem is that none of the files in units folder will load properly. From the error log I can see that it tries to search for bullet.lua in the root directory and then various love2d and lua libraries.
I have tried various things like require("./units/bullet") or replace / with . but so far, no luck.
Extracting those files from the units folder into root folder will work. Loading images from sprites folder works, however (for example love.graphics.draw(sprites.background, 0, 0)).
Any help?
I found out the problem was that while my main.lua had correct requires, my update file didn't.
Two things come to mind.
Make Sure LUA_PATH is set to your project directory
add return Bullet, return Player and so on... into each module you have created

Trying to implement object-oriented programming in Lua, but it's not quite working

Ok, so I'm trying to follow the instructions found here: https://www.lua.org/pil/16.1.html to do something resembling OO programming in Lua (and the LOVE game framework), but it's not working. Here's the core of my code. I have a generic Object class:
local Object = {}
function Object:load(arg)
end
function Object:update(dt)
end
function Object:draw()
end
function Object:new(arg)
o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
return Object
and a Ship class that inherits from it:
Object = require('objects.object')
local Ship = Object:new()
Ship.sprite = love.graphics.newImage('assets/sprites/ship.png')
Ship.sprite:setFilter('nearest', 'nearest', 0)
Ship.px = Ship.sprite:getWidth()/2
Ship.py = Ship.sprite:getHeight()/2
function Ship:load(arg)
self.x = arg.x or 0
self.y = arg.y or 0
self.sx = arg.sx or arg.s or 1
self.sy = arg.sy or arg.s or 1
self.rot = arg.rot or 0
self.tint = arg.tint or {255, 255, 255}
end
function Ship:draw()
love.graphics.setColor(self.tint)
love.graphics.draw(self.sprite, self.x, self.y, self.rot,
self.sx, self.sy, self.px, self.py)
love.graphics.setColor({255, 255, 255})
end
return Ship
Now the problem is, I create two of these Ships as members of another object with this code:
self.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
self.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
But when I draw them, I only see one - the second. As it turns out, it's like the code above doesn't assign the new instances of Ship to ship1 and ship2, but directly to self, for reasons that I can't understand. Did I do something wrong or is this a weird bug of the interpreter?
It is not bug of the interpreter, it is designed behavior of the language. o={} creates global variable, which is not expected by the programmer here. "Why it is so?" is a frequent question to the language creator. There are many efforts to take control over that behavior simpler .
o = {} without local creates global variable global variable is accessible and shared between all the functions in the program, unless you use fancy environment scoping techniques. Using global variable inside a function opens up doors for various side effects, you should be careful with side effects.
I've removed some of the syntactic sugar and added code above can be equivalently written as follows:
Object.new = function(table_before_colon,arg)
highlander = {} --global variable, there can be only one
setmetatable(highlander,table_before_colon);
table_before_colon.__index = table_before_colon;
highlander:load(arg) -- table_before_colon.__index.load(highlander,arg)
return highlander
end
local Ship = Object:new()
--global highlander == Ship
--Ship.new points to Object.new
function Ship:load(arg)--equivalent to: Ship.load=function(self,arg)
--code that sets fields of the `self` object and is called from within new
self.x = arg.x or 0 -- equivalently highlander.x=arg.x or 0
end
Now, the presence of the global variable would not matter, if nothing happened to it in the period from the start of the new till the new returns. But, apparently, your other code is similar to this:
local OtherObject = Object:new()
--otherObject == highlander
OtherObject.load = function(new_other_obj,arg)
--highlander == new_other_obj
new_other_obj.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
--highlander == new_other_obj.ship1
new_other_obj.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
--highlander == new_other_obj.ship2
end
So, OtherObject.load calls other functions, and those functions also access and modify the same global variable.
local some_object = OtherObject:new()
returns the global variable as it is at the end of the call, which is last set to the ship2 inside the call to Ship:new inside the call to OtherObject.load inside call to OtherObject:new.
Solved! Apparently, what was needed was this little snippet:
function Object:new(arg)
local o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
Making o local in all my new methods solved everything. I don't know how it worked exactly but I assume having it in the global variable space broke something when the meta tables are set.

for loop help and sprite addition

I have two questions. what I am
trying to do is every time I shoot a enemy/vine it should be
removed and a explosion should occur. the removal works perfect
but the sprite is not called to explode
What's this in the for loop #sections[sectInt]["vines"] ?
Are they parent/ child references? Can someone break this
down to the letter even telling me what the # is?
How can I use my explosion sprite after each vine is destroyed?
I am having a difficult time figuring out how to call every x and y
vine in the for loop to explode when removed.
Code:
local sections = require("sectionData")
local lastSection = 0
function createSection()
--Create a random number. If its eqaul to the last one, random it again.
local sectInt = mR(1,#sections)
if sectInt == lastSection then sectInt = mR(1,#sections) end
lastSection = sectInt
--Get a random section from the sectionData file and then
--Loop through creating everything with the right properties.
local i
-- the random creation of vines throughout the screen
for i=1, #sections[sectInt]["vines"] do
local object = sections[sectInt]["vines"][i]
local vine = display.newImageRect(objectGroup, "images/vine"..object["type"]..".png", object["widthHeight"][1], object["widthHeight"][2])
vine.x = object["position"][1]+(480*object["screen"]); vine.y = object["position"][2]; vine.name = "vine"
local rad = (vine.width*0.5)-8; local height = (vine.height*0.5)-8
local physicsShape = { -rad, -height, rad, -height, rad, height, -rad, height }
physics.addBody( vine, "static", { isSensor = true, shape = physicsShape } )
end
end
-- explosion sprite
options1 =
{
width = 96, height = 96,
numFrames = 16,
sheetContentWidth = 480,
sheetContentHeight = 384
}
playerSheet1 = graphics.newImageSheet( "images/explosion.png", options1)
playerSprite1 = {
{name="explosion", start=1, count=16, time = 400, loopCount = 1 },
}
explode = display.newSprite(playerSheet1, playerSprite1)
explode.anchorX = 0.5
explode.anchorY = 1
--player:setReferencePoint(display.BottomCenterReferencePoint)
-- i want to reference the for loop position if each vine so it plays sprite when they are removed
explode.x = "vine.x" ; explode.y = "vine .y"
explode.name = "explode"
explode.position=1
extraGroup:insert(explode)
1) What's this in the for loop #sections[sectInt]["vines"] ? Are they parent/ child references? Can someone break this down to the letter even telling me what the # is?
As I said in my comment the # is table length.
The loop is looping over each bit of "vine" data in the selected segment data (whatever that means exactly in terms of the game) and then creates the objects for those vines.
When it's time to make your vine explode, you play the sprite. If you want to explode every vine, you would have something like:
sheetOptions =
{
width = 96, height = 96,
numFrames = 16,
sheetContentWidth = 480,
sheetContentHeight = 384
}
playerSheet = graphics.newImageSheet( "images/explosion.png", sheetOptions)
spriteSequence = {
{name="explosion", start=1, count=16, time = 400, loopCount = 1 },
}
for i, vine in ipairs(objectGroup) do
local explode = display.newSprite(playerSheet, spriteSequence)
explode.anchorX = 0.5
explode.anchorY = 1
explode.x = vine.x
explode.y = vine.y
explode.name = "explode"
explode.position=1
explode:play()
extraGroup:insert(explode)
end
Note: not tested, let me know of if any issues you can't resolve.
ok so, this is what i did to get my object to have a explosion, for anybody that is having the same issue.
function isShot( event )
print('shot!!!')
if (event.other.class == "enemy") then
if (event.other.name == "limitLine") then
event.target:doRemove() --remove bullet if hits the limitLine
elseif (event.other.name == "vine") then
event.other:doRemove()
spriteExplode(event.other.x, event.other.y) -- this function calls itself & runs the sprite
if event.other.name == "vine" then
if event.other.name == "explode" then
event.other:doRemove() --this removes the explosion sprite
end
end
end
-- remove the bullet & explosion sprite
timer.performWithDelay( 5000, function(event) explode:doRemove(); end, 1 )
event.target:doRemove()
end
return true
end
function spriteExplode( x, y)
explode.isVisible = true
explode:play()
print("play explode")
if (x and y) then -- this is the code that keeps my sprite updating upon removal of vine
explode.x, explode.y = x, y
end
function explode:doRemove(event)
timer.performWithDelay(
1,
function(event)
display.remove(self)
self= nil
end,
1 )
end
end
i added the isShot function eventListener inside of the Bullet function
bullet:addEventListener("collision", isShot) & a bullet:doRemove function is inside the bullet function as well.
hope this helps.

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)

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.

Resources