loading...
Cover image for Make a Shooter in Lua/Love2D - Firing Projectiles

Make a Shooter in Lua/Love2D - Firing Projectiles

jeansberg profile image Jens Genberg ・5 min read

In the first part of this series, we wrote some code to handle player input and movement for our submarine shoot 'em up. But what good is a shoot 'em up if you can't shoot? No good at all, that's what! In this post I will show you how to get our little submarine to fire some torpedoes.

Initialization

First we will make some additions and changes to the load() method.

function love.load()
  submarineImage = love.graphics.newImage("resources/images/submarine.png")
  torpedoImage = love.graphics.newImage("resources/images/torpedo.png")

  player = {xPos = 0, yPos = 0, width = 64, height = 64, speed=200, img=submarineImage}
  torpedoes = {}

  canFire = false
  torpedoTimerMax = 0.2
  torpedoTimer = torpedoTimerMax
  torpedoStartSpeed = 100
  torpedoMaxSpeed = 300
end

As you can see, we are now loading another image which will be used when drawing torpedoes. Also, all the information related to the player is now grouped in something called a table. Tables are a great way of grouping data and very easy to create. All you need to create a table is a pair of brackets enclosing some keys and values with equal signs in between. In order to access one of the values later, you just type the name of the object followed by a dot and the name of the associated key. We are also adding an empty table to keep track of all the torpedoes on the screen. Finally we add some variables that will be used to control the firing mechanic and the torpedoes themselves.

Let's draw some more things

The first addition to the draw() function tells the engine to draw a rectangle the size of the screen, using a blue color. The second setColor() call is used to reset the color before additional calls to the draw() function.

function love.draw()
  love.graphics.setColor(186, 255, 255)
  background = love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
  love.graphics.setColor(255, 255, 255)

  love.graphics.draw(player.img, player.xPos, player.yPos, 0, 2, 2)
  for index, torpedo in ipairs(torpedoes) do
    love.graphics.draw(torpedo.img, torpedo.xPos, torpedo.yPos)
  end
end

The player is now drawn using the new 'player' table we created. We also need to draw the torpedoes, though, and this is where another Lua construct comes in, namely the 'for loop'. This loop allows us to go through all of the torpedoes currently in the table and draw them at their current positions. Lua lets you name two variables when creating a loop. The first one will hold the index of the current torpedo and the second one will hold the value. I chose to simply call these 'index' and 'torpedo'. Torpedoes are tables themselves and hold a reference to the torpedo image as well as values for their horizontal and vertical position. We will be creating some soon!

Let's update some more things

Writing all of the game logic inside of the update() function would lead to some very hard-to-maintain code once we add a few more features. That's why I have decided to extract the logic for the player object and the torpedoes into two separate functions. These functions are forwarded the same delta time value as a parameter.

function love.update(dt)
  updatePlayer(dt)
  updateTorpedoes(dt)
end

More input handling

For the updatePlayer() function, I slightly refactored the keyboard input code to store pressed direction keys in individual variables. I also took the new 'player' table into use. I omitted the player movement code from the above snippet for brevity.

function updatePlayer(dt)
  down = love.keyboard.isDown("down")
  up = love.keyboard.isDown("up")
  left = love.keyboard.isDown("left")
  right = love.keyboard.isDown("right")

  --Player movement--

  if love.keyboard.isDown("space") then
    torpedoSpeed = torpedoStartSpeed
    if(left) then
      torpedoSpeed = torpedoSpeed - player.speed/2
    elseif(right) then
      torpedoSpeed = torpedoSpeed + player.speed/2
    end
    spawnTorpedo(player.xPos + player.width, player.yPos + player.height/2, torpedoSpeed)
  end

  if torpedoTimer > 0 then
    torpedoTimer = torpedoTimer - dt
  else
    canFire = true
  end
end

Since the player can now shoot, we need to check for another key. I chose the space bar. When the key is pressed, we first check if the player is moving left or right and if so, we allow the torpedo to be affected by the player velocity. I think it gives the projectiles a better feel, but it's a matter of taste. After calculating the speed, we call another new function called spawnTorpedo() which is described below. Remember those variables we defined at the end of the load() function? We will now use them to control the fire rate of the torpedoes. We let the timer count down until it reaches zero. Then we set the 'canFire' variable to true.

Creating and updating torpedoes

In the spawnTorpedo() function we check the 'canFire' variable and spawn a torpedo only if it's set to true. We simply create a table with position, size, speed and image values and add it to the torpedoes table. Then we reset the 'canFire' variable and the timer.

function spawnTorpedo(x, y, speed)
  if canFire then
    torpedo = {xPos = x, yPos = y, width = 16, height=16, speed=speed, img = torpedoImage}
    table.insert(torpedoes, torpedo)

    canFire = false
    torpedoTimer = torpedoTimerMax
  end
end

The last thing we have to write is the updateTorpedoes() function. We write another loop to go through all the torpedoes and update their position. We let the torpedoes accelerate from their initial speed to the max speed we defined in the load() function. This makes them behave more realistically in my opinion. Once a torpedo leaves the screen we are no longer interested in it, so we set it to nil. In Lua this basically means that the table object no longer exists and so it will no longer be updated or drawn.

function updateTorpedoes(dt)
  for index, torpedo in ipairs(torpedoes) do
    torpedo.xPos = torpedo.xPos + dt * torpedo.speed
    if torpedo.speed < torpedoMaxSpeed then
      torpedo.speed = torpedo.speed + dt * 100
    end
    if torpedo.xPos > love.graphics.getWidth() then
      --torpedo = nil -does not actually work-
      table.remove(torpedoes, index)
    end
  end
end

Edit: I had misunderstood something regarding removing items from tables. To actually remove something you need to call the table.remove() function, supplying the table and the index of the item to be removed.
alt text
Next up we will spawn some enemies to shoot!
Part 3

Links

Source - part 2
Latest source
Love2D wiki
Lua reference

Posted on by:

jeansberg profile

Jens Genberg

@jeansberg

Software engineer with a passion for game development.

Discussion

markdown guide
 

I'm learning Lua now. I like it so much more it's like a useful "Ruby" ha. But I like Ruby also. I'm learning Love2d right now.