Error every time I try and remove object on collision, no matter what I try.

Hi everyone.

I’m trying to do something pretty simple. I have some circles which spawn randomly and move down the screen. When they collide with an object at the bottom of the screen, they need to be removed/deleted. I’ve extensively searched the forums and no matter what approach I try it always kicks up an error.

I’ve tried putting the collision event inside the spawn function, changing the syntax numerous ways, creating a function to delete the objects, etc. I can’t use transitions or “onComplete” to remove them, so am stuck.

Can someone please tell me what I’m doing wrong? It’s probably something really simple, but I just can’t seem to figure out what.

Here’s my code:

--hides iphone status bar  
--display.setStatusBar(display.HiddenStatusBar)  
  
physics = require("physics")  
physics.start()  
physics.setGravity(0,0)  
  
physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--cache content dimensions and math.random for better performance  
\_H = display.contentHeight;  
\_W = display.contentWidth;  
mRand = math.random;  
terrainspeed = 5  
maxCrater = 5  
crad30 = 30  
crad60 = 60  
crad100 = 100  
tsmod = 0  
craterRate = 1000  
craterMod = 0  
local centerX = display.contentWidth/2;  
  
local hitdet = display.newRect(0, 0, \_W, 5)  
hitdet.x = \_W / 2  
hitdet.y = \_H - 5  
hitdet:setFillColor(0, 0, 255)  
physics.addBody( hitdet, { friction=0, bounce=0,} )  
 hitdet.isSensor = true  
 hitdet.myName = "hitDet"  
local buggy = display.newRect(0, 0, 30, 30)  
buggy.x = \_W / 2  
buggy.y = \_H / 3 + \_H / 3  
buggy.strokeWidth = 3  
buggy:setFillColor(140, 140, 140)  
buggy:setStrokeColor(180, 180, 180)  
  
function tsmodifier() --picks a random value by which to modify terrainspeed  
 tsmod = mRand(0, 10)  
 --print (tsmod)  
end  
  
function spawnCrater() -- spawns a crater, checks its y position and moves it if necessary.  
 local crater = display.newCircle (mRand(0, \_W), \_H / \_H, mRand(30, 100))  
  
 physics.addBody( crater, { friction=0, bounce=0, radius = crater.radius} )  
 crater.isSensor = true  
 crater.myName = "Crater"  
  
  
 local function moveCrater() -- moves the crater  
 if crater.y \< \_H + crater.contentHeight then  
 crater.y = crater.y + terrainspeed + tsmod   
 end  
 end   
  
 local function onCollision( event )  
 if ( event.phase == "began" ) then  
   
 print( "began: " .. event.object1.myName .. " & " .. event.object2.myName )  
 if ( crater ) then  
 crater:removeSelf()  
 crater = nil  
 end  
  
 elseif ( event.phase == "ended" ) then  
   
 print( "ended: " .. event.object1.myName .. " & " .. event.object2.myName )  
   
 end  
end  
  
 Runtime:addEventListener( "collision", onCollision )  
 Timer2 = timer.performWithDelay(10,moveCrater, 0)   
end  
  
Runtime:addEventListener( "collision", onCollision )  
  
Timer1 = timer.performWithDelay(craterRate, spawnCrater, 0)  
Timer3 = timer.performWithDelay(5000,tsmodifier, 0)  

Thanks

Dan [import]uid: 67933 topic_id: 12865 reply_id: 312865[/import]

I had problems removing bodies on collision too, eventually I ended up making the collision function simply turn the body’s isSensor value on and making alpha = 0, then cleaning it up at some later point. Not the best solution, but when you just really need it to work it does the trick. [import]uid: 75744 topic_id: 12865 reply_id: 47219[/import]

REPLACE:

crater:removeSelf()  
crater = nil  

WITH:

local function removeCrate(event)  
crater:removeSelf()  
crater = nil  
end  
  
removeCrateTimer = timer.performWithDelay(0, removeCrate)  

That is what I did to solve this problem… Try and see if it helps.
[import]uid: 12979 topic_id: 12865 reply_id: 47225[/import]

And BTW, you might want to use timer.cancel() to remove your spawn and move crater timer as well. [import]uid: 12979 topic_id: 12865 reply_id: 47226[/import]

Hi.

Thanks for the code.

It works to an extent, but I’m still getting the same error kicked up each time. Only difference is now, whenever an object called cratr hits the object on the bottom of the screen, all the visible crater objects are simultaneously deleted.

I really don’t know what to do to get this working. All I want is for the crater objects to spawn, move down the screen and then delete at the bottom. Do I have to set it up to re-name every object that’s spawn with some kind of suffix, (crater1, crater2, etc)? There must be an easier way.

Here is my (now partially working code)

physics = require("physics")  
physics.start()  
physics.setGravity(0,0)  
  
physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--cache content dimensions and math.random for better performance  
\_H = display.contentHeight;  
\_W = display.contentWidth;  
mRand = math.random;  
terrainspeed = 5  
maxCrater = 5  
tsmod = 0  
craterRate = 1000  
craterMod = 0  
local centerX = display.contentWidth/2;  
  
local hitdet = display.newRect(0, 0, \_W, 5)  
hitdet.x = \_W / 2  
hitdet.y = \_H - 5  
hitdet:setFillColor(0, 0, 255)  
physics.addBody( hitdet, { friction=0, bounce=0,} )  
 hitdet.isSensor = true  
 hitdet.myName = "hitDet"  
local buggy = display.newRect(0, 0, 30, 30)  
buggy.x = \_W / 2  
buggy.y = \_H / 3 + \_H / 3  
buggy.strokeWidth = 3  
buggy:setFillColor(140, 140, 140)  
buggy:setStrokeColor(180, 180, 180)  
  
function tsmodifier() --picks a random value by which to modify terrainspeed  
 tsmod = mRand(0, 10)  
 --print (tsmod)  
end  
  
function spawnCrater() -- spawns a crater, checks its y position and moves it if necessary.  
 local crater = display.newCircle (mRand(0, \_W), \_H / \_H, mRand(30, 100))  
  
 physics.addBody( crater, { friction=0, bounce=0, radius = crater.radius} )  
 crater.isSensor = true  
 crater.myName = "Crater"  
  
  
 local function moveCrater() -- moves the crater  
 if crater.y \< \_H + crater.contentHeight then  
 crater.y = crater.y + terrainspeed + tsmod   
 end  
 end   
  
 local function onCollision( event )  
 if ( event.phase == "began" ) then  
   
 print( "began: " .. event.object1.myName .. " & " .. event.object2.myName )  
 if ( crater ) then  
 local function removeCrater(event)  
 crater:removeSelf()  
 crater = nil  
 end  
   
removeCrateTimer = timer.performWithDelay(1, removeCrater)  
 end  
  
 elseif ( event.phase == "ended" ) then  
   
 print( "ended: " .. event.object1.myName .. " & " .. event.object2.myName )  
   
 end  
end  
  
 Runtime:addEventListener( "collision", onCollision )  
 Timer2 = timer.performWithDelay(10,moveCrater, 0)   
end  
  
--Runtime:addEventListener( "collision", onCollision )  
  
Timer1 = timer.performWithDelay(craterRate, spawnCrater, 0)  
Timer3 = timer.performWithDelay(5000,tsmodifier, 0)  

Thanks again for the snippet, any other advice would be greatly appreciated.

Dan [import]uid: 67933 topic_id: 12865 reply_id: 47367[/import]

I think you might want to do this:

Instead of using Collision for Runtime, set specific collision for the crater itself.

Inside the new Collision function (self, event) you can identify the crater on action with “self”. Example you can set the crater on collision alpha to 0 with self.alpha = 0. [import]uid: 12979 topic_id: 12865 reply_id: 47368[/import]

Thanks Pinch,

It’s a whisker away from being sorted.

Whenever a crater hits the bar at the bottom, they now delete independently. It’s still giving me an error though, presumably because the:

event.other:removeSelf()  

in the “onLocalCollision” function is deleting any reference to the crater objects, so it throws a nil value when evaluating craters following the first collision?

You mentioned setting the alpha to 0 which unfortunately wouldn’t make any difference to my app. By the time they need to be deleted they’d have left the screen completely and wouldn’t need to return.

If I don’t delete them as they leave the stage/screen, it’ll eventually clog the memory as there’ll be possibly hundreds/thousands held in memory but not having any impact on the game.

I can’t use transitions to delete them with “onComplete” due to the requirements of my app, and varying the speed the craters fall dynamically.

I’m not asking to to write my code for me, I just don’t understand how I’m supposed to acheive this.

My new code is as follows:

--hides iphone status bar  
--display.setStatusBar(display.HiddenStatusBar)  
  
physics = require("physics")  
physics.start()  
physics.setGravity(0,0)  
  
physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
  
--cache content dimensions and math.random for better performance  
\_H = display.contentHeight;  
\_W = display.contentWidth;  
mRand = math.random;  
terrainspeed = 5  
maxCrater = 5  
  
tsmod = 0  
craterRate = 1000  
craterMod = 0  
local centerX = display.contentWidth/2;  
  
local hitdet = display.newRect(0, 0, \_W, 5)  
hitdet.x = \_W / 2  
hitdet.y = \_H - 5  
hitdet:setFillColor(0, 0, 255)  
physics.addBody( hitdet, { friction=0, bounce=0,} )  
 hitdet.isSensor = true  
 hitdet.myName = "hitDet"  
local buggy = display.newRect(0, 0, 30, 30)  
buggy.x = \_W / 2  
buggy.y = \_H / 3 + \_H / 3  
buggy.strokeWidth = 3  
buggy:setFillColor(140, 140, 140)  
buggy:setStrokeColor(180, 180, 180)  
  
function tsmodifier() --picks a random value by which to modify terrainspeed  
 tsmod = mRand(0, 10)  
 --print (tsmod)  
end  
  
function spawnCrater() -- spawns a crater, checks its y position and moves it if necessary.  
 local crater = display.newCircle (mRand(0, \_W), \_H / \_H, mRand(30, 100))  
  
 physics.addBody( crater, { friction=0, bounce=0, radius = crater.radius} )  
 crater.isSensor = true  
 crater.myName = "Crater"  
------------------------------------------------------------------------------------------------------------------------------  
  
 local function moveCrater() -- moves the crater  
 if crater.y \< \_H + crater.contentHeight then  
 crater.y = crater.y + terrainspeed + tsmod   
 end  
 end   
  
 Timer2 = timer.performWithDelay(10,moveCrater, 0)   
 -------------------------------------------------------------------------------------------------------------------------  
end  
  
local function onLocalCollision( self, event )  
 if ( event.phase == "began" ) then  
   
 print( self.myName .. ": collision began with " .. event.other.myName )  
 local function removecrater()  
 event.other:removeSelf()  
 end  
  
 Timer4 = timer.performWithDelay(1,removecrater, 1)   
  
 elseif ( event.phase == "ended" ) then  
   
 print( self.myName .. ": collision ended with " .. event.other.myName )  
   
 end  
 --------------------------------------------------------------------------------------------  
end  
   
--crater.collision = onLocalCollision  
--crater:addEventListener( "collision", hitdet )  
  
hitdet.collision = onLocalCollision  
hitdet:addEventListener( "collision", hitdet )  
--]]  
Timer1 = timer.performWithDelay(craterRate, spawnCrater, 0)  
Timer3 = timer.performWithDelay(5000,tsmodifier, 0)  
  

Again, thanks for your help. [import]uid: 67933 topic_id: 12865 reply_id: 47689[/import]

Hi spider,

I mentioned using self.alpha is just an example that you can do…

I’ve reviewed and test you code, you problem is the moveCrater() timer, which I think it is not appropriate to use that. Why not you use gravity instead? Try the following code, I change the gravity to (0,9.8) and the hitdet bodyType to “static”.

--hides iphone status bar  
--display.setStatusBar(display.HiddenStatusBar)  
   
physics = require("physics")  
physics.start()  
physics.setGravity(0,9.8)  
   
physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
   
--physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
   
--cache content dimensions and math.random for better performance  
\_H = display.contentHeight;  
\_W = display.contentWidth;  
mRand = math.random;  
terrainspeed = 5  
maxCrater = 5  
   
tsmod = 0  
craterRate = 1000  
craterMod = 0  
local centerX = display.contentWidth/2;  
   
local hitdet = display.newRect(0, 0, \_W, 5)  
hitdet.x = \_W / 2  
hitdet.y = \_H - 5  
hitdet:setFillColor(0, 0, 255)  
physics.addBody(hitdet, "static", {density = 1.0, friction = 0, bounce = 0, isSensor =true})  
hitdet.myName = "hitDet"  
   
   
local buggy = display.newRect(0, 0, 30, 30)  
buggy.x = \_W / 2  
buggy.y = \_H / 3 + \_H / 3  
buggy.strokeWidth = 3  
buggy:setFillColor(140, 140, 140)  
buggy:setStrokeColor(180, 180, 180)  
   
function tsmodifier() --picks a random value by which to modify terrainspeed  
 tsmod = mRand(0, 10)  
 --print (tsmod)  
end  
   
   
   
function spawnCrater() -- spawns a crater, checks its y position and moves it if necessary.  
 local crater = display.newCircle (mRand(0, \_W), \_H / \_H, mRand(30, 100))  
  
 physics.addBody( crater, { friction=0, bounce=0, radius = crater.radius} )  
 crater.isSensor = true  
 crater.myName = "Crater"  
------------------------------------------------------------------------------------------------------------------------------  
  
 local function moveCrater() -- moves the crater  
 if crater then  
 if crater.y \< \_H + crater.contentHeight then  
 crater.y = crater.y + terrainspeed + tsmod   
 end  
 end  
 end   
  
 --Timer2 = timer.performWithDelay(10,moveCrater, 0)   
 -------------------------------------------------------------------------------------------------------------------------  
end  
   
local function onLocalCollision( self, event )  
 if ( event.phase == "began" ) then  
   
 print( self.myName .. ": collision began with " .. event.other.myName )  
 local function removecrater()  
 event.other:removeSelf()  
 end  
  
 Timer4 = timer.performWithDelay(1,removecrater, 1)   
  
 elseif ( event.phase == "ended" ) then  
   
 print( self.myName .. ": collision ended with " .. event.other.myName )  
   
 end  
 --------------------------------------------------------------------------------------------  
end  
   
   
   
--crater.collision = onLocalCollision  
--crater:addEventListener( "collision", hitdet )  
  
hitdet.collision = onLocalCollision  
hitdet:addEventListener( "collision", hitdet )  
--]]  
Timer1 = timer.performWithDelay(craterRate, spawnCrater, 0)  
Timer3 = timer.performWithDelay(5000,tsmodifier, 0)  

Do you prefer this approach? Or you must move the crater without gravity? [import]uid: 12979 topic_id: 12865 reply_id: 47726[/import]

The code works great but unfortunately doesn’t fit the requirements of the app.

The idea for the game is simple enough. A buggy drives up the screen and avoids oncoming craters. the tilt controls move the buggy on the x axis, and when tilted up/down, it changes the speed that the terrain/ground moves. So by tilting the device up, the speed of the falling objects is increased, (using the “tsmod” variable, which is currently set up just to see if it works)

In my full code, the craters (and other groups of objects, debris, backgrounds etc) all move using transitions, but I’ve since decided that I need the accelerometer to control the speed that the objects move past.

The reason gravity wont work is two-fold. Firstly, on spawning an object it accelerates from 0 to a terminal velocity. This behaivour is clearly different from a landscape which would move at a constant rate. I suppose I could get around this by spawning objects far off the top of the screen, so that by the time they are seen, they are already moving at a constant rate.

Secondly, there will likely be around 60-70 objects on screen in the final build. It would likely be memory intensive to use gravity on so many objects.

Can you think of any other approach I can try?

Thanks again, you’ve been really helpful.
[import]uid: 67933 topic_id: 12865 reply_id: 47862[/import]

Your game is very similar to my game OmniBlaster (http://bit.ly/omniblaster) where I have a ship that faces oncoming objects that either have to be dodged or blown up and I have a scrolling background.

Since none of this is physics based, I’m not using physics at all. I do my own collision detection (you can search the forums for collision detection without physics to get sample routines).

Basically my game is a big gameLoop on the enterFrame event firing 30 times a second. Inside that function, I scroll the background and moved the objects at their speed, then I check each object to see if its collided with me or weapons fire and remove it. Then I check the object’s Y position to see if it’s off screen (object.y > display.contentHeight + object.y) I wait until its position is totally off screen so they just don’t pop away.

I used two different collision routines, one for rectangles and one for circles. I use rectangles for weapons fire and circles for everything else since that fit their graphics better.

Here is the code from my game:

[lua]local function hasCollidedCircle(obj1, obj2)
if obj1 == nil then
return false
end
if obj2 == nil then
return false
end
local sqrt = math.sqrt

local dx = (obj1.x) - (obj2.x + 32);
local dy = (obj1.y) - (obj2.y + 32);

local distance = sqrt(dx*dx + dy*dy);
local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2)
if distance < objectSize then
return true
end
return false
end

local function hasCollided(obj1, obj2)
if obj1 == nil then
return false
end
if obj2 == nil then
return false
end
if (obj1.isVisible == false) or (obj2.isVisible == false) then
return
– get out of here if the object isn’t really there. This is my ghost Chasing check as to
– why my weapons sometimes just disappear. In theory I’m entering the frame in the middle of collision detection
– which I shouldn’t be because I flag that I am, but is possible that I’m done detecing collisions before I actually
– get the target removed so I’m going to set the visibility flag as soon as I can and then test for it here.
– fingers crossed…
end
local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax
return (left or right) and (up or down)
end

function gameLoop(event)
local i
if gameState == “playing” then
scrollBackground(event)
for i=1,#targets do
move(targets[i])
if checkingCollision == false then
checkingCollision = true
if targets[i] ~= nil then
if hasCollidedCircle(targets[i], myRocket) then
killMe(targets[i], i, true)
end
end
checkingCollision = false
end
end
return true
end

RunTime:addEventListener(“enterFrame”, gameLoop)[/lua]

Hope that will help you. [import]uid: 19626 topic_id: 12865 reply_id: 47878[/import]

Thanks for the sample without the physics, it looks really good.

I’d still prefer to use physics if possible, as it makes interactions much, much simpler. The crater objects I’m using will be constructed from 2 x circlular objects, one for the outside lip of the crater, and one for the inside. On collision with the outside, the terrain speed will be modified to slow all the objects falling from the screen. On collision with the inside cirle, the player character is killed. Again, I really need physics for this.

I’ve tried modifying gravity and spawning each object way off screen, so that it reaches terminal velocity by the time it’s seen. I’ve also tried using transitions and modifying a variable in the “time=” parameter of the transition to change the speed with which the transition completes. It still doesn’t do the job.

It was mentioned that there was a problem with my using a timer to move the crater objects. Do we know what the problem is with this, so I can try and find a work around?

Thanks [import]uid: 67933 topic_id: 12865 reply_id: 48290[/import]

Ok, I’m nearly there.

I’ve set the craters to move by “applyLinearForce” rather than gravity, which gets them to move at a constant rate, and they now delete on collision with the bar at the bottom of the screen without error!

All I need to do now is change the speed of all the craters as a group, rather than individually. I’ve tried adding the craters to a group, and moving that, but it doesn’t work.

I currently have “tsmod” set up to randomly generate a value every 5 seconds, and need this value to modify the speed by which all the craters move, at the same time.

If I can sort this, then I’ll stop bugging you all.

Here’s my code:

--hides iphone status bar  
display.setStatusBar(display.HiddenStatusBar)  
   
tsmod = 0  
   
physics = require("physics")  
physics.start()  
physics.setGravity(0,0)  
   
physics.setDrawMode( "hybrid" ) -- uncomment to see physics interactions.  
   
--cache content dimensions and math.random for better performance  
\_H = display.contentHeight;  
\_W = display.contentWidth;  
mRand = math.random;  
terrainspeed = 100  
maxCrater = 5  
   
--groups  
local cratergroup = display.newGroup()  
  
--variables  
craterRate = 1000  
craterMod = 0  
local centerX = display.contentWidth/2;  
  
--objects  
local hitdet = display.newRect(0, 0, \_W, 5)  
hitdet.x = \_W / 2  
hitdet.y = \_H - 5  
hitdet:setFillColor(0, 0, 255)  
physics.addBody(hitdet, "static", {density = 1.0, friction = 0, bounce = 0, isSensor =true})  
hitdet.myName = "hitDet"  
   
   
local buggy = display.newRect(0, 0, 30, 30)  
buggy.x = \_W / 2  
buggy.y = \_H / 3 + \_H / 3  
buggy.strokeWidth = 3  
buggy:setFillColor(140, 140, 140)  
buggy:setStrokeColor(180, 180, 180)  
   
function tsmodifier() --picks a random value by which to modify terrainspeed  
 tsmod = mRand(0, 10)  
 print (tsmod)  
end  
   
 function movecratergroup()  
 cratergroup.y = cratergroup.y + tsmod  
 end  
   
function spawnCrater() -- spawns a crater, checks its y position and moves it if necessary.  
 local crater = display.newCircle (mRand(0, \_W), \_H / \_H, mRand(30, 100))  
  
 physics.addBody( crater, { friction=0, bounce=0, radius = crater.radius} )  
 crater.isSensor = true  
 crater.myName = "Crater"  
 crater:setLinearVelocity( 0, terrainspeed)  
 cratergroup:insert(crater)  
  
  
end  
   
   
local function onLocalCollision( self, event )  
 if ( event.phase == "began" ) then  
   
 print( self.myName .. ": collision began with " .. event.other.myName )  
 local function removecrater()  
 event.other:removeSelf()  
 end  
  
 Timer4 = timer.performWithDelay(1,removecrater, 1)   
  
 elseif ( event.phase == "ended" ) then  
   
 print( self.myName .. ": collision ended with " .. event.other.myName )  
   
 end  
end  
  
hitdet.collision = onLocalCollision  
hitdet:addEventListener( "collision", hitdet )  
--]]  
Timer1 = timer.performWithDelay(craterRate, spawnCrater, 0)  
Timer3 = timer.performWithDelay(5000,tsmodifier, 0)  

Thanks in advance. [import]uid: 67933 topic_id: 12865 reply_id: 49066[/import]