Trampoline and ball

Shows how to incorporate verlet into box2D physics, could be used for a game.


--# Main
-- Trampoline

-- Use this function to perform your initial setup
function setup()
    strings = {}
    for i=1,1 do
        strings[i] = Rope(vec2(0,100*-i+600),vec2(WIDTH-0,100*-i+600),50)
    end
    parameter.watch("1/DeltaTime")
    ball = physics.body(CIRCLE,60)
    ball.position = vec2(WIDTH/2,HEIGHT*0.8)
    ball.restitution = 0.9
    walls = physics.body(CHAIN,true,vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT))
end

function angleOfPoint(pnt)
    return math.atan2(pnt.y,pnt.x)
end
function touched(t) 
    ball.position = vec2(t.x,t.y)
    if t.state == MOVING then
    ball.linearVelocity = vec2(t.deltaX,t.deltaY)*20
    end
end
-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)
    ellipse(ball.x,ball.y,ball.radius*2)
    --ball.radius = ball.linearVelocity:len()*0.01+40
    -- Do your drawing here
    for k,v in pairs(strings) do
        v:draw()
    end
end


--# Rope
Rope = class()

function Rope:init(pos1,pos2,segamnt)
    -- you can accept and set parameters here
    self.joint = joint
    self.p1 = pos1
    self.p2 = pos2
    self.m = mesh()
    self.m.texture = readImage("Documents:gradbar")
    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()
    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.01),0.01,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 d=1,4 do
    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+grav-prevpos)
        da = (maxl-va:len())/va:len()
        ea = va:len()-maxl
        vb = (pos+grav-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
            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
        if ball:testPoint(pos) then
            local dir = pos-ball.position
            self.segtbl[i].pos = self.segtbl[i].pos+dir:normalize()*(ball.radius-dir:len())
            ball:applyForce(-dir:normalize()*(ball.radius-dir:len())*10-ball.linearVelocity/60,self.segtbl[i].pos)
        end
        self.prevnt = DeltaTime
        segtbl[i].ang = angleOfPoint(pos-prevpos)+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
    len = (p:dist(self.p2))
    self.m:setRect(self.r[segamnt+1],dpos.x,dpos.y,5,len,math.rad(angleOfPoint(p-self.p2))+math.pi/2)
    end
    self.m:draw()
end

Enjoy!

And a different one updated with friction for the balls:


--# Main
-- Trampoline

-- Use this function to perform your initial setup
function setup()
    strings = {}
    for i=1,1 do
        strings[i] = Rope(vec2(0,100*-i+600),vec2(WIDTH-0,100*-i+600),40)
    end
    parameter.watch("1/DeltaTime")
    ball = {}
    for i=1,5 do
        ball[i] = physics.body(CIRCLE,math.random(30,30))
        ball[i].position = vec2(30*i,HEIGHT*0.8)
        ball[i].restitution = 0.25
        ball[i].gravityScale = 0
    end
    walls = physics.body(CHAIN,true,vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT))
    tchs = {}
end

function angleOfPoint(pnt)
    return math.atan2(pnt.y,pnt.x)
end
function touched(t) 
    if t.state == BEGAN then
        table.insert(tchs,t)
    end
    for k,v in pairs(tchs) do
        if v.id == t.id then
            tchs[k] = t
            if t.state == ENDED then
                table.remove(tchs,k)
            end
        end
    end
    if #tchs == 1 then
        strings[1].p1 = vec2(tchs[1].x,tchs[1].y)
    end
    if #tchs == 2 then
        strings[1].p1 = vec2(tchs[1].x,tchs[1].y)
        strings[1].p2 = vec2(tchs[2].x,tchs[2].y)
    end
end
-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)
    for i=1,#ball do
        ball[i]:applyForce(vec2(Gravity.x,Gravity.y)*40)
        ball[i]:applyForce(vec2(UserAcceleration.x,UserAcceleration.y)*-350)
        sprite("Documents:slider",ball[i].x,ball[i].y,ball[i].radius*2,ball[i].radius*2)
    end
    --ball.radius = ball.linearVelocity:len()*0.01+40
    -- Do your drawing here
    for k,v in pairs(strings) do
        v:draw()
    end
end


--# Rope
Rope = class()

function Rope:init(pos1,pos2,segamnt)
    -- you can accept and set parameters here
    self.joint = joint
    self.p1 = pos1
    self.p2 = pos2
    self.m = mesh()
    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()
    local p,dpos,len,va,da,ea,vb,db,eb,grav,stiffness,dt,nt,np,delta
    local vd,vdl,diff
    grav,stiffness,nt = -vec2(Gravity.x,Gravity.y)*0.01,0.01,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 d=1,4 do
    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
            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   
        for j=1,#ball do
            if ball[j]:testPoint(pos) then
                local dir = pos-ball[j].position
                self.segtbl[i].pos = self.segtbl[i].pos+dir:normalize()*(ball[j].radius-dir:len())
                ball[j]:applyForce(-dir:normalize()*(ball[j].radius-dir:len())*10-
                ball[j].linearVelocity/60+(segi.prev-pos)*0.1,pos)
            end
        end
        segi.prev = pos+grav
        self.prevnt = DeltaTime
        segtbl[i].ang = angleOfPoint(pos-prevpos)+math.pi/2
        dpos = (prevpos+segi.pos)/2
        len = (prevpos:dist(segi.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
    len = (p:dist(self.p2))
    self.m:setRect(self.r[segamnt+1],dpos.x,dpos.y,5,len,math.rad(angleOfPoint(p-self.p2))+math.pi/2)
    end
    self.m:draw()
end

@luatee nice!
Note the second project is using a sprite not available… Maybe replace it by a circle?

@Jmv38 yes sorry, I used a sprite to speed it up. Thanks.