Problem with event.other during collision

Hi everyone,

I’m having a problem during execution of my game: the error message is “attempt to index field ‘other’ (a nil value)” that happens when there is a collision between the ball and the brick that is handled by the function removeBrick.

I removed some parts of code like variables declaration, and small function to load/save tables.

Can someone help me? Thanks.

function scene:create(event) sceneGroup = self.view print("game: create event") end function scene:show(event) sceneGroup = self.view print("game: show event") buildScene() loadSettings() buildLevel(gameSettings.currentLevel) end function scene:hide() print("game: hide event") end function scene:destroy() print("game: destroy event") end scene:addEventListener("create",scene) scene:addEventListener("show",scene) scene:addEventListener("hide",scene) scene:addEventListener("destroy",scene) function buildScene() print("building scene...") addBackground() addPaddleAndBall() setPaddleAndBall() -- function to set at start position paddle and ball (in case of restart or death) addUi() sceneGroup:insert(mainGroup); sceneGroup:insert(uiGroup); sceneGroup:insert(bricksGroup); sceneGroup:insert(alertGroup); sceneGroup:insert(pauseGroup) print("scene ready, tap listener up on background") end function addBackground() background = display.newImageRect("resources/background.png",W,H) background.x = display.contentCenterX; background.y = display.contentCenterY mainGroup:insert(background) print("add background") end function addPaddleAndBall() paddle = display.newImageRect("resources/paddle.png",200,50) ball = display.newImageRect("resources/ball.png",50,50) ball.name = "ball"; paddle.name = "paddle" mainGroup:insert(paddle); mainGroup:insert(ball) physics.addBody(paddle, "static",{density = 1, friction = 0, bounce = 0}) physics.addBody(ball, "dynamic", {density = 1, friction = 0, bounce = 0}) print("added paddle and ball and loaded on physics") end function setPaddleAndBall() paddle.x = W/2; paddle.y = 1660 ball.x = W/2; ball.y = 1600 background:addEventListener("tap",startGame) print("set paddle and ball") end function addUi() scoreText = display.newText("Score:",200,150,font,40) scoreNumber = display.newText(score,400,150,font,40) livesText = display.newText("Lives:",W-300,150,font,40) livesNumber = display.newText(lives,W-100,150,font,40) addPauseBtn() uiGroup:insert(scoreText); uiGroup:insert(scoreNumber); uiGroup:insert(livesText); uiGroup:insert(livesNumber); uiGroup:insert(pauseBtn) uiGroup:toFront() print("add ui elements") end function buildLevel(levelNumber) local length = table.maxn(levels[levelNumber]) bricksGroup:toFront() for i = 1, length do for j = 1, bricks\_row\_length do if(levels[levelNumber][i][j] == 1) then insertBrick(levels[levelNumber][i][j],i,j) end end end end function insertBrick(type,i,j) brick = display.newImageRect("resources/0.png",brick\_width,brick\_height) brick.name = "brick" brick.x = 100 + brick\_width \* j brick.y = 400 + brick\_height \* i physics.addBody(brick, "static", {density = 1, friction = 0, bounce = 0}) bricksGroup:insert(brick) end function startGame() print("starting game") background:removeEventListener("tap", startGame) gameListeners("add") end function bounce(event) ySpeed = -5 if((ball.x + ball.width \* 0.5) \< paddle.x) then -- left side of paddle \>\> bounce to left xSpeed = -5 elseif((ball.x + ball.width \* 0.5) \>= paddle.x) then -- right side of paddle \>\> bounce to right xSpeed = 5 end end function ballUpdate(event) ball.x = ball.x + xSpeed ball.y = ball.y + ySpeed if(ball.x \< 0) then ball.x = ball.x + 3 xSpeed = -xSpeed end --Left if((ball.x + ball.width) \> display.contentWidth) then ball.x = ball.x - 3 xSpeed = -xSpeed end --Right if(ball.y \< 200) then ySpeed = -ySpeed end --Up if(ball.y + ball.height \> paddle.y + paddle.height) then ballDown() end --down/lose end function movePaddle(event) if event.phase == "began" then moveX = event.x - paddle.x; elseif event.phase == "moved" then paddle.x = event.x - moveX; end if((paddle.x - paddle.width \* 0.5) \< 0) then paddle.x = paddle.width \* 0.5; elseif((paddle.x + paddle.width \* 0.5) \> display.contentWidth) then paddle.x = display.contentWidth - paddle.width \* 0.5; end end function ballDown() updateLives() if lives == 0 then lose() else xSpeed = 0 ySpeed = 0 setPaddleAndBall() end end function updateLives() lives = lives - 1 livesNumber.text = lives livesNumber.anchorX = 0 livesNumber.x = W-100 end function removeBrick(event) -- Check the which side of the brick the ball hits, left, right if event.other.name == "brick" and ball.x + ball.width \* 0.5 \< event.other.x + event.other.width \* 0.5 then xSpeed = -xSpeed elseif event.other.name == "brick" and ball.x + ball.width \* 0.5 \>= event.other.x + event.other.width \* 0.5 then xSpeed = xSpeed end -- Bounce, Remove if event.other.name == "brick" then ySpeed = ySpeed \* -1 event.other:removeSelf() event.other = nil bricksGroup.numChildren = bricksGroup.numChildren - 1 end -- Check if all bricks are destroyed if bricksGroup.numChildren \< 0 then --alertScreen("YOU WIN!", "Continue") --gameEvent = "win" end end function gameListeners(action) if(action == 'add') then Runtime:addEventListener('enterFrame', ballUpdate) paddle:addEventListener('collision', bounce) paddle:addEventListener("touch",movePaddle) ball:addEventListener('collision', removeBrick) else Runtime:removeEventListener('enterFrame', ballUpdate) paddle:removeEventListener('collision', bounce) ball:removeEventListener('collision', removeBrick) paddle:removeEventListener("touch",movePaddle) end end

**“attempt to index field ‘other’ (a nil value)” **means that other simply doesn’t exist. In this case it probably means that you are trying to remove a display object that has already been removed. object:removeSelf() doesn’t check if an object is nil before trying to delete it, but display.remove(object) does.

Also, you don’t need to (and you don’t want to) write “bricksGroup.numChildren = bricksGroup.numChildren - 1”. That value is automatically updated whenever you remove a display object, so you’ll probably run into some issues with that as well (if you haven’t already). For instance, if you have two objects left and you remove one of them, then the numChildren count drops to 1, and you deduct one from it using that line. This is probably why you check if a negative amount of numChildren exist, because realistically there can’t be a negative number of display objects in a group.
 

Thanks for the reply XeduR,

i will fix that part of code. The problem still exists because the runtime error appears on first if statement in my function (this means when he checks if event.other is the brick and not when i have to remove the brick)

if event.other.name == "brick" and ball.x + ball.width \* 0.5 \< event.other.x + event.other.width \* 0.5 then xSpeed = -xSpeed elseif event.other.name == "brick" and ball.x + ball.width \* 0.5 \>= event.other.x + event.other.width \* 0.5 then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xSpeed = xSpeed end

That’s the problem with sharing a part of the code without detailed information on what happens, where and when, etc. If I can’t just start your project and try to replicate the issue, I can’t try to debug it accurately.

The issue is still the same though. If you receive a runtime error stating that “other” is nil, then it simply means that it doesn’t exist. It could be that it doesn’t have that .name value or it doesn’t exist. Write “print(event.other)” on the line before that if statement. If it prints nil, then other doesn’t exist. If it prints a table, then you can try printing event.other.name to see if name doesn’t exist.

You can also output all table contents by using https://docs.coronalabs.com/tutorial/data/outputTable/index.html

print(event.other) gives me “nil”

print(event.other.name) gives me “brick”

Thanks for taking the time to try and debug it :smiley: I don’t personally do anything with the values, but you do. :stuck_out_tongue:

I took another quick glance at your code and it seems that you might be using global collision handling instead of local, so go ahead and read: https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#collision-handling

i swapped my local handler in a global handler, and the function is

function onCollision(event) if ( event.phase == "began" ) then print( "began: " .. event.object1.name .. " and " .. event.object2.name ) elseif ( event.phase == "ended" ) then print( "ended: " .. event.object1.name .. " and " .. event.object2.name ) if event.object2.name == "brick" and ball.x + ball.width \* 0.5 \< event.other.x + event.other.width \* 0.5 then xSpeed = -xSpeed elseif event.object2.name == "brick" and ball.x + ball.width \* 0.5 \>= event.other.x + event.other.width \* 0.5 then xSpeed = xSpeed end -- Bounce, Remove if event.object2.name == "brick" then ySpeed = ySpeed \* -1 event.object2:removeSelf() event.object2 = nil end end

still same error, i tried to swap object2 in object1 (obviously i changed ball:addEventListener on Runtime:EventListener)

Hi @marcodelia1900,

According to documentation in global handler do not appear variable named event.other. It only exists in local handler.

Object References

Depending on whether collisions are detected locally or globally, references to the objects involved will vary. For more information about these two collision handling methods, see the Collision Detection guide.

 

Local Collision Event   Global Collision Event

 

event.target or self      event.object1

event.other                    event.object2

event.selfElement        event.element1

event.otherElement    event.element2

 

I would use local collision handler for one-many colision detection as @XeduR @Spyric suggested you.

 

Have a nice day:)

 

ldurniat

Hi @Idurniat,

the solution you quoted was the “global” one… the first solution that gave me same problem was the “local” one.

In local collision handling, you need to pass ( self, event ) into the function. I only said that you seemed to have used global as you hadn’t included both of them. This could be a reason as to why your event.other is nil.

But really, in the future, if you need help with debugging, try uploading a small sample project to the forums that demonstrates your issue. That way people will have a much easier time helping you. At this point it is just about guessing why event.other doesn’t exist within the scope of the function.

I can send you my project in private if you want (is really small)

Sure, you can send it to me via private discussion or at eetu.rantanen@spyric.com.

Just posting a status update here. I already sent the updated project back to marcodelia1900.

The collision function itself worked fine. The problems were in function scene:show, which was run during both “will” and “did” phases. This meant that all display objects were created twice and the reference to the original ball was lost immediately. Additionally, the bricks also created in a way that each subsequent brick took over the variable’s reference, etc.

So, when the collision function was initially called, it was called because two overlapping balls (one without a reference) were colliding.

There’s a lot left to work on the project, but at least now these major issues are addressed. :smiley:

Good luck!

Thanks again bro!

**“attempt to index field ‘other’ (a nil value)” **means that other simply doesn’t exist. In this case it probably means that you are trying to remove a display object that has already been removed. object:removeSelf() doesn’t check if an object is nil before trying to delete it, but display.remove(object) does.

Also, you don’t need to (and you don’t want to) write “bricksGroup.numChildren = bricksGroup.numChildren - 1”. That value is automatically updated whenever you remove a display object, so you’ll probably run into some issues with that as well (if you haven’t already). For instance, if you have two objects left and you remove one of them, then the numChildren count drops to 1, and you deduct one from it using that line. This is probably why you check if a negative amount of numChildren exist, because realistically there can’t be a negative number of display objects in a group.
 

Thanks for the reply XeduR,

i will fix that part of code. The problem still exists because the runtime error appears on first if statement in my function (this means when he checks if event.other is the brick and not when i have to remove the brick)

if event.other.name == "brick" and ball.x + ball.width \* 0.5 \< event.other.x + event.other.width \* 0.5 then xSpeed = -xSpeed elseif event.other.name == "brick" and ball.x + ball.width \* 0.5 \>= event.other.x + event.other.width \* 0.5 then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xSpeed = xSpeed end

That’s the problem with sharing a part of the code without detailed information on what happens, where and when, etc. If I can’t just start your project and try to replicate the issue, I can’t try to debug it accurately.

The issue is still the same though. If you receive a runtime error stating that “other” is nil, then it simply means that it doesn’t exist. It could be that it doesn’t have that .name value or it doesn’t exist. Write “print(event.other)” on the line before that if statement. If it prints nil, then other doesn’t exist. If it prints a table, then you can try printing event.other.name to see if name doesn’t exist.

You can also output all table contents by using https://docs.coronalabs.com/tutorial/data/outputTable/index.html

print(event.other) gives me “nil”

print(event.other.name) gives me “brick”

Thanks for taking the time to try and debug it :smiley: I don’t personally do anything with the values, but you do. :stuck_out_tongue:

I took another quick glance at your code and it seems that you might be using global collision handling instead of local, so go ahead and read: https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#collision-handling

i swapped my local handler in a global handler, and the function is

function onCollision(event) if ( event.phase == "began" ) then print( "began: " .. event.object1.name .. " and " .. event.object2.name ) elseif ( event.phase == "ended" ) then print( "ended: " .. event.object1.name .. " and " .. event.object2.name ) if event.object2.name == "brick" and ball.x + ball.width \* 0.5 \< event.other.x + event.other.width \* 0.5 then xSpeed = -xSpeed elseif event.object2.name == "brick" and ball.x + ball.width \* 0.5 \>= event.other.x + event.other.width \* 0.5 then xSpeed = xSpeed end -- Bounce, Remove if event.object2.name == "brick" then ySpeed = ySpeed \* -1 event.object2:removeSelf() event.object2 = nil end end

still same error, i tried to swap object2 in object1 (obviously i changed ball:addEventListener on Runtime:EventListener)