Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

DYNAMIC bodies connected by ROPE

edited February 2016 in Questions Posts: 20

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!

Comments

  • IgnatzIgnatz Mod
    Posts: 5,396

    @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.

  • IgnatzIgnatz Mod
    Posts: 5,396

    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

  • edited February 2016 Posts: 20

    @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
    
  • edited February 2016 Posts: 1,595

    @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

  • dave1707dave1707 Mod
    Posts: 7,520

    @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
    
  • QMGQMG
    Posts: 9

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

  • QMGQMG
    Posts: 9

    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

Sign In or Register to comment.