physics joint-ROPE

Here’s an example of a physics ROPE joint that I have. Touch near the rope ends to move them. You’re not exactly moving the rope ends, you’re just altering their speed and direction. So depending on the tension of the rope, sometimes the end will move easily, sometimes it takes more force to move it where you want. Once you lift your finger, the rope end will stay at that position. It’s kind of interesting to loop the rope and watch it react.


displayMode(FULLSCREEN)

function setup()
    t1,t2=0,0   -- id for rope ends
    tab={}  -- table of physics bodies
    jnt={}  -- table of joints
    j=200   -- number of joints
    len=(WIDTH-20)/j    -- length between joints
    for z=1,j do    -- create bodies
        a = physics.body(CIRCLE,0)
        a.x = 10+z*len
        a.y = HEIGHT-100
        if z==1 or z==j then
            a.type=STATIC   -- lock end points of rope
        end
        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,5))
        end
    end
end

function draw()
    background(40, 40, 50)
    fill(255)
    text("Physics Joint (Rope)",WIDTH/2,HEIGHT-50)
    text("Touch near a rope end to move it.",WIDTH/2,HEIGHT-80)
    strokeWidth(2)
    for z=2,#tab do
        stroke(255)
        if z%2==0 then  -- alternate white and red colors
            stroke(255,0,0)
        end
        line(tab[z].x,tab[z].y,tab[z-1].x,tab[z-1].y)   -- draw line between joints
    end
    noStroke()
    fill(255,0,0)
    ellipse(tab[1].x,tab[1].y,10)   -- draw circles for rope end
    fill(0,255,0)
    ellipse(tab[j].x,tab[j].y,10)
end

function touched(t)
    if t.state==BEGAN then
        -- find which rope end is closest to the touch
        v1=vec2(t.x,t.y)
        d1=v1:dist(vec2(tab[1].x,tab[1].y)) -- distance to red end
        d2=v1:dist(vec2(tab[j].x,tab[j].y)) -- distance to green end
        if d2<d1 then
            t2=t.id -- set to move green end
        elseif t1==0 then 
            t1=t.id -- set to move red end
        end
    end
    if t.state==MOVING then -- move red or green end
        if t.id==t1 then  -- red end
            tab[1].type=DYNAMIC -- make end movable
            -- change linear velocity of red end
            tab[1].linearVelocity=vec2(t.deltaX*200,t.deltaY*200)
        end
        if t.id==t2 then  -- green end
            tab[j].type=DYNAMIC -- make end movable
            -- change linear velocity of green end
            tab[j].linearVelocity=vec2(t.deltaX*200,t.deltaY*200)
        end
    end
    if t.state==ENDED then  -- lock ends to current positions
        if t.id==t1 then
            tab[1].type=STATIC  -- make end stop
            t1=0
        else
            tab[j].type=STATIC  -- make end stop
            t2=0
        end
    end
end

Never thought of using more than one rope joint!
Here’s my rope class renderer method for rope joints using verlet:

Rope = class()

function Rope:init(joint,segamnt)
    -- you can accept and set parameters here
    local pos1,pos2 = joint.anchorA,joint.anchorB
    self.id = level.ropes:amount() + 1
    self.joint = joint
    self.p1 = pos1
    self.p2 = pos2
    self.m = mesh()
    self.m.texture = readImage("Documents:ropeseg")
    self.r = {}
    self.dst = pos1:dist(pos2)
    self.segamnt = segamnt
    self.prevnt = DeltaTime
    self.seglen = (self.dst/segamnt)*0.7
    self.segtbl = {}
    for i=1,segamnt do
        self.segtbl[i] = {}
        self.segtbl[i].pos = pos1+(pos2-pos1):normalize()*self.seglen*i
        self.segtbl[i].prev = self.segtbl[i].pos
        self.segtbl[i].next = self.segtbl[i].pos +vec2(0,-5)
        self.segtbl[i].ang = 0
        self.r[i] = self.m:addRect(self.segtbl[i].pos.x,self.segtbl[i].pos.y,5,self.seglen)
    end
    self.r[segamnt+1] = self.m:addRect(self.p2.x,self.p2.y,5,self.seglen)
    self.i = 0
end

function Rope:draw()
    self.p1 = self.joint.anchorA
    self.p2 = self.joint.anchorB
    local p,dpos,len,va,da,ea,vb,db,eb,grav,stiffness,dt,nt,np,delta
    local vd,vdl,diff
    grav,stiffness,nt = vec2(0,0.1),5,DeltaTime
    local segamnt = self.segamnt
    local segtbl = self.segtbl
    local maxl = self.seglen
    local prevpos = vec2()
    local nextpos = vec2()
    local pos = vec2()
    local segi = nil
    for i=1,segamnt do
        segi = segtbl[i]
        pos = segi.pos
        if i==1 then
            prevpos = self.p1
            nextpos = segtbl[i+1].pos
        elseif i==segamnt then
            prevpos = segtbl[i-1].pos
            nextpos = self.p2
        else
            prevpos = segtbl[i-1].pos
            nextpos = segtbl[i+1].pos
        end
        if not pos or not prevpos or not nextpos then level.ropes:removeRope(self.id) return end
        va = (pos-prevpos)
        da = (maxl-va:len())/va:len()
        ea = va:len()-maxl
        vb = (pos-nextpos)
        db = (maxl-vb:len())/vb:len()
        eb = vb:len()-maxl
        dt = (va*stiffness*da+vb*stiffness*db)
        if dt:len() > 0 then  
            np = (pos-segtbl[i].prev) *(nt/self.prevnt) +(dt)*nt*nt
            segi.pos = segi.pos + np
        end
        if i == segamnt then
            vd = self.p2-segi.pos
            vdl = vd:len()
            diff = vdl-maxl
            vd = vd:normalize()
            if vd:len()>0 then
                segtbl[i].pos = segtbl[i].pos + vd*diff*0.5
            end
        end
        if i > 1 then 
            vd = segtbl[i].pos-segtbl[i-1].pos
            vdl = vd:len()
            diff = vdl-maxl
            vd = vd:normalize()
            if vd:len()>0 then
                segi.pos = segi.pos - vd*diff*0.5
                segtbl[i-1].pos = segtbl[i-1].pos + vd*diff*0.5
            end
            elseif i==1 then
            vd = segi.pos-self.p1
            vdl = vd:len()
            diff = vdl-maxl
            vd = vd:normalize()
            if vd:len()>0 then
                segi.pos = segi.pos - vd*diff*0.5
            end
        end
        
        segi.prev = pos+grav
        pos = segi.pos
        self.prevnt = DeltaTime
        segtbl[i].ang = angleOfPoint(pos-prevpos)/mp+math.pi/2
        dpos = (prevpos+pos)/2
        len = (prevpos:dist(pos))
        self.m:setRect(self.r[i],dpos.x,dpos.y,5,len,segtbl[i].ang)
    end
    self.segtbl = segtbl
    p = segtbl[segamnt].pos
    dpos = (p+self.p2)/2
    len = (p:dist(self.p2))
    self.m:setRect(self.r[segamnt+1],dpos.x,dpos.y,5,len,angleOfPoint(p-self.p2)/mp+math.pi/2)
    self.m:draw()
end

Gets to be quite slow with about 10 ropes though… Easy to implement just put the rope joint and segment amount in. I haven’t accounted for the change of maxLength because of the bugs

Here’s a rope class based on my above code. The parameters are: the x,y position of where the rope starts, the x,y position where it ends, the number of segments, and the length of each segment. This example draws 6 ropes. They don’t do much except hang around.


displayMode(FULLSCREEN)

function setup()
    rp={}
    for r=1,6 do
        table.insert(rp,rope(math.random(WIDTH/2),math.random(HEIGHT/2,HEIGHT),
            math.random(WIDTH/2,WIDTH),math.random(HEIGHT/2,HEIGHT),100,10))
    end
end

function draw()
    background(40, 40, 50)
    for a,b in pairs(rp) do
        b:draw()
    end
end

rope=class()

function rope:init(sx,sy,ex,ey,nbr,len)
    self.tab={} -- table of objects
    self.jnt={} -- table of joints
    self.sx=sx  -- start x,y
    self.sy=sy
    self.ex=ex  -- end x,y 
    self.ey=ey
    self.nbr=nbr    -- number of segments
    self.len=len    -- length of segments
    self.dx=(self.ex-self.sx)/self.nbr
    self.dy=(self.ey-self.sy)/self.nbr   
    for z=1,self.nbr do    -- create bodies
        a = physics.body(CIRCLE,0)
        a.x = self.sx+z*self.dx
        a.y = self.sy+z*self.dy
        if z==1 or z==self.nbr then
            a.type=STATIC   -- lock end points of rope
        end
        table.insert(self.tab,a) -- add bodies to table
        if z>1 then -- create joint information and put in table
            table.insert(self.jnt,physics.joint(ROPE,self.tab[z-1],self.tab[z],
                self.tab[z-1].position,self.tab[z].position,self.len))
        end
    end
end

function rope:draw()
    strokeWidth(2)
    for z=2,#self.tab do
        stroke(255)
        if z%2==0 then  -- alternate white and red colors
            stroke(255,0,0)
        end
        -- draw line between joints
        line(self.tab[z].x,self.tab[z].y,self.tab[z-1].x,self.tab[z-1].y)
    end
    -- draw rope end points
    noStroke()
    fill(255,0,0)
    ellipse(self.tab[1].x,self.tab[1].y,10)   -- draw circles for rope end
    fill(0,255,0)
    ellipse(self.tab[self.nbr].x,self.tab[self.nbr].y,10)
end