[RESOLVED] Unintended spinning

I have four circles as physics objects joined together by a sensor anchor object (in the centre) and to each other with weld joints (in a ring.)

For some reason, they spin. I don’t apply force of any kind and if I try to stop it with damping they continue.

If I add touch joints to move them around eventually the effect gets so bad they spin with almost infinite force and can’t be stopped with any amount of damping.

Can anyone tell me what I’m doing wrong here, please?

The code below is not the literal simplest way of demonstrating the problem, but it is a very simplified version of my code and demonstrates the problem.

[lua]-- spinning balls

display.setStatusBar(display.HiddenStatusBar)

require(“physics”)
physics.start()
physics.setGravity(0,0)
physics.setDrawMode(“hybrid”)

–require(“toucheventlib”)

– rotates a point around the (0,0) point by degrees
– returns new point object
function rotateTo( point, degrees )
local x, y = point.x, point.y
local theta = math.rad(degrees)
local pt = {
x = x * math.cos(theta) - y * math.sin(theta),
y = x * math.sin(theta) + y * math.cos(theta)
}
return pt
end

function midPoint( pts )
local x, y, c = 0, 0, #pts
if (pts.numChildren and pts.numChildren > 0) then c = pts.numChildren end
for i=1, c do
x = x + pts[i].x
y = y + pts[i].y
end
return { x=x/c, y=y/c }
end

function newBall( params )
local group = display.newGroup()
group.x, group.y = params.x, params.y

local circle = display.newCircle( group, 0, 0, params.radius )
local c = params.colour
circle:setFillColor( c.r,c.g,c.b )

physics.addBody( group, {bounce=.8, friction=0, density=.1, radius=params.radius} )

group.linearDamping = 10
group.angularDamping = 1000

return group
end

function newGroup( balls )
– get midpoint of the balls to be anchored together
local pt = midPoint( balls )

– create anchor at that location
local anchor = display.newCircle( pt.x, pt.y, 38 )
anchor.alpha = .3
physics.addBody( anchor, {radius=38, isSensor=true} )

– store the list of balls
anchor.balls = balls

– join the balls to the anchor
for i=1, #balls do
local ball = balls[i]
physics.newJoint( “weld”, anchor, ball, anchor.x, anchor.y, ball.x, ball.y )
ball.anchor = anchor
end

–[[emergency fix - join balls to each other in a circle - does not work!]]–
local a, b = nil, nil
for i=1, #balls do
a = balls[i]
b = balls[i+1]
if (i == #balls) then b = balls[1] end
a.nextjoint = physics.newJoint( “weld”, a, b, a.x, a.y, b.x, b.y )
end

– return the anchor
return anchor
end

– create the balls
local balls = {}
for i=1, 4 do
local pt = rotateTo( {x=20,y=0}, 360/4*i )
balls[#balls+1] = newBall{ colour={r=0,g=0,b=255}, radius=38, x=display.contentCenterX+pt.x, y=display.contentCenterY+pt.y }
end

local anchor = newGroup( balls )[/lua] [import]uid: 8271 topic_id: 33409 reply_id: 333409[/import]

@horacebury Neat problem!

I’m playing with this a little bit, and noticed that if you remove the body on the anchor (comment out line 57) , it rotates the opposite direction.

I also noticed when i commented out the joints on line 75, the four circles sprung apart from each other… neat!

Ultimately I think changing this line (42) did it: (isSensor=true)
[lua] physics.addBody( circle, {bounce=.8, friction=0, density=.1, radius=params.radius,isSensor=true} )[/lua]

But I also wound up changing the balls function to apply the physics body to the circle, rather than the group. (Then returning the circle in stead)

I also removed all the physics joints from your anchor (Uncomment line 114 if you want to use it for something still) , and put touch joints on everything, so you could move the pieces around (I took the touch joint piece directly from: http://developer.coronalabs.com/content/game-edition-physics-joints )

[lua]-- spinning balls

display.setStatusBar(display.HiddenStatusBar)

require(“physics”)
physics.start()
physics.setGravity(0,0)
physics.setDrawMode(“hybrid”)

–require(“toucheventlib”)

– rotates a point around the (0,0) point by degrees
– returns new point object
function rotateTo( point, degrees )
local x, y = point.x, point.y
local theta = math.rad(degrees)
local pt = {
x = x * math.cos(theta) - y * math.sin(theta),
y = x * math.sin(theta) + y * math.cos(theta)
}
return pt
end

function midPoint( pts )
local x, y, c = 0, 0, #pts
if (pts.numChildren and pts.numChildren > 0) then c = pts.numChildren end
for i=1, c do
x = x + pts[i].x
y = y + pts[i].y
end
return { x=x/c, y=y/c }
end
local function dragBody( event )
local body = event.target
local phase = event.phase
local stage = display.getCurrentStage()

if “began” == phase then
stage:setFocus( body, event.id )
body.isFocus = true

– Create a temporary touch joint and store it in the object for later reference
body.tempJoint = physics.newJoint( “touch”, body, event.x, event.y )

elseif body.isFocus then
if “moved” == phase then

– Update the joint to track the touch
body.tempJoint:setTarget( event.x, event.y )

elseif “ended” == phase or “cancelled” == phase then
stage:setFocus( body, nil )
body.isFocus = false

– Remove the joint when the touch ends
body.tempJoint:removeSelf()

end
end

– Stop further propagation of touch event
return true
end
function newBall( params )

–remove group, add physics body to circle, and return that

— local group = display.newGroup()
– group.x, group.y = params.x, params.y

local circle = display.newCircle( params.x, params.y, params.radius )

local c = params.colour
circle:setFillColor( c.r,c.g,c.b )

physics.addBody( circle, {bounce=.8, friction=0, density=.1, radius=params.radius,isSensor=true} )
circle:addEventListener( “touch”, dragBody )
– group.linearDamping = 10
– group.angularDamping = 1000

return circle
end

function newGroup( balls )
– get midpoint of the balls to be anchored together
local pt = midPoint( balls )

– create anchor at that location
local anchor = display.newCircle( pt.x, pt.y, 38 )
anchor:setReferencePoint(display.CenterReferencePoint)
anchor.alpha = .3
physics.addBody( anchor, {radius=38, isSensor=true} )
anchor.bodyType= “dynamic”

anchor:addEventListener( “touch”, dragBody )
– store the list of balls
anchor.balls = balls

– join the balls to the anchor
for i=1, #balls do
local ball = balls[i]
– physics.newJoint( “weld”, anchor, ball, anchor.x, anchor.y, ball.x, ball.y )
ball.anchor = anchor
end

–[[emergency fix - join balls to each other in a circle - does not work!]]–
local a, b = nil, nil
for i=1, #balls do
a = balls[i]
b = balls[i+1]
if (i == #balls) then b = balls[1] end
a.nextjoint = physics.newJoint( “weld”, a, b, a.x, a.y, b.x, b.y )
end

– return the anchor
return anchor
end

– create the balls
local balls = {}
for i=1, 4 do
local pt = rotateTo( {x=20,y=0}, 360/4*i )
balls[#balls+1] = newBall{ colour={r=0,g=0,b=255}, radius=38, x=display.contentCenterX+pt.x, y=display.contentCenterY+pt.y }
end

local anchor = newGroup( balls )[/lua]

[import]uid: 135765 topic_id: 33409 reply_id: 132735[/import]

I see, but the problem is that I need four circles joined together to act as a single body, which I can split up later. They also need to bounce off other objects, so I can’t make them sensors. It should not matter that the physics body is applied to the group and not the circle, because the display object will not have any effect on the physics element.

It is the unintended angular momentum that worries me.

I have produced a separate example, extremely simplified this time. While the code is very different and the effect is much slower, it is still there, making me worry that this is a Box2D implementation bug:

[lua]local anchor

local a, b, c, d

anchor = display.newCircle( display.contentCenterX, display.contentCenterY, 100 )

a = display.newCircle( display.contentCenterX-50, display.contentCenterY-50, 100 )
b = display.newCircle( display.contentCenterX+50, display.contentCenterY-50, 100 )
c = display.newCircle( display.contentCenterX-50, display.contentCenterY+50, 100 )
d = display.newCircle( display.contentCenterX+50, display.contentCenterY+50, 100 )

physics.addBody( anchor, {radius=100,isSensor=true} ) – isSensor=true|false does not stop the rotation

physics.addBody( a, {radius=100} )
physics.addBody( b, {radius=100} )
physics.addBody( c, {radius=100} )
physics.addBody( d, {radius=100} )

physics.newJoint( “weld”, anchor, a, anchor.x, anchor.y, a.x, a.y )
physics.newJoint( “weld”, anchor, b, anchor.x, anchor.y, b.x, b.y )
physics.newJoint( “weld”, anchor, c, anchor.x, anchor.y, c.x, c.y )
physics.newJoint( “weld”, anchor, d, anchor.x, anchor.y, d.x, d.y )

physics.newJoint( “weld”, a, b, a.x, a.y, b.x, b.y )
physics.newJoint( “weld”, b, c, b.x, b.y, c.x, c.y )
physics.newJoint( “weld”, c, d, c.x, c.y, d.x, d.y )
physics.newJoint( “weld”, d, a, d.x, d.y, a.x, a.y )[/lua] [import]uid: 8271 topic_id: 33409 reply_id: 132738[/import]

Well, it appears as though changing line 42 in the original listing to this solves the problem:
[lua]physics.addBody( group, {bounce=.8, friction=0, density=.1, radius=params.radius, filter={groupIndex=-1}} )[/lua]

I believe it is basically because the 4 circle bodies are causing friction against each other. Adding a collision filter (albeit the most basic type) stops them from colliding against each other.

I’m just not sold on why this should happen.

My second sample can also be updated with this:
[lua]physics.addBody( anchor, {radius=100,isSensor=true,filter={groupIndex=-1}} )

physics.addBody( a, {radius=100,filter={groupIndex=-1}} )
physics.addBody( b, {radius=100,filter={groupIndex=-1}} )
physics.addBody( c, {radius=100,filter={groupIndex=-1}} )
physics.addBody( d, {radius=100,filter={groupIndex=-1}} )[/lua] [import]uid: 8271 topic_id: 33409 reply_id: 132756[/import]

@horacebury Neat problem!

I’m playing with this a little bit, and noticed that if you remove the body on the anchor (comment out line 57) , it rotates the opposite direction.

I also noticed when i commented out the joints on line 75, the four circles sprung apart from each other… neat!

Ultimately I think changing this line (42) did it: (isSensor=true)
[lua] physics.addBody( circle, {bounce=.8, friction=0, density=.1, radius=params.radius,isSensor=true} )[/lua]

But I also wound up changing the balls function to apply the physics body to the circle, rather than the group. (Then returning the circle in stead)

I also removed all the physics joints from your anchor (Uncomment line 114 if you want to use it for something still) , and put touch joints on everything, so you could move the pieces around (I took the touch joint piece directly from: http://developer.coronalabs.com/content/game-edition-physics-joints )

[lua]-- spinning balls

display.setStatusBar(display.HiddenStatusBar)

require(“physics”)
physics.start()
physics.setGravity(0,0)
physics.setDrawMode(“hybrid”)

–require(“toucheventlib”)

– rotates a point around the (0,0) point by degrees
– returns new point object
function rotateTo( point, degrees )
local x, y = point.x, point.y
local theta = math.rad(degrees)
local pt = {
x = x * math.cos(theta) - y * math.sin(theta),
y = x * math.sin(theta) + y * math.cos(theta)
}
return pt
end

function midPoint( pts )
local x, y, c = 0, 0, #pts
if (pts.numChildren and pts.numChildren > 0) then c = pts.numChildren end
for i=1, c do
x = x + pts[i].x
y = y + pts[i].y
end
return { x=x/c, y=y/c }
end
local function dragBody( event )
local body = event.target
local phase = event.phase
local stage = display.getCurrentStage()

if “began” == phase then
stage:setFocus( body, event.id )
body.isFocus = true

– Create a temporary touch joint and store it in the object for later reference
body.tempJoint = physics.newJoint( “touch”, body, event.x, event.y )

elseif body.isFocus then
if “moved” == phase then

– Update the joint to track the touch
body.tempJoint:setTarget( event.x, event.y )

elseif “ended” == phase or “cancelled” == phase then
stage:setFocus( body, nil )
body.isFocus = false

– Remove the joint when the touch ends
body.tempJoint:removeSelf()

end
end

– Stop further propagation of touch event
return true
end
function newBall( params )

–remove group, add physics body to circle, and return that

— local group = display.newGroup()
– group.x, group.y = params.x, params.y

local circle = display.newCircle( params.x, params.y, params.radius )

local c = params.colour
circle:setFillColor( c.r,c.g,c.b )

physics.addBody( circle, {bounce=.8, friction=0, density=.1, radius=params.radius,isSensor=true} )
circle:addEventListener( “touch”, dragBody )
– group.linearDamping = 10
– group.angularDamping = 1000

return circle
end

function newGroup( balls )
– get midpoint of the balls to be anchored together
local pt = midPoint( balls )

– create anchor at that location
local anchor = display.newCircle( pt.x, pt.y, 38 )
anchor:setReferencePoint(display.CenterReferencePoint)
anchor.alpha = .3
physics.addBody( anchor, {radius=38, isSensor=true} )
anchor.bodyType= “dynamic”

anchor:addEventListener( “touch”, dragBody )
– store the list of balls
anchor.balls = balls

– join the balls to the anchor
for i=1, #balls do
local ball = balls[i]
– physics.newJoint( “weld”, anchor, ball, anchor.x, anchor.y, ball.x, ball.y )
ball.anchor = anchor
end

–[[emergency fix - join balls to each other in a circle - does not work!]]–
local a, b = nil, nil
for i=1, #balls do
a = balls[i]
b = balls[i+1]
if (i == #balls) then b = balls[1] end
a.nextjoint = physics.newJoint( “weld”, a, b, a.x, a.y, b.x, b.y )
end

– return the anchor
return anchor
end

– create the balls
local balls = {}
for i=1, 4 do
local pt = rotateTo( {x=20,y=0}, 360/4*i )
balls[#balls+1] = newBall{ colour={r=0,g=0,b=255}, radius=38, x=display.contentCenterX+pt.x, y=display.contentCenterY+pt.y }
end

local anchor = newGroup( balls )[/lua]

[import]uid: 135765 topic_id: 33409 reply_id: 132735[/import]

I see, but the problem is that I need four circles joined together to act as a single body, which I can split up later. They also need to bounce off other objects, so I can’t make them sensors. It should not matter that the physics body is applied to the group and not the circle, because the display object will not have any effect on the physics element.

It is the unintended angular momentum that worries me.

I have produced a separate example, extremely simplified this time. While the code is very different and the effect is much slower, it is still there, making me worry that this is a Box2D implementation bug:

[lua]local anchor

local a, b, c, d

anchor = display.newCircle( display.contentCenterX, display.contentCenterY, 100 )

a = display.newCircle( display.contentCenterX-50, display.contentCenterY-50, 100 )
b = display.newCircle( display.contentCenterX+50, display.contentCenterY-50, 100 )
c = display.newCircle( display.contentCenterX-50, display.contentCenterY+50, 100 )
d = display.newCircle( display.contentCenterX+50, display.contentCenterY+50, 100 )

physics.addBody( anchor, {radius=100,isSensor=true} ) – isSensor=true|false does not stop the rotation

physics.addBody( a, {radius=100} )
physics.addBody( b, {radius=100} )
physics.addBody( c, {radius=100} )
physics.addBody( d, {radius=100} )

physics.newJoint( “weld”, anchor, a, anchor.x, anchor.y, a.x, a.y )
physics.newJoint( “weld”, anchor, b, anchor.x, anchor.y, b.x, b.y )
physics.newJoint( “weld”, anchor, c, anchor.x, anchor.y, c.x, c.y )
physics.newJoint( “weld”, anchor, d, anchor.x, anchor.y, d.x, d.y )

physics.newJoint( “weld”, a, b, a.x, a.y, b.x, b.y )
physics.newJoint( “weld”, b, c, b.x, b.y, c.x, c.y )
physics.newJoint( “weld”, c, d, c.x, c.y, d.x, d.y )
physics.newJoint( “weld”, d, a, d.x, d.y, a.x, a.y )[/lua] [import]uid: 8271 topic_id: 33409 reply_id: 132738[/import]

Well, it appears as though changing line 42 in the original listing to this solves the problem:
[lua]physics.addBody( group, {bounce=.8, friction=0, density=.1, radius=params.radius, filter={groupIndex=-1}} )[/lua]

I believe it is basically because the 4 circle bodies are causing friction against each other. Adding a collision filter (albeit the most basic type) stops them from colliding against each other.

I’m just not sold on why this should happen.

My second sample can also be updated with this:
[lua]physics.addBody( anchor, {radius=100,isSensor=true,filter={groupIndex=-1}} )

physics.addBody( a, {radius=100,filter={groupIndex=-1}} )
physics.addBody( b, {radius=100,filter={groupIndex=-1}} )
physics.addBody( c, {radius=100,filter={groupIndex=-1}} )
physics.addBody( d, {radius=100,filter={groupIndex=-1}} )[/lua] [import]uid: 8271 topic_id: 33409 reply_id: 132756[/import]