Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Problem with event.other during collision
Started by marcodelia1900 Dec 06 2018 04:38 AM

- - - - -
13 replies to this topic
collision event other

Best Answer XeduR @Spyric , 06 December 2018 - 02:49 PM

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. :D

Good luck!

[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

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


[TOPIC: post.html]
#2

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

"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.
 



[TOPIC: post.html]
#3

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

"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
        xSpeed = xSpeed
end


[TOPIC: post.html]
#4

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

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



[TOPIC: post.html]
#5

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

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"



[TOPIC: post.html]
#6

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

Thanks for taking the time to try and debug it :D I don't personally do anything with the values, but you do. :P

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



[TOPIC: post.html]
#7

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

Thanks for taking the time to try and debug it :D I don't personally do anything with the values, but you do. :P

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)



[TOPIC: post.html]
#8

ldurniat

[GLOBAL: userInfoPane.html]
ldurniat
  • Contributor

  • 374 posts
  • Corona SDK

Hi @marcodelia1900,

 

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)

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


[TOPIC: post.html]
#9

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

 

Hi @marcodelia1900,

 

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

 

 
 
Better use local collision handler 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.



[TOPIC: post.html]
#10

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

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.



[TOPIC: post.html]
#11

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

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)



[TOPIC: post.html]
#12

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

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



[TOPIC: post.html]
#13

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 368 posts
  • Corona SDK

  Best Answer

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. :D

Good luck!



[TOPIC: post.html]
#14

marcodelia1900

[GLOBAL: userInfoPane.html]
marcodelia1900
  • Observer

  • 7 posts
  • Corona SDK

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. :D

Good luck!

Thanks again bro!




[topic_controls]
[/topic_controls]