Responding to fire controls and creating bullets

It's possible to use the game on your device now for some simple gameplay dodging rocks, although there's no progress or way to lose or advance. It's time to start adding more interactivity by accepting shake and touch inputs to control the player ship's laser fire and hyperspace jump.

Getting ready

Since we won't want the ship to be destroyed by its own lasers, we're going to include it in a collision group much as we did with the asteroids. Start by opening spaceship.lua and adding a line to that effect in the body definition used by the player constructor. Save that file and create a new file at the top level of the project, laser.lua, for the first stages of this task.

Also, we'll set a timer on the laser objects so that they disappear after a moment if they don't hit anything. To do this, we'll need an object that receives clock events to track the passage of time.

Getting on with it

This module is a constructor, so we'll create the framework for a function for it to return as follows:

  1. The function will need to know what clock will govern the laser's lifetime, how long that lifetime is, what is firing the laser, which direction it will go, and what art to use for it:
    return function(clock, life, origin, direction, image)
    end
    
  2. The function will create a new image object using the specified file:
    return function(clock, life, origin, direction, image)
      local self = display.newImage(origin.parent, image)
      return self
    end
  3. It will rotate the image to face the specified direction, and place it the appropriate distance from the center of the object firing it:
    return function(clock, life, origin, direction, image)
      local self = display.newImage(origin.parent, image)
      self.rotation = direction
      direction = math.rad(direction)
      local dX, dY = math.cos(direction), math.sin(direction)
      self.x, self.y = origin.x + self.width * dX, origin.y + self.width * dY
      return self
    end
  4. It will add a physics identity to the image, and set it as a sensor so that it doesn't transfer any momentum to things that it hits:
      local dX, dY = math.cos(direction), math.sin(direction)
      self.x, self.y = origin.x + self.width * dX, origin.y + self.width * dY
      physics.addBody(self, "dynamic", body)
      self.isSensor = true
      return self
  5. All lasers will belong to the same collision group as the player, so that they don't collide with the player or each other:
    local body = {filter = {groupIndex = -1}}
    
    return function(clock, life, origin, direction, image)
      local self = display.newImage(origin.parent, image)
  6. We'll register the laser to receive clock events on its governing object and respond to them:
      self.isSensor = true
      clock:addEventListener('clock', self)
      function self:clock(event)
      end
      return self
  7. In that handler, we'll subtract the elapsed time (in milliseconds) from the remaining duration (in seconds), and clear the laser if it's been used up:
      function self:clock(event)
        life = life - event.delta / 1000
        if life <= 0 then
          event.clock:removeEventListener('clock', self)
          self:removeSelf()
        end
      end
  8. Finally, we'll give the laser its initial motion before returning it:
          self:removeSelf()
        end
      end
      local speed = 400
      self:setLinearVelocity(speed * dX, speed * dY)
      return self

Dispatching fire control events

Save laser.lua and open game.lua. We'll respond to physical touch events on the device by dispatching semantic touch events to the game object for the player ship to respond to.

  1. Above the createScene and other scene event handlers, add a handler for touch events:
      self:dispatchEvent{name='Yaw'; value = lateral * 64}
    end
    
    function scene:touch(event)
    end
    
    function scene:createScene( event )
  2. When a touch starts, we notify the game that the player has started firing the ship lasers.
    function scene:touch(event)
      if event.phase == 'began' then
        self:dispatchEvent{name = 'Fire'; phase = 'began'}
      end
    end
  3. When a touch ends or is cancelled, we'll notify the game that firing can stop:
      if event.phase == 'began' then
        self:dispatchEvent{name = 'Fire'; phase = 'began'}
      elseif event.phase == 'ended' or event.phase == 'cancelled' then
        self:dispatchEvent{name = 'Fire'; phase = 'ended'}
      end
  4. Since we don't care where on the screen these touches are for these events, we'll listen for touches that reach Runtime. Since we only want to respond while the scene is fully loaded, we'll start and stop listening in response to enterScene and exitScene events:
      Runtime:addEventListener('Tilt', self)
      Runtime:addEventListener('touch', self)
      self:Start()
    end
    
    function scene:exitScene( event )
      Runtime:removeEventListener('Tilt', self)
      Runtime:removeEventListener('touch', self)
      self:Stop()

Responding to fire events

Now that fire events are being transmitted, the player object needs to respond to them. We'll use a field on the player object, player.FireCounter, to track whether there's an active touch being used as a Fire command. This field will be nil when the player is not firing, and the number of seconds until the ship can fire again when the player is firing.

  1. Save game.lua and open player.lua. After the registration for Fire events, add a handler for them, which will toggle the player between firing and non-firing states:
      input:addEventListener('Fire', self)
      function self:Fire(event)
        if event.phase == 'began' then
          self.FireCounter = 0
        elseif event.phase == 'ended' then
          self.FireCounter = nil
        end
      end
      input:addEventListener('Jump', self)
  2. Inside the clock handler, we'll count down the remaining fire time (if any), and reset the timer if it gets below zero. Remember that the clock event's duration is in milliseconds, but the fire counter is in seconds.
      function self:clock(event)
        if self.FireCounter then
          self.FireCounter = self.FireCounter - event.delta / 1000
          if self.FireCounter <= 0 then
            self.FireCounter = self.FireCounter + 0.5
          end
        end
        local dX, dY = self:getLinearVelocity()
  3. When the timer resets, we'll also create a laser facing in the direction of the player ship:
          if self.FireCounter <= 0 then
            self.FireCounter = self.FireCounter + 0.5
            laser(event.clock, 1, self, self.rotation, "effect/laserGreen.png")
          end
  4. This also means that we need to require the laser module at the beginning of the player module:
    require "math.pythagorean"
    
    local laser = require "laser"
    
    return function(input, world, x, y)

Teleporting the player

While we're completing the controls the player can respond to, we'll take a moment to add the hyperspace function.

  1. In player.lua, add a handler function under the listener registration for Jump events:
      input:addEventListener('Jump', self)
      function self:Jump(event)
      end
      return self
  2. Move the player to the coordinates specified in the event and stop the motion.
      function self:Jump(event)
        self.x, self.y = event.x, event.y
        self:setLinearVelocity(0, 0)
      end
  3. Save player.lua and return to game.lua so that we can add a listener for Shake events:
      self:dispatchEvent{name='Yaw'; value = lateral * 64}
    end
    
    function scene:Shake(event)
    end
    
    function scene:touch(event)
  4. The response to these events is simple; we'll request a random location from the World object, and send a Jump event with those coordinates to move the player there:
    function scene:Shake(event)
      local x, y = self.World:Random()
      self:dispatchEvent{name = 'Jump'; x = x, y = y}
    end
  5. We'll register and unregister to receive Shake events at the same times we do for Tilt events, since they're provided by the same source.
      Runtime:addEventListener('Tilt', self)
      Runtime:addEventListener('Shake', self)
      Runtime:addEventListener('touch', self)
      self:Start()
    end
    
    function scene:exitScene( event )
      Runtime:removeEventListener('Tilt', self)
      Runtime:removeEventListener('Shake', self)
      Runtime:removeEventListener('touch', self)

What did we do?

We created a module to manage temporary laser objects and destroy them cleanly after they hit something. We translated two different events about device information into their equivalent events with game significance, and programmed the player object to respond to them with suitable actions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.67.5