Game freezes and lags because of collision function [Video explanation included]

Yea title says it all ( almost ),

here is my code:

local function collisionFunction( event ) if "began" == event.phase and dead == false then --print("eobj1"..event.object1.name.."// eobj2:"..event.object2.name.."") if event.object1 and event.object2 then if event.object1.name == "zombie" and event.object2.name == "player" then -- remove part of player's hp -- remove part of zombie's hp when player is touching zombie local function substractPlayerHp( event ) --if zombie hit player then remove portion of his/hers hp player.hp = player.hp - 1 playerHpText.text = "HP: "..player.hp.."" -- if player's hp is or goes below 0 then make the player diee if player.hp \<= 0 then playerDeath() player.hp = 0 playerHpText.text = "HP: "..player.hp.."" end end substractPlayerHp() timer2 = timer.performWithDelay( 1000, substractPlayerHp, 0 ) elseif event.object1.name == "zombie" and event.object2.name == "bullet" then event.object1.hp = event.object1.hp - bulletDamage display.remove( event.object2 ) -- if zombie's hp is equal or less than 0 then if event.object1.hp \<= 0 then totalZombies = totalZombies - 1 -- remove bullet and zombie display.remove( event.object1 ) end elseif event.object1.name == "ammobox" and event.object2.name == "player" then -- reset player's magazine & bullet situation bullets = maxBullets leftBullets = maxLeftBullets bulletText.text = ""..leftBullets.."┃"..bullets.."" display.remove( event.object1 ) end end elseif "ended" == event.phase then -- cancel timer2 only if it exists otherwise it will be error if timer2 then timer.cancel( timer2 ) end end end Runtime:addEventListener( "collision", collisionFunction )

so in my map I have 1 player, 30 zombies ( 1 spawn every 2 seconds ( 30 zombies is max ) ), and 1 ammo box spawn in a random place in map every 30 seconds.

When I have this function and I spread bullets to the zombies the game freezes. Here is video explanation:

https://www.youtube.com/watch?v=QuAAu7V_530&feature=youtu.be

So what I think happens here is that there is kind of some low memory thing, cause the collision function is enterFrame which means it udpate 60 times every second AND there is alot of if & elseif logic etc…

So if you have any ideas how to fix this please let me know.

Also I tried the game without the collision function and it worked out good without the collision function.

Coder101

There are also other weird freeses and bugs it might still be because of the collision detection function.

@Coder

I’m not sure what that code is, but it isn’t a going to achieve collision detection.  Ate least it looks wrong to me.

I’ll post back in a few with what I think the code should look like. OK?

Sorry having trouble reading your code still…

Sorry to hammer on you here, but I’m so used to table listeners that when I see folks using function listeners it makes me scratch my head.

Local Listeners (I call these table listeners; I’m sure at one time that was the accepted name… weird)

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#local-collision-handling

Global Listeners (I call these function listeners; I’m sure at one time that was the accepted name… weird)

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#global-collision-handling

** UPDATED TO USE CURRENTLY ACCEPTED TERMS **

Hi again.  This code (below) uses some advanced techniques, but I think you’ll be able to work them out.  If not, ask specific numbered questions and I’ll respond in kind.

Words of Advice and Notes

  1. Define the listeners once and use the local style listeners. This is more efficient.

  2. Don’t make a single event listener and use it for all object types. This is a bad practice because:

  • it is confusing

  • it can get too bit and quickly sprawl

  • it is generally error prone

  1. Code not tested so it may have syntax errors and or typos…
     

The Code
 

local function zombieCollision( self, event ) local other = event.other local phase = event.phase -- Only do work on 'began' phase ... if( event ~= "began" ) then return false end -- Zombie collides with 'player' that has not yet been hit by zombie... if( other.name == "player" and other.timer == nil ) then -- Tip: In the following function, I re-used the word 'self'. -- This may be confusing. So, be aware, because the function timer() -- is a method (attached to an object) 'self' is the object it is attached to -- i.e. the player object. -- I could just as easily have replaced the word self with 'player' or 'obj' -- The name is not special, the scope is. other.timer = function( self ) -- Safety check to see if player was removed (display object destroyed) -- if( self.removeSelf == nil or type(self.removeSelf) ~= "function" ) then return end self.hp = self.hp - 1 playerHpText.text = "HP: " .. tostring(self.hp) if( self.hp \<= 0 ) then self.hp = 0 playerHpText.text = "HP: " .. tostring(self.hp) playerDeath() return end -- Call again in 1 second timer.performWithDelay( 1000, self ) end -- Do damage now .... other:timer() return true -- Zombie collided with 'bullet' elseif( other.name == "player" ) then self.hp = self.hp - 1 if( self.hp \<= 0 ) then display.remove( self ) totalZombies = totalZombies - 1 end return true end return false end local function playerCollision( self, event ) local other = event.other local phase = event.phase -- Only do work on 'began' phase ... if( event ~= "began" ) then return false end -- Player collides with 'ammobox' if( other.name == "ammobox" ) then bullets = maxBullets leftBullets = maxLeftBullets bulletText.text = ""..leftBullets.."┃"..bullets.."" -- I assume you want to remove the ammo box? display.remove( other ) return true end return false end

… now when you create zombies, be sure the function zombieCollision is in scope and do this:

local zombie = display.newImageRect( ... ) -- make your enemy however you want. physics.addBody( zombie, { ... } ) -- set up your body however you want. zombie.collision = zombieCollision -- 'attach' the function defined above to the enemy. -- this re-uses the same code for all enemies. zombie:addEventListener("collision") -- Start listening for the collision event

Same idea for player…
 

local player = display.newImageRect( ... ) physics.addBody( player, { ... } ) player.collision = playerCollision player:addEventListener("collision")

I hope the code post wasn’t confusing.  I converted to the style I am most familiar with because I almost never use global listeners for touch, collision, or other events.

I believe you’re new to coding or at least new to Corona, so the natural progression is to write listener code the way you did.  

However, I believe that once you switch over to local listeners with small scopes (i.e. the listener only handles events directly related to the object) you’ll find your code is easier to write, read, and less buggy. 

Before you ask, let me explain a piece of code above.  Specifically the timer code.
 
This code uses a trick.  

-- 1. Create a object (call it player) local player = function display.newCircle( 10, 10, 10 ) -- 2. Add a method named 'timer' to the player object player.timer = function( self ) -- In this scope, self is the player object. -- The word 'self' is not special and could be anything you want. -- Call this method again in 1 second timer.performWithDelay( 1000, self ) end -- 3. Immediately call the timer in one second timer.performWithDelay( 1000, player )

Step 3 shows the trick.
 
I have created a field on the player object and attached a function to that field (essentially the function is now a method).
 
When I call timer.performWithDelay()  I only need to pass a reference to the object instead of a function as you normally would.  
 
The timer.* library will see that the second argument in performWithDelay() is NOT a function and will assume it is an object AND it will check to see if the object has a ‘timer’ field pointing to a function. It will then call that function as a method.
 
This is a very powerful concept although it may seem a little strange at first.
 
The takeaway (besides this specific example) is that ALL events can also be coded this way.  i.e. Attach a method to the object on a field that has the same name as the event.

  • obj.collision = function( self, event ) … end
  • obj.touch = function( self, event ) … end
  • obj.finalize = function( self) … end
  • … and so on.

PS - These are also acceptable ways of coding the timer method:

function player.timer( self ) -- ... end function player:timer() -- self is implied -- ... end function player.timer( obj ) -- use a different word, instead of 'self' -- ... end

There are also other weird freeses and bugs it might still be because of the collision detection function.

@Coder

I’m not sure what that code is, but it isn’t a going to achieve collision detection.  Ate least it looks wrong to me.

I’ll post back in a few with what I think the code should look like. OK?

Sorry having trouble reading your code still…

Sorry to hammer on you here, but I’m so used to table listeners that when I see folks using function listeners it makes me scratch my head.

Local Listeners (I call these table listeners; I’m sure at one time that was the accepted name… weird)

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#local-collision-handling

Global Listeners (I call these function listeners; I’m sure at one time that was the accepted name… weird)

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#global-collision-handling

** UPDATED TO USE CURRENTLY ACCEPTED TERMS **

Hi again.  This code (below) uses some advanced techniques, but I think you’ll be able to work them out.  If not, ask specific numbered questions and I’ll respond in kind.

Words of Advice and Notes

  1. Define the listeners once and use the local style listeners. This is more efficient.

  2. Don’t make a single event listener and use it for all object types. This is a bad practice because:

  • it is confusing

  • it can get too bit and quickly sprawl

  • it is generally error prone

  1. Code not tested so it may have syntax errors and or typos…
     

The Code
 

local function zombieCollision( self, event ) local other = event.other local phase = event.phase -- Only do work on 'began' phase ... if( event ~= "began" ) then return false end -- Zombie collides with 'player' that has not yet been hit by zombie... if( other.name == "player" and other.timer == nil ) then -- Tip: In the following function, I re-used the word 'self'. -- This may be confusing. So, be aware, because the function timer() -- is a method (attached to an object) 'self' is the object it is attached to -- i.e. the player object. -- I could just as easily have replaced the word self with 'player' or 'obj' -- The name is not special, the scope is. other.timer = function( self ) -- Safety check to see if player was removed (display object destroyed) -- if( self.removeSelf == nil or type(self.removeSelf) ~= "function" ) then return end self.hp = self.hp - 1 playerHpText.text = "HP: " .. tostring(self.hp) if( self.hp \<= 0 ) then self.hp = 0 playerHpText.text = "HP: " .. tostring(self.hp) playerDeath() return end -- Call again in 1 second timer.performWithDelay( 1000, self ) end -- Do damage now .... other:timer() return true -- Zombie collided with 'bullet' elseif( other.name == "player" ) then self.hp = self.hp - 1 if( self.hp \<= 0 ) then display.remove( self ) totalZombies = totalZombies - 1 end return true end return false end local function playerCollision( self, event ) local other = event.other local phase = event.phase -- Only do work on 'began' phase ... if( event ~= "began" ) then return false end -- Player collides with 'ammobox' if( other.name == "ammobox" ) then bullets = maxBullets leftBullets = maxLeftBullets bulletText.text = ""..leftBullets.."┃"..bullets.."" -- I assume you want to remove the ammo box? display.remove( other ) return true end return false end

… now when you create zombies, be sure the function zombieCollision is in scope and do this:

local zombie = display.newImageRect( ... ) -- make your enemy however you want. physics.addBody( zombie, { ... } ) -- set up your body however you want. zombie.collision = zombieCollision -- 'attach' the function defined above to the enemy. -- this re-uses the same code for all enemies. zombie:addEventListener("collision") -- Start listening for the collision event

Same idea for player…
 

local player = display.newImageRect( ... ) physics.addBody( player, { ... } ) player.collision = playerCollision player:addEventListener("collision")

I hope the code post wasn’t confusing.  I converted to the style I am most familiar with because I almost never use global listeners for touch, collision, or other events.

I believe you’re new to coding or at least new to Corona, so the natural progression is to write listener code the way you did.  

However, I believe that once you switch over to local listeners with small scopes (i.e. the listener only handles events directly related to the object) you’ll find your code is easier to write, read, and less buggy. 

Before you ask, let me explain a piece of code above.  Specifically the timer code.
 
This code uses a trick.  

-- 1. Create a object (call it player) local player = function display.newCircle( 10, 10, 10 ) -- 2. Add a method named 'timer' to the player object player.timer = function( self ) -- In this scope, self is the player object. -- The word 'self' is not special and could be anything you want. -- Call this method again in 1 second timer.performWithDelay( 1000, self ) end -- 3. Immediately call the timer in one second timer.performWithDelay( 1000, player )

Step 3 shows the trick.
 
I have created a field on the player object and attached a function to that field (essentially the function is now a method).
 
When I call timer.performWithDelay()  I only need to pass a reference to the object instead of a function as you normally would.  
 
The timer.* library will see that the second argument in performWithDelay() is NOT a function and will assume it is an object AND it will check to see if the object has a ‘timer’ field pointing to a function. It will then call that function as a method.
 
This is a very powerful concept although it may seem a little strange at first.
 
The takeaway (besides this specific example) is that ALL events can also be coded this way.  i.e. Attach a method to the object on a field that has the same name as the event.

  • obj.collision = function( self, event ) … end
  • obj.touch = function( self, event ) … end
  • obj.finalize = function( self) … end
  • … and so on.

PS - These are also acceptable ways of coding the timer method:

function player.timer( self ) -- ... end function player:timer() -- self is implied -- ... end function player.timer( obj ) -- use a different word, instead of 'self' -- ... end