DYNAMIC bodies connected by ROPE

OK this has been driving me nuts…

For a long time I thought I had a bug in my app and it was getting to me but after going back to the Physics Lab example I realised it wasn’t me. I changed the joint between circle2 and the box to be a ROPE not a DISTANCE and confirmed my observation.

Two DYNAMIC bodies connected by a ROPE do not collide with each other but will hit other DYNAMIC bodies. Why is this so? Actually as I am typing this I can see a possible reason why this behaviour is so. If the maximum length of the ROPE joint is less than the distance between the outer edges of the two bodies we have a conundrum of two dynamic bodies overlapping and fighting each other with the ROPE constricting them.

My aim is to be able to drag multiple objects around with each object joined to the next with a ROPE so that they can bounce around but can only go a max distance from each other. I want those bodies to be able to knock each other around. Imagine dragging a string with beads tied at various locations along the length. If you move them around they hit each other.

Could someone please suggest how I can simulate this?

Thanks!

@toddixon - why don’t you set up a minimal code example that demonstrates the problem and post it here, to give people something to play with.

You might want to look at the Box2D documentation and forums too, since that is what Codea uses.

It may be related to this issue, still not addressed…

https://codea.io/talk/discussion/comment/59113/#Comment_59113

@Ignatz Thanks… Here’s some code simulating the problem. Basically it’s Physics Lab gutted for the purpose of illustrating what it’s doing wrong for me.

Basically if you drag the first circle and move it around it will will collide with every other circle except for the one it is directly attached to. If you drag a different circle and attempt to hit other circles you will see varying effects.

I simply want every circle to collide with every other one using a ROPE-like effect.

supportedOrientations(LANDSCAPE_ANY)
function setup()    
    lineCapMode(ROUND)
    debugDraw = PhysicsDebugDraw()
    currentTest = Test()
    currentTest:setup()
end

function createCircle(x,y,r)
    local circle = physics.body(CIRCLE, r)
    circle.interpolate = true
    circle.type=DYNAMIC
    circle.x = x
    circle.y = y
    circle.restitution = 0.25
    circle.sleepingAllowed = false
    debugDraw:addBody(circle)
    return circle
end

function createGround()
    local ground = physics.body(POLYGON, vec2(-50,20), vec2(-50,0), vec2(WIDTH+50,0), vec2(WIDTH+50,20))
    ground.type = STATIC
    debugDraw:addBody(ground)
    return ground
end

function draw()
    background(0, 0, 0)
    debugDraw:draw()
end

function touched(touch)
    debugDraw:touched(touch)
end

PhysicsDebugDraw = class()

function PhysicsDebugDraw:init()
    self.bodies = {}
    self.joints = {}
    self.touchMap = {}
end

function PhysicsDebugDraw:addBody(body)
    table.insert(self.bodies,body)
end

function PhysicsDebugDraw:addJoint(joint)
    table.insert(self.joints,joint)
end

function PhysicsDebugDraw:draw()
    
    pushStyle()
    smooth()
    strokeWidth(5)
    stroke(128,0,128)
    
    local gain = 2.0
    local damp = 0.5
    for k,v in pairs(self.touchMap) do
        local worldAnchor = v.body:getWorldPoint(v.anchor)
        local touchPoint = v.tp
        local diff = touchPoint - worldAnchor
        local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
        v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
        line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
    end
    stroke(0,255,0,255)
    strokeWidth(5)
    for k,joint in pairs(self.joints) do
        local a = joint.anchorA
        local b = joint.anchorB
        line(a.x,a.y,b.x,b.y)
    end
    stroke(255,255,255,255)
    noFill()
    for i,body in ipairs(self.bodies) do
        pushMatrix()
        translate(body.x, body.y)
        rotate(body.angle)
    
        if body.type == STATIC then
            stroke(255,255,255,255)
        elseif body.type == DYNAMIC then
            stroke(150,255,150,255)
        elseif body.type == KINEMATIC then
            stroke(150,150,255,255)
        end
    
        if body.shapeType == POLYGON then
            strokeWidth(3.0)
            local points = body.points
            for j = 1,#points do
                a = points[j]
                b = points[(j % #points)+1]
                line(a.x, a.y, b.x, b.y)
            end
        elseif body.shapeType == CHAIN or body.shapeType == EDGE then
            strokeWidth(3.0)
            local points = body.points
            for j = 1,#points-1 do
                a = points[j]
                b = points[j+1]
                line(a.x, a.y, b.x, b.y)
            end      
        elseif body.shapeType == CIRCLE then
            strokeWidth(3.0)
            line(0,0,body.radius-3,0)            
            ellipse(0,0,body.radius*2)
        end
        popMatrix()
    end 
    stroke(255, 0, 0, 255)
    fill(255, 0, 0, 255)
    popStyle()
end

function PhysicsDebugDraw:touched(touch)
    local touchPoint = vec2(touch.x, touch.y)
    if touch.state == BEGAN then
        for i,body in ipairs(self.bodies) do
            if body.type == DYNAMIC and body:testPoint(touchPoint) then
                self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)} 
                return true
            end
        end
    elseif touch.state == MOVING and self.touchMap[touch.id] then
        self.touchMap[touch.id].tp = touchPoint
        return true
    elseif touch.state == ENDED and self.touchMap[touch.id] then
        self.touchMap[touch.id] = nil
        return true
    end
    return false
end

Test= class()

function Test:init()
end

function Test:setup()
    createGround()
    local circle = createCircle(WIDTH/2, HEIGHT/2, 30)
    local circle2 = createCircle(WIDTH/2+70, HEIGHT/2, 30)
    local circle3 = createCircle(WIDTH/2+140, HEIGHT/2, 30)
    local circle4 = createCircle(WIDTH/2+210, HEIGHT/2, 30)
    local circle5 = createCircle(WIDTH/2+280, HEIGHT/2, 30)
    
    local joint = physics.joint(ROPE, circle, circle2, circle.position,circle2.position,150)
    debugDraw:addJoint(joint)
    local joint = physics.joint(ROPE, circle2, circle3, circle2.position,circle3.position,150)
    debugDraw:addJoint(joint)
    local joint = physics.joint(ROPE, circle3, circle4, circle3.position,circle4.position,150)
    debugDraw:addJoint(joint)
    local joint = physics.joint(ROPE, circle4, circle5, circle4.position,circle5.position,150)
    debugDraw:addJoint(joint)
end

@todddixon Hi there, I haven’t been keeping up with Codea for a long time so it’s nice to see new faces. I can’t give you the code you need as I don’t have an iPad or Codea atm.

This is easy to fix, if I can explain in clear terms. Say you have BoxA roped to BoxB, they pass through one another. Instead start off by not roping these two together.

If you create a third BoxC which must be smaller than boxA, position Box C in the center of BoxA give them the same angle. Once you’ve placed BoxC inside of BoxA you want to use physics.joint(WELD,…) between boxC and boxA to make sure that boxC is attached to the center of BoxA. Then you rope BoxC to BoxB and viola!

Here’s an image in case that’s not helping:

Also I forgot to mention you need to set boxC.sensor = true

@todddixon I had this example that does what you want. Instead of just one rope joint, you need several joints to form a rope. The more rope joints you have, the more it acts like a rope. This has 50 rope joints.

displayMode(FULLSCREEN)
supportedOrientations(PORTRAIT_ANY)

function setup()
    tab={}  -- table of physics bodies
    jnt={}  -- table of joints
    j=50    -- number of joints
    for z=1,j do
        size=0
        if z==1 or z==25 or z==j then
            size=40
        end
        a=physics.body(CIRCLE,size)
        a.x=200+z*8
        a.y=HEIGHT-100
        a.restitution=1.1
        table.insert(tab,a) -- add bodies to table
        if z>1 then -- create joint information and put in table
            table.insert(jnt,physics.joint(ROPE,tab[z-1],tab[z],tab[z-1].position,
                        tab[z].position,8))
        end
    end
    b1=physics.body(CIRCLE,20)
    b1.x=400
    b1.y=700
    b1.type=STATIC
    b2=physics.body(CIRCLE,20)
    b2.x=470
    b2.y=315
    b2.type=STATIC
end

function draw()
    background(40, 40, 50)
    strokeWidth(2)
    stroke(255)
    for z=2,#tab do
        line(tab[z].x,tab[z].y,tab[z-1].x,tab[z-1].y)   -- draw line between joints
    end
    fill(255,0,0)
    ellipse(tab[1].x,tab[1].y,80)   -- draw circles on rope end
    ellipse(tab[25].x,tab[25].y,80)
    ellipse(tab[j].x,tab[j].y,80)
    ellipse(b1.x,b1.y,40)
    ellipse(b2.x,b2.y,40)
end

Hi, been lurking here a bit. Trying to find any in this discussion.

Categories, there are no body.categories being used to discern or filter betwixt the bodies. That at least is partially responsible for the collision detection.

you said:

 Basically if you drag the first circle and move it around it will will collide with every other circle except for the one it is directly attached to. If you drag a different circle and attempt to hit other circles you will see varying effects.

I simply want every circle to collide with every other one using a ROPE-like effect. 

As far as the usage of physics.joints they are not supposed to directly interact with what they are directly attached to. Cause they are attached to it.

But if you want to restrict their movement range you might try tinkering with their upper and lower limits and setting them explicitly.

I don’t want to say too much more cause I don’t want to push you in a particular direction.

@Luatee @dave1707 @QMG Thanks for your input. I didn’t think to get back to the discussion and instead started playing one evening and I eventually ended up with a solution that was basically the same suggestion from @Luatee except that interestingly it worked with all ROPEs and no WELDs. It was never actually a full rope-like effect I was after but after seeing the @dave1707 code I may choose to throw that effect in for the purpose I intend. Has all been good feedback. Cheers