Reputation: 569
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?
Upvotes: 1
Views: 153
Reputation: 1411
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).
Upvotes: 3