Continuous gun firing and how to housekeep all those bullets

Hi All,

I am very new to the community, in fact this is probably my first post here.  I have made myself quite familiar with Corona SDK for over a month now and I have made good progress on the current “game” I’m developing but I do have an issue that I’m hoping you guys can help me out.

I’m not sure if this has been discussed before, but I can’t seem to find an exact topic about my problem, so here goes:

Currently, my “gun” only allows player to fire again if the current bullet has gone off the top of the screen or hits a target (in any case it is set to nil ).  My “bullet” speed is slow, such that it approximately takes about .6 seconds from the bottom up.   

I want to allow the player to be trigger-happy, eg. fires at will on each tap (not the tap-and-hold kind of continuous firing, but one tap-one-bullet thing).  But if I did that, I am facing the problem of how to keep track of all those bullets, how to remove them, and how to manage the collision process.  The bullet:removeSelf() cannot seem to pinpoint which one currently flying on screen to clear.  

To illustrate my problem, pls see the example code below:

\_W = display.contentWidth \_H = display.contentHeight \_SoundON = true local physics = require("physics") physics.start() local bulletActive     = false local bullet local bulletReach    = -5 local bulletSpeed     = 600 local okToFire         = true  local fireButton = display.newCircle(\_W-40, \_H-40, 40) ------------------- Gun Listener -------------------- local function handleGun(event)     -- remove this IF block to allow continuous firing---      if bulletActive or not okToFire then          return      end     -----------------------------------------------------          bullet          = display.newCircle (0, 0, 5)     bullet.x        = \_W / 2     bullet.y        = \_H - 5     physics.addBody ( bullet, "kinematic", {density=0.1, friction=0, bounce=0.0} )              bullet.trans =  transition.to( bullet, { time=bulletSpeed, x=bullet.x, y=bulletReach,          onStart =                 function()                     bulletActive = true                     -- if \_SoundON then audio.play( bang ) end                 end ,              onCancel =                      function()                      bulletActive = false                     bullet:removeSelf()                      bullet = nil                     print("bullet removed onCancel")                 end ,         onComplete =                  function()                      bulletActive = false                     bullet:removeSelf()                      bullet = nil                     print("bullet removed onComplete")                 end              })          end    fireButton:addEventListener( "tap", handleGun ) 

If I remove the if-then block at the start of the function, player can fire rapidly at will but will introduce the problem when cleaning the bullets (bullet:removeSelf()  ).

What is the better way of handling this and still able to remove each bullet?  

Thanks!

Adding the bullets to a table like this should solve the problem. Before when you created each new bullet it was overwriting the reference to the last, so when the onComplete function ran it had no idea where the last bullet was.

Going forward though it might be an idea to use applyForce instead of transitions to fire the bullets if they are going to collide with some other physics objects. To remove those you would cycle backwards through the table of bullets say every second and remove those that are now off screen.

[lua]

_W = display.contentWidth

_H = display.contentHeight

_SoundON = true

local physics = require(“physics”)

physics.start()

local scene = storyboard.newScene()

local bulletActive     = false

local bullets = {}

local bulletReach    = -5

local bulletSpeed     = 600

local okToFire         = true

local fireButton = display.newCircle(_W-40, _H-40, 40)

------------------- Gun Listener --------------------

local function handleGun(event)

    – remove this IF block to allow continuous firing—

        if bulletActive or not okToFire then

          – return

        end

    -----------------------------------------------------

    local bullet          = display.newCircle (0, 0, 5)

    bullet.x        = _W / 2

    bullet.y        = _H - 5

    physics.addBody ( bullet, “kinematic”, {density=0.1, friction=0, bounce=0.0} )

    bullet.trans =  transition.to( bullet, { time=bulletSpeed, x=bullet.x, y=bulletReach,

        onStart =

                function()

                    bulletActive = true

                    – if _SoundON then audio.play( bang ) end

                end ,

        onCancel =

                function()

                    bulletActive = false

                    bullet:removeSelf()

                    bullet = nil

                    print(“bullet removed onCancel”)

                end ,

        onComplete =

                function()

                    bulletActive = false

                    bullet:removeSelf()

                    bullet = nil

                    print(“bullet removed onComplete”)

                end

            })

    bullets[#bullets+1] = bullet

end

fireButton:addEventListener( “tap”, handleGun )

[/lua]

Hi nick,

Thanks a lot for your suggestion.  I actually tried to do that before, but it introduced another complication on managing the table, particularly re-using the available slots.

Consider the scenario:

Player fired 5 bullets in rapid succession.   The first bullet went of the screen; 2nd and 3rd still in view; the 4th bullet hits a target, 5th bullet still on the way up.  At this point the code could now have removed the bullets in index location 1 and 4, leaving their space available for re-use.   Now the player taps again to fire three more… naturally you would scan the table for available slot and re-use them rather than just extending the array at the end.  Now consider at this point the 2nd location is already available too, and so on and so forth.  This whole process of managing the table eventually led to timing issues and I couldn’t find a better way .   

Here is my method for firing&handling multiple bullets – I removed all the irrelevant code to make it more readable –

local PlayerBullets = {} local function shootBullet()     audio.play( tank\_shot )            local bullet = display.newGroup()         local pt = mathapi.rotateAboutPoint( {x=user\_turret.x,y=user\_turret.y-50}, user\_turret, user\_turret.rotation - 90 )                 --bullet attributes           bullet.body = loader:createSpriteFromSHDocument("Weapon3ex", "GameFX", "LevelAssets.pshs"); -- load bullet image       bullet.body.x = pt.x; bullet.body.y = pt.y;       physics.addBody( bullet.body , "dynamic", { friction= 0, bounce= 1.0, density= .6, radius= 7, filter = bulletCollisionFilter} );       bullet.body:scale(.6,.54);                bullet.body.class = "bullet"       bullet.body.isBullet = true                bullet.body.rotation = user\_turret.rotation + 90              bullet.body:applyForce( pt.x - user\_turret.x, pt.y - user\_turret.y, user\_turret.x, user\_turret.y )             -- collision function        local onPlayerBulletCollision = function(self, event)                               if (event.phase == "began") then                 if (event.other.class == "EnemyBullet") then                            display.remove(self)                     display.remove(event.other)                 end                          elseif (event.phase == "ended") then                              audio.play(Bullet\_Impact)                 display.remove(self)                   end         return true       end       bullet.body.collision = onPlayerBulletCollision       bullet.body:addEventListener("collision", bullet.body)             bullet:insert( bullet.body )       camera:add(bullet, 2, false)             table.insert(PlayerBullets, bullet)            return bullet       end  

Hope it helps.

Cheers!

-Saer

Edit: I use an invisible but hit testable rectangle for detecting touch events. When the user touches the screen to fire, it calls the ‘shootBullet’ function. Since the bullets are created dynamically and added to the table as individual bullets, each bullet receives the specified attributes and therefore behaves as a single entity.
(it’s currently 3:00 A.M. so I’m hoping that was clear enough.)

Here’s a tutorial on a scrolling shooter game with bullets:

http://karnakgames.com/wp/2011/02/how-to-make-a-simple-physics-based-shooter-game-with-the-corona-sdk/

Adding the bullets to a table like this should solve the problem. Before when you created each new bullet it was overwriting the reference to the last, so when the onComplete function ran it had no idea where the last bullet was.

Going forward though it might be an idea to use applyForce instead of transitions to fire the bullets if they are going to collide with some other physics objects. To remove those you would cycle backwards through the table of bullets say every second and remove those that are now off screen.

[lua]

_W = display.contentWidth

_H = display.contentHeight

_SoundON = true

local physics = require(“physics”)

physics.start()

local scene = storyboard.newScene()

local bulletActive     = false

local bullets = {}

local bulletReach    = -5

local bulletSpeed     = 600

local okToFire         = true

local fireButton = display.newCircle(_W-40, _H-40, 40)

------------------- Gun Listener --------------------

local function handleGun(event)

    – remove this IF block to allow continuous firing—

        if bulletActive or not okToFire then

          – return

        end

    -----------------------------------------------------

    local bullet          = display.newCircle (0, 0, 5)

    bullet.x        = _W / 2

    bullet.y        = _H - 5

    physics.addBody ( bullet, “kinematic”, {density=0.1, friction=0, bounce=0.0} )

    bullet.trans =  transition.to( bullet, { time=bulletSpeed, x=bullet.x, y=bulletReach,

        onStart =

                function()

                    bulletActive = true

                    – if _SoundON then audio.play( bang ) end

                end ,

        onCancel =

                function()

                    bulletActive = false

                    bullet:removeSelf()

                    bullet = nil

                    print(“bullet removed onCancel”)

                end ,

        onComplete =

                function()

                    bulletActive = false

                    bullet:removeSelf()

                    bullet = nil

                    print(“bullet removed onComplete”)

                end

            })

    bullets[#bullets+1] = bullet

end

fireButton:addEventListener( “tap”, handleGun )

[/lua]

Hi nick,

Thanks a lot for your suggestion.  I actually tried to do that before, but it introduced another complication on managing the table, particularly re-using the available slots.

Consider the scenario:

Player fired 5 bullets in rapid succession.   The first bullet went of the screen; 2nd and 3rd still in view; the 4th bullet hits a target, 5th bullet still on the way up.  At this point the code could now have removed the bullets in index location 1 and 4, leaving their space available for re-use.   Now the player taps again to fire three more… naturally you would scan the table for available slot and re-use them rather than just extending the array at the end.  Now consider at this point the 2nd location is already available too, and so on and so forth.  This whole process of managing the table eventually led to timing issues and I couldn’t find a better way .   

Here is my method for firing&handling multiple bullets – I removed all the irrelevant code to make it more readable –

local PlayerBullets = {} local function shootBullet()     audio.play( tank\_shot )            local bullet = display.newGroup()         local pt = mathapi.rotateAboutPoint( {x=user\_turret.x,y=user\_turret.y-50}, user\_turret, user\_turret.rotation - 90 )                 --bullet attributes           bullet.body = loader:createSpriteFromSHDocument("Weapon3ex", "GameFX", "LevelAssets.pshs"); -- load bullet image       bullet.body.x = pt.x; bullet.body.y = pt.y;       physics.addBody( bullet.body , "dynamic", { friction= 0, bounce= 1.0, density= .6, radius= 7, filter = bulletCollisionFilter} );       bullet.body:scale(.6,.54);                bullet.body.class = "bullet"       bullet.body.isBullet = true                bullet.body.rotation = user\_turret.rotation + 90              bullet.body:applyForce( pt.x - user\_turret.x, pt.y - user\_turret.y, user\_turret.x, user\_turret.y )             -- collision function        local onPlayerBulletCollision = function(self, event)                               if (event.phase == "began") then                 if (event.other.class == "EnemyBullet") then                            display.remove(self)                     display.remove(event.other)                 end                          elseif (event.phase == "ended") then                              audio.play(Bullet\_Impact)                 display.remove(self)                   end         return true       end       bullet.body.collision = onPlayerBulletCollision       bullet.body:addEventListener("collision", bullet.body)             bullet:insert( bullet.body )       camera:add(bullet, 2, false)             table.insert(PlayerBullets, bullet)            return bullet       end  

Hope it helps.

Cheers!

-Saer

Edit: I use an invisible but hit testable rectangle for detecting touch events. When the user touches the screen to fire, it calls the ‘shootBullet’ function. Since the bullets are created dynamically and added to the table as individual bullets, each bullet receives the specified attributes and therefore behaves as a single entity.
(it’s currently 3:00 A.M. so I’m hoping that was clear enough.)

Here’s a tutorial on a scrolling shooter game with bullets:

http://karnakgames.com/wp/2011/02/how-to-make-a-simple-physics-based-shooter-game-with-the-corona-sdk/