TimerDelay doesn't work for Physics.addBody

Hi everyone, 

I’ve been attempting to solve this problem for the past few days, tried a few approaches, but all unsuccessful.

So to start off describing my problem, let me first describe what I attempting to achieve.

This game i’m making involves objects colliding with each other and combining to form a new object. For example, when two different coloured rectangles collide, a new rectangle with the combined width of the two rectangles and a new colour is formed. 

Thus this new rectangle needs to have physics properties, as well as the collision event listener attached to it as well. The two previous rectangles then fade into alpha = 0 using transition.to and onComplete, is removed. The code is as follow. 

local function RectCollision ( self, event ) local phase = event.phase local other = event.other print( self.width, other.width ) local new\_width = self.width+other.width local forceModifier = 0.01 local function CombineRects () transition.to( self, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) transition.to( other, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) local CombinedRect = display.newRect( self.x, self.y, 10, self.height ) CombinedRect.fill = self.colour CombinedRect.colourname = self.colourname CombinedRect.type = 'rect' CombinedRect.alpha = 0 transition.to( CombinedRect, { time = 400, transition = easing.inOutCubic, alpha = 1, width = new\_width, onStart = function() display.remove(self) ; display.remove(other) end } ) timer.performWithDelay( 20, physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) ) CombinedRect:applyForce( forceModifier\*(halfW-CombinedRect.x), forceModifier\*(halfH-CombinedRect.y), CombinedRect.x, CombinedRect.y ) CombinedRect.touch = RectControl CombinedRect:addEventListener("touch") CombinedRect.collision = RectCollision CombinedRect:addEventListener("collision") mapGroup:insert(CombinedRect) if (phase == 'began') then print(phase, self.colourname, 'collided with', other.colourname, other.type) if other.type == 'rect' then ObjColourBehaviour( self, other ) timer.performWithDelay( 40, CombineRects ) elseif other.type == 'ObjCircle' then timer.performWithDelay( 40, reSize(other) ) --reSizeObj1 = true --timer.performWithDelay( 1000, physics.removeBody(other) ) --timer.performWithDelay( 1000, physics.addBody( other, 'static', {radius = other.path.radius, density = 1, bounce = 0.3, friction = 10} ) ) end elseif (phase == 'ended') then end return true end 

This listener functions is found in the same file where the rectangles are created, i.e. classes.objects. Which is called during the scene:create function of the game itself, a separate scene called tutorial… I’ve tried calling it during the will and did phase of the scene:show as well but it doesn’t work.

The new rectangles spawn as working, but the the physics collision boundaries still follow the old rectangles. 

On a related note, I have two circles, also physics bodies, which the player has to aim the combined rectangles at, if the rectangles do not have the same colour of the circle, this circle will increase in size via the transition.to method.I also use a similar method, timer.performWithDelay to update the physics.addBody but it doesn’t work as well. But I keep getting the error that says physics addBody cannot be called when the world is locked and in the middle of number crunching.

I have tried removing the previous physics body with physics.removeBody (via timer as well) before using timer to call physics.addBody, in an attempt to ‘reset’ the physics bodies. I have also varied the timers, from 20ms to 1000ms but it doesn’t work.

I’ve read the collision detection guide and understand the limitations, the guide suggested to use timer.performWithDelay, which is why I used it in my code above. I also tried to put the CombineRects function outside the collision listener function with it’s own parameters which the collision handler will pass when it calls it, but it doesn’t work.

This is from the tutorial scene file where it calls the objects

function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen physics.start() physics.setGravity ( 0, 0 ) elseif phase == "did" then -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc -- load scripted tilemap from tutorialLvl.lua local map = {} map.level = require( "levels.tutorialLvl" ) local mapGroup = newObjects( tileSize, tileSize, map ) --sceneGroup:insert( mapGroup ) end end

All help to solve this problem will be much much appreciated.

Just a note:

timer.performWithDelay( 20, physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) )

You need to wrap function calls in a closure if you want to “store” them into a variable with your parameters:

timer.performWithDelay( 20, function() physics.addBody( CombinedRect, { density = 1.8, bounce = 0, friction = 10 }) end )

Otherwise you’re not really passing a function into the variable, you’re just calling the function. Essentially, any time a function has () on it, you are calling it as a function, not a reference. Which means you need a closure.


local function mk() print '-----------------------------------' end local function getName(name)   name = name or 'nobody'   print(name) end mk() a = getName('Jim') --This will print 'jim' and `a` will be nil mk() a = getName() --This will print 'nobody' and `a` will be nil mk() a = getName -- this will print nothing and `a` will be a function mk() a('Jim') -- This will print 'Jim' mk() a = function() getName('Jim') end --This will print nothing and `a` will be a function mk() a() -- This will print 'Jim' mk() a('billy') --This will also print jim mk()

http://codepad.org/HoAmOJWM

Hi txzeenath,

Thanks for your note. I thought the physics.addBody is a function itself per se, but anyhow I’m going to change it and who knows it might solve the problem.

Well it didn’t solve the problem. And i got another error that says:

attempt to call method “applyForce” (a nil value) at the line where I applyForce to the combinedrect object.

I think it’s because the physics.addBody didn’t run/execute as the combined rect object isn’t recognised as a physics body.

I thought the physics.addBody is a function itself per se

It is a function. But if you don’t put it inside a closure, you’re just calling the function, not passing it into the timer. It will execute immediately. Try it with a print statement.

timer.performWithDelay(2000,print(‘hi’))

vs

timer.performWithDelay(2000,function() print(‘hi’) end)

Edit:

Just saw…

timer.performWithDelay( 20, physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) )

CombinedRect:applyForce( forceModifier*(halfW-CombinedRect.x), forceModifier*(halfH-CombinedRect.y), CombinedRect.x, CombinedRect.y )

You can’t define a timer to add the physics, and then immediately apply force to it. Since the physics won’t exist for 20ms.

If you want to delay it, you can always wrap it all into a closure that will execute in 20ms.

ah that makes sense, ok i’m gonna try to put all that into a function.

edit: ok thanks txzeenath for your help and input. It seems to work now, except it introduced another strange problem that I have not encountered before. After the rect collides with the circle, the simulator went into an almost infinite loop of increasing the radius of the circle, like the rectangle keeps colliding. 

There’s probably something wrong with the new collision bounds somewhere, I got to troubleshoot this new problem. 

This is the updated code btw:

local function RectCollision ( self, event ) local phase = event.phase local other = event.other print( self.width, other.width ) local new\_width = self.width+other.width local forceModifier = 0.01 local function CombineRects () transition.to( self, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) transition.to( other, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) local CombinedRect = display.newRect( self.x, self.y, 10, self.height ) CombinedRect.fill = self.colour CombinedRect.colourname = self.colourname CombinedRect.type = 'rect' CombinedRect.alpha = 0 transition.to( CombinedRect, { time = 400, transition = easing.inOutCubic, alpha = 1, width = new\_width, onStart = function() display.remove(self) ; display.remove(other) end } ) timer.performWithDelay( 20, function () physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) CombinedRect:applyForce( forceModifier\*(halfW-CombinedRect.x), forceModifier\*(halfH-CombinedRect.y), CombinedRect.x, CombinedRect.y ) CombinedRect.touch = RectControl CombinedRect:addEventListener("touch") CombinedRect.collision = RectCollision CombinedRect:addEventListener("collision") mapGroup:insert(CombinedRect) end ) end local function reSize(obj) local new\_radius = obj.path.radius\*1.2 local enlargedCircle = display.newCircle ( obj.x, obj.y, new\_radius ) enlargedCircle.fill = obj.colour enlargedCircle.colourname = obj.colourname enlargedCircle.type = obj.type timer.performWithDelay( 40, function () physics.addBody( enlargedCircle, 'static', {radius = new\_radius, density = 1, bounce = 0.3, friction = 10} ) print( enlargedCircle.colourname, 'circle enlarged, radius:', enlargedCircle.path.radius ) mapGroup:insert(enlargedCircle) end ) end if (phase == 'began') then print(phase, self.colourname, 'collided with', other.colourname, other.type) if other.type == 'rect' then ObjColourBehaviour( self, other ) timer.performWithDelay( 40, CombineRects() ) elseif other.type == 'ObjCircle' then print( other.colourname, 'circle original radius:', other.path.radius ) transition.to( other, { time = 200, transition = easing.inCirc, width = other.width\*1.2, height = other.height\*1.2, onComplete = function () reSize(other) end} ) end elseif (phase == 'ended') then end return true end

Collisions can be fired many times for a single impact as things move. To be safe, you can add a “flag” to the object, and check for it before doing any processing. (Like a spin-lock in multithreaded applications). Like:

(rough mock-up)

local function onCollision(evt) if object.waiting then return end object.waiting = true --Do some stuff --'Unlock' after one frame timer.performWithDelay(1, function(object) if object then --Always check if the object still exists when using delays object.waiting = false end end) end

This way processing completes before another can occur.

Also, when you re-size an object. do physics.removeBody, and then add it back. Otherwise you’ll get leaks and weirdness by adding bodies over and over. Keep in mind as well, since you’re adding bodies, you’ll likely trigger the collision each time it scales. Another trick is to use the event ‘force’ property as a filter.

Scaling and dynamic creation of physics bodies gets complicated fast :-p

Note on timers: timers always fire either when the time is up, or on the next frame. Whichever is later. A 1ms delay will wait one frame, which is usually sufficient unless you need the delay for cosmetics.


I had a little game which had orbs you “eat” and you grow. For that I did something similar to this (mock up):

object:doEat(target)   physics.removeBody(self) --Remove physics   physics.removeBody(target)   transition.to(target,{time=2000,alpha=0,onComplete = function(target) if target then target:removeSelf() end end})   self.path.radius = self.path.radius\*1.1 --Scale 10%   physics.addBody(self) --Add physics   self.eating = false --Done eating end local function onCollision(self,evt) if evt.phase == "began" then   local target = evt.target   if self.eating then return end --Already eating   self.eating = true --Lock   timer.performWithDelay(1, function() self:doEat(target) end) --Wait a frame end end

thanks for your advice and pointers, will give it a go.

Thanks man, i managed to get it to work, had to change some forms of implementation but it’s working so far. fingers crossed

Just a note:

timer.performWithDelay( 20, physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) )

You need to wrap function calls in a closure if you want to “store” them into a variable with your parameters:

timer.performWithDelay( 20, function() physics.addBody( CombinedRect, { density = 1.8, bounce = 0, friction = 10 }) end )

Otherwise you’re not really passing a function into the variable, you’re just calling the function. Essentially, any time a function has () on it, you are calling it as a function, not a reference. Which means you need a closure.


local function mk() print '-----------------------------------' end local function getName(name)   name = name or 'nobody'   print(name) end mk() a = getName('Jim') --This will print 'jim' and `a` will be nil mk() a = getName() --This will print 'nobody' and `a` will be nil mk() a = getName -- this will print nothing and `a` will be a function mk() a('Jim') -- This will print 'Jim' mk() a = function() getName('Jim') end --This will print nothing and `a` will be a function mk() a() -- This will print 'Jim' mk() a('billy') --This will also print jim mk()

http://codepad.org/HoAmOJWM

Hi txzeenath,

Thanks for your note. I thought the physics.addBody is a function itself per se, but anyhow I’m going to change it and who knows it might solve the problem.

Well it didn’t solve the problem. And i got another error that says:

attempt to call method “applyForce” (a nil value) at the line where I applyForce to the combinedrect object.

I think it’s because the physics.addBody didn’t run/execute as the combined rect object isn’t recognised as a physics body.

I thought the physics.addBody is a function itself per se

It is a function. But if you don’t put it inside a closure, you’re just calling the function, not passing it into the timer. It will execute immediately. Try it with a print statement.

timer.performWithDelay(2000,print(‘hi’))

vs

timer.performWithDelay(2000,function() print(‘hi’) end)

Edit:

Just saw…

timer.performWithDelay( 20, physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) )

CombinedRect:applyForce( forceModifier*(halfW-CombinedRect.x), forceModifier*(halfH-CombinedRect.y), CombinedRect.x, CombinedRect.y )

You can’t define a timer to add the physics, and then immediately apply force to it. Since the physics won’t exist for 20ms.

If you want to delay it, you can always wrap it all into a closure that will execute in 20ms.

ah that makes sense, ok i’m gonna try to put all that into a function.

edit: ok thanks txzeenath for your help and input. It seems to work now, except it introduced another strange problem that I have not encountered before. After the rect collides with the circle, the simulator went into an almost infinite loop of increasing the radius of the circle, like the rectangle keeps colliding. 

There’s probably something wrong with the new collision bounds somewhere, I got to troubleshoot this new problem. 

This is the updated code btw:

local function RectCollision ( self, event ) local phase = event.phase local other = event.other print( self.width, other.width ) local new\_width = self.width+other.width local forceModifier = 0.01 local function CombineRects () transition.to( self, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) transition.to( other, { time = 400, transition = easing.inOutCubic, alpha = 0 } ) local CombinedRect = display.newRect( self.x, self.y, 10, self.height ) CombinedRect.fill = self.colour CombinedRect.colourname = self.colourname CombinedRect.type = 'rect' CombinedRect.alpha = 0 transition.to( CombinedRect, { time = 400, transition = easing.inOutCubic, alpha = 1, width = new\_width, onStart = function() display.remove(self) ; display.remove(other) end } ) timer.performWithDelay( 20, function () physics.addBody( CombinedRect, {density = 1.8, bounce = 0, friction = 10} ) CombinedRect:applyForce( forceModifier\*(halfW-CombinedRect.x), forceModifier\*(halfH-CombinedRect.y), CombinedRect.x, CombinedRect.y ) CombinedRect.touch = RectControl CombinedRect:addEventListener("touch") CombinedRect.collision = RectCollision CombinedRect:addEventListener("collision") mapGroup:insert(CombinedRect) end ) end local function reSize(obj) local new\_radius = obj.path.radius\*1.2 local enlargedCircle = display.newCircle ( obj.x, obj.y, new\_radius ) enlargedCircle.fill = obj.colour enlargedCircle.colourname = obj.colourname enlargedCircle.type = obj.type timer.performWithDelay( 40, function () physics.addBody( enlargedCircle, 'static', {radius = new\_radius, density = 1, bounce = 0.3, friction = 10} ) print( enlargedCircle.colourname, 'circle enlarged, radius:', enlargedCircle.path.radius ) mapGroup:insert(enlargedCircle) end ) end if (phase == 'began') then print(phase, self.colourname, 'collided with', other.colourname, other.type) if other.type == 'rect' then ObjColourBehaviour( self, other ) timer.performWithDelay( 40, CombineRects() ) elseif other.type == 'ObjCircle' then print( other.colourname, 'circle original radius:', other.path.radius ) transition.to( other, { time = 200, transition = easing.inCirc, width = other.width\*1.2, height = other.height\*1.2, onComplete = function () reSize(other) end} ) end elseif (phase == 'ended') then end return true end

Collisions can be fired many times for a single impact as things move. To be safe, you can add a “flag” to the object, and check for it before doing any processing. (Like a spin-lock in multithreaded applications). Like:

(rough mock-up)

local function onCollision(evt) if object.waiting then return end object.waiting = true --Do some stuff --'Unlock' after one frame timer.performWithDelay(1, function(object) if object then --Always check if the object still exists when using delays object.waiting = false end end) end

This way processing completes before another can occur.

Also, when you re-size an object. do physics.removeBody, and then add it back. Otherwise you’ll get leaks and weirdness by adding bodies over and over. Keep in mind as well, since you’re adding bodies, you’ll likely trigger the collision each time it scales. Another trick is to use the event ‘force’ property as a filter.

Scaling and dynamic creation of physics bodies gets complicated fast :-p

Note on timers: timers always fire either when the time is up, or on the next frame. Whichever is later. A 1ms delay will wait one frame, which is usually sufficient unless you need the delay for cosmetics.


I had a little game which had orbs you “eat” and you grow. For that I did something similar to this (mock up):

object:doEat(target)   physics.removeBody(self) --Remove physics   physics.removeBody(target)   transition.to(target,{time=2000,alpha=0,onComplete = function(target) if target then target:removeSelf() end end})   self.path.radius = self.path.radius\*1.1 --Scale 10%   physics.addBody(self) --Add physics   self.eating = false --Done eating end local function onCollision(self,evt) if evt.phase == "began" then   local target = evt.target   if self.eating then return end --Already eating   self.eating = true --Lock   timer.performWithDelay(1, function() self:doEat(target) end) --Wait a frame end end

thanks for your advice and pointers, will give it a go.

Thanks man, i managed to get it to work, had to change some forms of implementation but it’s working so far. fingers crossed