Question About "if" and "elseIf"

I don’t know why, but the code after “elseif” doesn’t work.
The first part of “if” function works, but second doesn’t work, it doesn’t print anything even if the beam is coliding with zombie…

local function zombieCollision(self, event)
   if event.other == hero then

      if event.phase == "began" then
         hero.isColliding = true
         checkHeroCollision()
      elseif event.phase == "ended" then
         hero.isColliding = false
      end
      
   elseif event.other == beam then
      print("ouch")

   end
end

(Post edited by moderator; Please format code in posts; Click pencil icon to review changes.)

Can you share a sample project where we can see how you define hero and beam? (It would be a good idea to use simple rectangles / circles as placeholder images)

  1. I am assuming you did this:
local zombie = someCreateFunction()
zombie.collision = zombieCollision
zombie:addEventListenter( 'collision' )
  1. If you delete the zombie before the collision ends, you won’t get an ‘ended’ phase.

ok, thanks, but I am asking about this part:

...
   elseif event.other == beam then
      print("ouch")

   end
end

Yes, sure.
I will do it later today or tomorrow, because I haven’t much time today.

@ConveyedRex7592

Try making this change to your code so you can debug it on your own more easily:

local function zombieCollision(self, event)
   print( "zombieCollision event.other == ", event.other, event.time )
   print( "zombieCollision hero == ", hero, event.time )
   print( "zombieCollision beam == ", beam, event.time )
   print( "zombieCollision hero.isColliding == ", hero.isColliding, event.time )
   print( "zombieCollision other =? hero ", event.other == hero, event.time )
   print( "zombieCollision other =? beam ", event.other == beam, event.time )
   print ( "----------------\n")
   if event.other == hero then

      if event.phase == "began" then
         hero.isColliding = true
         checkHeroCollision()
      elseif event.phase == "ended" then
         hero.isColliding = false
      end
      
   elseif event.other == beam then
      print("ouch")

   end
end

I did this and when beam collides with zombie it says hero false and beam false

The thing is in sample everythings works perfectly… :thinking:

Sample.zip (51.6 KB)

I looked at your example. The ‘zombie’ never collides with the ‘beam’, so that statement will never trigger.

I tweaked your sample, look in the folder named ‘tweaked’. It now hits both and all *if sections trigger.
tweaked_sample.zip (107.6 KB)

The thing is what sample was very simplyfied version of my project.
And in my actual project when zombie collides with beam it just says:

|17:52:49.295 zombieCollision event.other == |table: 0EBB0AE0|nil|
|17:52:49.295 zombieCollision hero == |table: 0EC77330|nil|
|17:52:49.295 zombieCollision beam == |nil|nil|
|17:52:49.295 zombieCollision hero.isColliding == |nil|nil|
|17:52:49.295 zombieCollision other =? hero |false|nil|
|17:52:49.295 zombieCollision other =? beam |false|nil|

So it says what it collides, but not with beam and not with hero.
Maybe I added beam not correctly?

Very strange things are happening…

I changed one line of code:
Before:

local function zombieCollision(**self, event**)

  print( "zombieCollision other =? hero ", event.other == hero, event.time )
  print( "zombieCollision other =? beam ", event.other == beam, event.time )
  print ( "----------------\n")

  if event.other == hero then

    if event.phase == "began" then
      hero.isColliding = true
      checkHeroCollision()

    elseif event.phase == "ended" then
      hero.isColliding = false
    end

  elseif event.other == beam then
    print("ouch")

  end
end

After Change:

local function zombieCollision(**event**)

  print( "zombieCollision other =? hero ", event.other == hero, event.time )
  print( "zombieCollision other =? beam ", event.other == beam, event.time )
  print ( "----------------\n")

  if event.other == hero then

    if event.phase == "began" then
      hero.isColliding = true
      checkHeroCollision()

    elseif event.phase == "ended" then
      hero.isColliding = false
    end

  elseif event.other == beam then
    print("ouch")

  end
end

And now it says what beam is colliding with zombie even if hero is colliding with zombie…

Maybe I should update?

My version: Corona 2018.3326

It is your code, not Corona. Collisions have no issues otherwise there would be many posts about this.

Please note, I think you’re approaching this incorrectly, by referencing objects directly in the collision code.

You have this:

local beam = display.newRect(display.contentCenterX + 100, display.contentCenterY, 10, 150)
local zombie = display.newRect( display.contentCenterX , display.contentCenterY - 200, 30, 90 )
local hero = display.newRect( display.contentCenterX, display.contentCenterY, 80, 120 )

local function zombieCollision(self, event)
  if event.other == hero then
    if event.phase == "began" then
      print("\nhero is colliding with zombie.\n")
    elseif event.phase == "ended" then
      print("\nhero is not colliding with zombie anymore.\n")
    end
  elseif event.other == beam then
  end
end

zombie.collision = zombieCollision
zombie:addEventListener("collision", zombie)

This is a much cleaner way to do it:


local beam      = display.newRect(display.contentCenterX + 100, display.contentCenterY, 10, 150)
beam.objType    = "beam"

local zombie    = display.newRect( display.contentCenterX , display.contentCenterY - 200, 30, 90 )
zombie.objType  = "zombie"

local hero      = display.newRect( display.contentCenterX, display.contentCenterY, 80, 120 )
hero.objType    = "hero"

local function zombieCollision(self, event)
  local other = event.other
  local phase = event.phase
  print( self.objType, " collising with ", other.objType, " phase: ", phase )

  if( other.objType == "hero" ) then
    if( phase == "began" ) then
      print("  -- hero; began ")
    elseif phase == "ended" then
      print("  -- hero; ended ")
    end
  elseif( other.objType == "beam" ) then
    print("  -- beam ")
  end
end

zombie.collision = zombieCollision
zombie:addEventListener("collision", zombie)

Looks like this method works :slight_smile:.
Thanks for help!

Now it prints “ouch” few times… :confused:
I remove object beam after a short time(about 700ms) maybe it is a reason?
I understand what engine in this situation registers collision few times.

With hero and zombie everything is good it prints began and ended, but with beam and zombie it prints began and began (sometimes more than 2 times).

   local function zombieCollision(self, event)
      local other = event.other
      local phase = event.phase
      print( self.objType, " collising with ", other.objType, " phase: ", phase )

      if( other.objType == "hero" ) then
        if( phase == "began" ) then
          hero.isColliding = true
          checkHeroCollision()
        elseif phase == "ended" then
          hero.isColliding = false
        end
      elseif( other.objType == "beam" ) then
        if( phase == "began" ) then
          print("ouch")
        end
      end
    end
  1. If there is any bouncing and/or movement, you will see multiple collisions from a falling body as it bounces: began, ended, began, ended, …

  2. If you have multiple bodies using this listener and they are all moving, you will see multiple (possibly) overlapping collisions: began, began, ended, began, ended, ended, …

  3. As far as I know and have ever experienced, between body A and body B, you will never see began, began. So, I suspect something else is going on.

  • How many zombies are there?
  • How many beams?
  • I assume you only have one hero?

If you have multiple zombies and multiple beams, then all bets are off and you need to print out messages that clearly identify which zombie and which beam are being collided with if you’re trying to some specific thing.

Note: To date, I haven’t heard you say what you are trying to achieve. What is your end goal of all this collision detection and classification?

I’m pretty sure you’re still making errors in your code. You need to upload your game if you want more help on this. It’s a bit too hard to work out what is going on and why you think it is wrong.

  1. I think no, beam is static object, zombie is dynamic. And I have turned off gravity, because I am creating top-view game.
  2. Right now, no.
  • I have 1 zombie, but in future I will make what there will be more than 1 zombie.
  • 1 beam, the beam is kinda hitbox, what appears when player shoots from a weapon and disapears after 700ms
    1. Yes, one hero.

My goal is when beam (hitbox) collides with zombie zombie loses some of his hp.
And as I undestand particles can’t collide, so I made a hitbox to check collision.

So I need to put full file of code?

#1 - If the zombie moves (gravity or no) you can effectively have bouncing.

#3 - Before uploading, consider uploading just a log showing this problem. That way we can compare that to what you’re describing.

– PLEASE UPLOAD LOG BEFORE UPLOADING PROJEC –

However, you may well need to let someone look at your game eventually. If you do end up doing that, … zip up your project and upload it or provide a dropbox link.

If you’re not comfortable with that, you can PM me the link and I’ll look at it on the side, but be aware it may take me a day to get to this. Between my day job and some priority client work my day is full.

Whatever you do, be sure to give clear steps to reproduce the issue.

If your “log” had meaning of console output:

22:25:50.976 zombie collising with hero phase: began
22:25:51.330 zombie collising with hero phase: ended
22:25:53.692 zombie collising with beam phase: began
22:25:53.692 beam
22:25:53.780 zombie collising with beam phase: began
22:25:53.780 beam

If your “log” had meaning of “what the problem is?”:

When hero and zombie collides everything is ok, collision starts and ends, but when beam and zombie collides collision starts, but it repeats few times and never ends…
Maybe I can just put some delay just ignore what collision never ends or repeats?

And in code some particle effects are just for testing.

Game scene code:

local composer = require( "composer" )
local joystickPlugin = require( "plugin.joystick")
local physics = require( "physics" )
local json = require( "json" )
local scene = composer.newScene();

function scene:create(event)
    physics.start()
    physics.setGravity(0,0)
    local sceneGroup = self.view;
    system.activate("multitouch")

    local AttackButton = display.newImageRect("Images/mehaAttackButton.png",70,70)
    AttackButton.x = 500
    AttackButton.y = 300
    local AttackAnimationSheetData = {
      width = 400,
      height = 400,
      numFrames = 10,
      sheetContentWidth = 800,
      sheetContentHeight = 2000
    }
    local AttackAnimationSheet = graphics.newImageSheet("Images/mehaAttackAnimation.png", AttackAnimationSheetData)
    local sequenceData = {
      { name = "Attack", start = 1, count = 10, time = 500, loopCount = 1}
    }
    local props = {
      x = -30,
      y = 300,
      backgroundRadius = 40,
      movedStickRadius = 20
    }

    local filePath = system.pathForFile( "beamParticleData.json" )
    local f = io.open( filePath, "r" )
    local beamParticleData = f:read( "*a" )
    f:close()

    local beamParticleParams = json.decode( beamParticleData )

    local filePath = system.pathForFile( "chargeParticleData.json" )
    local f = io.open( filePath, "r" )
    local chargeParticleData = f:read( "*a" )
    f:close()

    local chargeParticleParams = json.decode( chargeParticleData )

    local filePath = system.pathForFile( "blastParticleData.json" )
    local f = io.open( filePath, "r" )
    local blastParticleData = f:read( "*a" )
    f:close()

    local blastParticleParams = json.decode( blastParticleData )

    local map = display.newImage("Images/survivalModeMap.png")
    map.x = display.contentCenterX
    map.y = display.contentCenterY

    local hero = display.newImage("Images/mehaHat.png")
    hero.x = display.contentCenterX
    hero.y = display.contentCenterY
    physics.addBody(hero,"static")
    hero.direction = "up"
    hero.hp = 100
    hero.objType = "hero"
    hero.selectedWeapon = "anti-alien"
    hero.anotherWeapon = "empty"

    local zombie = display.newImage("Images/zombie.jpg")
    zombie.x = display.contentCenterX
    physics.addBody(zombie,"dynamic")
    zombie.y = -100
    zombie.hp = 30
    zombie.objType = "zombie"

    local function shoot()
      if hero.selectedWeapon == "anti-alien" then
        if hero.direction == "up" then
          local beamParticle = display.newEmitter( beamParticleParams )
          beamParticle.x = display.contentCenterX + 50
          beamParticle.y = display.contentCenterY - 170
          local beam = display.newRect( display.contentCenterX + 50, display.contentCenterY - 130, 15, 100 )
          beam.fill = {1,0,0}
          beam.alpha = 0
          beam.objType = "beam"
          physics.addBody(beam,"static")
          timer.performWithDelay(700, function()
            beam:removeSelf()
          end)
        end
      end
    end

    if hero.selectedWeapon == "anti-alien" then
      if weapon then
        weapon:removeSelf()
      end
      local weapon = display.newImageRect("Images/anti-alien.png",
       35, 100)
       weapon.x = display.contentCenterX + 50
       weapon.y = display.contentCenterY - 30
       AttackButton.isVisible = false
       local shootButton = display.newImageRect("Images/shootButton.png", 70, 70)
       shootButton.x = 500
       shootButton.y = 300

       shootButton:addEventListener("touch", shoot)

       hero:toFront()

       local chargeParticle = display.newEmitter( chargeParticleParams )
       chargeParticle.x = display.contentCenterX + 50
       chargeParticle.y = display.contentCenterY - 75

       local blastParticle = display.newEmitter( blastParticleParams )
       blastParticle.x = display.contentCenterX - 100
       blastParticle.y = display.contentCenterY
    end

    local random = math.random(4)

    local hp = display.newText("Hp: "..hero.hp,display.contentCenterX,300,
    "Lobster-Regular", 50)

    local function zombieSpawn()

    end

    local checkHeroCollision
    checkHeroCollision = function()
      if hero.isColliding then
        hero.hp = hero.hp - 10
        hp.text = "Hp:"..hero.hp
        timer.performWithDelay(500, checkHeroCollision)
      end
    end

    local function zombieCollision(self, event)
      local other = event.other
      local phase = event.phase
      print( self.objType, " collising with ", other.objType, " phase: ", phase )

      if( other.objType == "hero" ) then
        if( phase == "began" ) then
          hero.isColliding = true
          checkHeroCollision()
        elseif phase == "ended" then
          hero.isColliding = false
        end
      elseif( other.objType == "beam" ) then
        if( phase == "began" ) then
          print("beam")
        end
      end
    end

    zombie.collision = zombieCollision
    zombie:addEventListener("collision", zombie)

    local function AttackDamage(self, event)
      if event.other == zombie then
        if event.phase == "began" then
          zombie.hp = zombie.hp - 10
          if zombie.hp <= 0 then
            Runtime:removeEventListener("enterFrame", enemy)
            timer.performWithDelay(1, function()
              zombie:removeSelf()
            end)
          end
        end
      end
    end
      local Joystick = joystickPlugin.newJoystick(props)
      Joystick.background:setFillColor(0.5)
      Joystick.movedStick:setFillColor(0.7)
      function enterFrame(event)
        if Joystick.isActivated() then
          local vector = Joystick.getVector()
          if vector.x < -0.2 then
            map.x = map.x + 2
            zombie.x = zombie.x + 2
            hero.rotation = 270
            hero.direction = "left"
          elseif vector.x > 0.2 then
            map.x = map.x - 2
            zombie.x = zombie.x -2
            hero.rotation = 90
            hero.direction = "right"
          end
          if vector.y < -0.2 then
            map.y = map.y + 2
            zombie.y = zombie.y + 2
            hero.rotation = 0
            hero.direction = "up"
          elseif vector.y > 0.2 then
            map.y = map.y - 2
            zombie.y = zombie.y-2
            hero.rotation = 180
            hero.direction = "down"
          end
        end
      end
      local AttackCoolDown = 0
      function Attack(event)
        if(event.phase == "began") then
          if AttackCoolDown == 0 then
            local attackHitbox = display.newRect( display.contentCenterX, display.contentCenterY, 100, 90 )
            attackHitbox.fill = {1,0,0}
            attackHitbox.alpha = 0
            physics.addBody(attackHitbox, "static")
            attackHitbox.collision = AttackDamage
            attackHitbox:addEventListener("collision", attackHitbox)
            timer.performWithDelay( 200, function()
            end)
            local mehaAttackAnimation = display.newSprite( AttackAnimationSheet, sequenceData )
            mehaAttackAnimation.x = display.contentCenterX
            mehaAttackAnimation.y = display.contentCenterY
            if hero.direction == "up" then
              mehaAttackAnimation.rotation = 0
              attackHitbox.x = display.contentCenterX
              attackHitbox.y = display.contentCenterY - 70
              attackHitbox.rotation = 0
            elseif hero.direction == "right" then
              mehaAttackAnimation.rotation = 90
              attackHitbox.x = display.contentCenterX + 70
              attackHitbox.y = display.contentCenterY
              attackHitbox.rotation = 90
            elseif hero.direction == "down" then
              mehaAttackAnimation.rotation = 180
              attackHitbox.x = display.contentCenterX
              attackHitbox.y = display.contentCenterY + 70
              attackHitbox.rotation = 180
            elseif hero.direction == "left" then
              mehaAttackAnimation.rotation = 270
              attackHitbox.x = display.contentCenterX - 70
              attackHitbox.y = display.contentCenterY
              attackHitbox.rotation = 180
            end
            mehaAttackAnimation:play()
            AttackCoolDown = 1
            timer.performWithDelay( 500, function()
              attackHitbox:addEventListener("collision", zombie)
              attackHitbox:removeSelf()
              mehaAttackAnimation:removeSelf()
              AttackCoolDown = 0
            end)
          end
        end
      end
      function hpDetection(event)
        if hero.hp == 0 then
          composer.removeScene( "scenes.survival_gamemode" )
          Runtime:removeEventListener("enterFrame", enemy)
          Runtime:removeEventListener("enterFrame", hpDetection)
          AttackButton:removeEventListener("touch", Attack)
          zombie:removeEventListener("collision", zombie)
          Runtime:removeEventListener("enterFrame", enterFrame)
          zombie:removeSelf()
          hero:removeSelf()
          AttackButton:removeSelf()
          hp:removeSelf()
          map:removeSelf()
          Joystick = nil
        end
      end

    local function enemy()
        if zombie.x < display.contentCenterX then
          zombie.x = zombie.x + 1
        end
        if zombie.y > display.contentCenterY then
          zombie.y = zombie.y - 1
        end
        if zombie.x > display.contentCenterX then
          zombie.x = zombie.x - 1
        end
        if zombie.y < display.contentCenterY then
          zombie.y = zombie.y + 1
        end
        zombie.rotation = 90 + math.deg(math.atan2(zombie.y - hero.y, zombie.x - hero.x))
    end
    --Runtime:addEventListener("enterFrame", enemy)
    Runtime:addEventListener("enterFrame", hpDetection)
    AttackButton:addEventListener("touch", Attack)
    Runtime:addEventListener("enterFrame", enterFrame)
end
function scene:destroy(event)
  physics.stop()
  composer.gotoScene("scenes.survival_gamemode")
end
scene:addEventListener("create", scene);
scene:addEventListener("destroy", scene);
return scene;

Sorry boss I don’t want to work with the scene code. I want to be able to run it with no effort or typing.

Having a working project lets me tweak it too.