Slight movement of dynamic physics bodies after physics.start()

Hi All,

I am writing a game that has two stages, design and play.  In the design stage the user can move around objects that get put into action when going into play mode.  

In the design mode the user moves around standard (non-physics) game pieces, that get removed and created as physics objects when in play mode.  This all works perfectly except there is a slight movement (rotation) of the objects when I start the physics engine (from pause).  

I played around with physics.stop, which solves this issue but created a whole heap of others (see https://forums.coronalabs.com/topic/61040-sporadic-error/)..)

I have decided to use physics.pause() instead, I have done a lot of research, and followed different peoples advice,  the best solution I have come up with is to add a weld joint to a static object when I create the object, and the straight away remove it, this seems to reduce the movement down to a very little sideways movement (maybe 2 pixels) which I can live with, but is still not ideal.    The code below is a very simplified case to demonstrate the issue.  If you comment out the 3 lines related to the weldJoint you will see the original issue I was dealing with.

Is there another approach I can try or should I just run with this? 

local centerX = display.contentCenterX local centerY = display.contentCenterY local fullh = display.contentHeight local fullw = display.contentWidth local physics = require("physics") physics.start() display.setStatusBar( display.HiddenStatusBar ) local bkg = display.newRect(centerX, centerY, fullw, fullh ) bkg:setFillColor(0,0,.5) local plank -- forward reference to plank local lock = display.newRect(0, 0, 100, 100 ) physics.addBody( lock, "static", { density = 10, bounce = 0, friction = 2}) local function drawPlank(event) if event.phase == "began" then if plank and plank.removeSelf ~= nil then print("removing") plank:removeSelf( ) plank = nil physics.pause() end plank = display.newRect( 0, 0, 200, 33 ) plank.anchorX, plank.anchorY = 0.5, 0.5 plank.x, plank.y = centerX , centerY plank.rotation = 90 physics.addBody( plank, "dynamic", { density = 10, bounce = 0, friction = 2}) --plank:setLinearVelocity( 0, 0 ) -- no effect --plank:applyTorque( 0 ) -- no effect local weldJoint = physics.newJoint( "weld", lock, plank , 0, 0 ) --comment this out weldJoint.dampingRatio = 1 --comment this out weldJoint:removeSelf() --comment this out timer.performWithDelay( 1500, function(event) physics.start() end) end end bkg:addEventListener( "touch", drawPlank )

Once again,  many thanks,

Craig

Consider physics.pause() instead of physics.stop().

Rob

Hi Rob,

Sorry I should have made myself clearer (I have updated my topic), I am using physics.pause() now.  The movement is occurring with physics.pause().

Thanks

Craig

Hi Craig,

In your other post, I pointed you to a tutorial on potentially using “touch” joints to keep objects in place. Another possibility, if you haven’t already tried it, is making the objects inactive or forcing them to “sleep” before you pause (and then making the active or wake them up on resume).

Brent

Hey Brent,

I just tried both .isBodyActive and .isAwake and the movement is still there.  It still looks like a weld joint is my best option, even though there is some very small movement.  

Interesting,  I used a timer to call drawPlank and noticed that if there is a delay of more than 1100 then there is no movement (with out modifying the code at all), at 1000 there is still a lot of moment, so it must be some form of inertia? Not sure how since it is a new object?

Note, there is no delay between pause and start, so these are probably having no impact.  I tried adding a delay between the pause and start and had the same results.  

Finally I tried drawing the planks at random locations on the screen and there is no movement, so I am assuming the movement is coming about as a result of a residual of the plank being removed?  The previous planks ‘ghost’ is giving it a nudge?

While this is interesting, it doesn’t really help me because in my game it will be in the same place. So…

With the delay set at 1000 here are the resulting x and rotation values (they should start at x = 160 and rotation = 90).  

As can be seen my results, a weld joint appears to be the way to go.

No changes, using .isAwake and .isBodyActive are all about the same

Plank.x = 156.6544342041      Plank.Rotation = 94.708938598633

Touch Joint

Plank.x = 156.37214660645      Plank.Rotation = 94.891036987305

Weld joint (very slight movement, smaller than I expected)

Plank.x = 160.07453918457      Plank.Rotation = 89.999992370605

Here is the code I was using.

Cheers,

Craig

local centerX = display.contentCenterX local centerY = display.contentCenterY local fullh = display.contentHeight local fullw = display.contentWidth local physics = require("physics") physics.start() display.setStatusBar( display.HiddenStatusBar ) local bkg = display.newRect(centerX, centerY, fullw, fullh ) bkg:setFillColor(0,0,.5) local plank -- forward reference to plank local lock = display.newRect(0, 0, 100, 100 ) physics.addBody( lock, "static", { density = 10, bounce = 0, friction = 2}) physics.pause() local function drawPlank(event) --if event.phase == "began" then if plank and plank.removeSelf ~= nil then plank:removeSelf( ) plank = nil physics.pause() end plank = display.newRect( 0, 0, 200, 33 ) plank.anchorX, plank.anchorY = 0.5, 0.5 plank.x, plank.y = centerX , centerY plank.rotation = 90 physics.addBody( plank, "dynamic", { density = 10, bounce = 0, friction = 2}) --plank.isAwake = false --plank.isBodyActive = false local weldJoint = physics.newJoint( "weld", lock, plank , 0, 0 ) --comment this out weldJoint.dampingRatio = 1 --comment this out weldJoint:removeSelf() --comment this out -- local newJoint = physics.newJoint( "touch", plank, plank.x, plank.y) -- newJoint:setTarget( plank.x, plank.y ) -- newJoint.dampingRatio = 1 -- newJoint.frequency = 1 physics.start() --newJoint:removeSelf() --plank.isAwake = true --plank.isBodyActive = true timer.performWithDelay( 100, function(event) -- slight delay to read change print("Plank.x = ", plank.x, " Plank.Rotation = ", plank.rotation) end) --end end timer.performWithDelay(1000, drawPlank , 10 ) --bkg:addEventListener( "touch", drawPlank )

Hey,

I had a think about the ghost idea and tried one last thing.  I set ‘plank.isActiveBody = false’ before I removed the body, and it worked, no movement, even when I took the delay right down to 200ms…

if plank and plank.removeSelf ~= nil then plank.isBodyActive = false plank:removeSelf( ) plank = nil physics.pause() end

isSensor=true would work around it too (much like isActiveBody=false does)

the true cause is pausing right after removal, and not giving the physics world a tick to process it, so when new body is created it collides with your “ghost” that hasn’t had a chance to be removed yet.  (not everything happens “right now” - particularly wrt physics.  yes, it’s gone from the display, but not yet gone from the paused physics world)  if you’d rather solve it with delays, then it’s right after the removal that you should delay, then the physics update loop will get a chance to run and clean up your old body before you pause it.  THEN you can safely create a new non-colliding body at the same location.

btw, this same root cause is why physics.stop crashes almost everyone’s code…

i’ve even witnessed corona staff in this forum say “don’t use physics.stop, it may crash” (without saying why)

so, if you’d like to try to prevent physics.stop from crashing, then:

  1. you must remove everything  from the physics world (don’t forget joints!)

  2. you must let physics update this “empty” world at least once before trying to stop it

(undocumented, and thus subject to change w corona internals, so ymmv)

Consider physics.pause() instead of physics.stop().

Rob

Hi Rob,

Sorry I should have made myself clearer (I have updated my topic), I am using physics.pause() now.  The movement is occurring with physics.pause().

Thanks

Craig

Hi Craig,

In your other post, I pointed you to a tutorial on potentially using “touch” joints to keep objects in place. Another possibility, if you haven’t already tried it, is making the objects inactive or forcing them to “sleep” before you pause (and then making the active or wake them up on resume).

Brent

Hey Brent,

I just tried both .isBodyActive and .isAwake and the movement is still there.  It still looks like a weld joint is my best option, even though there is some very small movement.  

Interesting,  I used a timer to call drawPlank and noticed that if there is a delay of more than 1100 then there is no movement (with out modifying the code at all), at 1000 there is still a lot of moment, so it must be some form of inertia? Not sure how since it is a new object?

Note, there is no delay between pause and start, so these are probably having no impact.  I tried adding a delay between the pause and start and had the same results.  

Finally I tried drawing the planks at random locations on the screen and there is no movement, so I am assuming the movement is coming about as a result of a residual of the plank being removed?  The previous planks ‘ghost’ is giving it a nudge?

While this is interesting, it doesn’t really help me because in my game it will be in the same place. So…

With the delay set at 1000 here are the resulting x and rotation values (they should start at x = 160 and rotation = 90).  

As can be seen my results, a weld joint appears to be the way to go.

No changes, using .isAwake and .isBodyActive are all about the same

Plank.x = 156.6544342041      Plank.Rotation = 94.708938598633

Touch Joint

Plank.x = 156.37214660645      Plank.Rotation = 94.891036987305

Weld joint (very slight movement, smaller than I expected)

Plank.x = 160.07453918457      Plank.Rotation = 89.999992370605

Here is the code I was using.

Cheers,

Craig

local centerX = display.contentCenterX local centerY = display.contentCenterY local fullh = display.contentHeight local fullw = display.contentWidth local physics = require("physics") physics.start() display.setStatusBar( display.HiddenStatusBar ) local bkg = display.newRect(centerX, centerY, fullw, fullh ) bkg:setFillColor(0,0,.5) local plank -- forward reference to plank local lock = display.newRect(0, 0, 100, 100 ) physics.addBody( lock, "static", { density = 10, bounce = 0, friction = 2}) physics.pause() local function drawPlank(event) --if event.phase == "began" then if plank and plank.removeSelf ~= nil then plank:removeSelf( ) plank = nil physics.pause() end plank = display.newRect( 0, 0, 200, 33 ) plank.anchorX, plank.anchorY = 0.5, 0.5 plank.x, plank.y = centerX , centerY plank.rotation = 90 physics.addBody( plank, "dynamic", { density = 10, bounce = 0, friction = 2}) --plank.isAwake = false --plank.isBodyActive = false local weldJoint = physics.newJoint( "weld", lock, plank , 0, 0 ) --comment this out weldJoint.dampingRatio = 1 --comment this out weldJoint:removeSelf() --comment this out -- local newJoint = physics.newJoint( "touch", plank, plank.x, plank.y) -- newJoint:setTarget( plank.x, plank.y ) -- newJoint.dampingRatio = 1 -- newJoint.frequency = 1 physics.start() --newJoint:removeSelf() --plank.isAwake = true --plank.isBodyActive = true timer.performWithDelay( 100, function(event) -- slight delay to read change print("Plank.x = ", plank.x, " Plank.Rotation = ", plank.rotation) end) --end end timer.performWithDelay(1000, drawPlank , 10 ) --bkg:addEventListener( "touch", drawPlank )

Hey,

I had a think about the ghost idea and tried one last thing.  I set ‘plank.isActiveBody = false’ before I removed the body, and it worked, no movement, even when I took the delay right down to 200ms…

if plank and plank.removeSelf ~= nil then plank.isBodyActive = false plank:removeSelf( ) plank = nil physics.pause() end

isSensor=true would work around it too (much like isActiveBody=false does)

the true cause is pausing right after removal, and not giving the physics world a tick to process it, so when new body is created it collides with your “ghost” that hasn’t had a chance to be removed yet.  (not everything happens “right now” - particularly wrt physics.  yes, it’s gone from the display, but not yet gone from the paused physics world)  if you’d rather solve it with delays, then it’s right after the removal that you should delay, then the physics update loop will get a chance to run and clean up your old body before you pause it.  THEN you can safely create a new non-colliding body at the same location.

btw, this same root cause is why physics.stop crashes almost everyone’s code…

i’ve even witnessed corona staff in this forum say “don’t use physics.stop, it may crash” (without saying why)

so, if you’d like to try to prevent physics.stop from crashing, then:

  1. you must remove everything  from the physics world (don’t forget joints!)

  2. you must let physics update this “empty” world at least once before trying to stop it

(undocumented, and thus subject to change w corona internals, so ymmv)