Howdy, Stranger!

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

In this Discussion

Interactive Elastic Ropes

edited July 2013 in Examples Posts: 437

This is an example of what can be done with the ROPE joint, searching for something similar to ContreJour game, check code and video:

--# Main
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
physics.resume()

-- Use this function to perform your initial setup
function setup() 
    ground = createGround(140)   
    rope = ElasticRope(WIDTH/2, HEIGHT/2-10)
    player = createCircle(WIDTH/2, 179,21)
    player.info = {name="player"}
    -- walls
    left = physics.body(EDGE, vec2(0,0), vec2(0,HEIGHT))
    right= physics.body(EDGE, vec2(WIDTH,0), vec2(WIDTH, HEIGHT))
    top  = physics.body(EDGE, vec2(0,HEIGHT), vec2(WIDTH, HEIGHT))
end


function createCircle(x,y,r)
    local circle = physics.body(CIRCLE, r)
    -- enable smooth motion
    circle.interpolate = true
    circle.x = x
    circle.y = y
    circle.restitution = 0.25
  
    return circle
end

function createBox(x,y,w,h)
    -- polygons are defined by a series of points in counter-clockwise order
    local box = physics.body(POLYGON, vec2(-w/2,h/2), vec2(-w/2,-h/2), vec2(w/2,-h/2), vec2(w/2,h/2))
    box.interpolate = true
    box.x = x
    box.y = y
    box.restitution = 0.25
 
    return box
end

function createGround(height)
    local ground = physics.body(POLYGON, vec2(0,height), vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,height))
    ground.type = STATIC

    return ground
end



-- This function gets called once every frame
function draw()
    -- This sets the background color to black
    background(0, 0, 0)
    --tint(142, 134, 134, 98)
     sprite("SpaceCute:Background",WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
    noTint()
    blendMode(ADDITIVE)
    fill(161, 187, 188, 84)
    noStroke()
    rect(
      ground.points[1].x, ground.points[1].y,
      ground.points[4].x, ground.points[4].y/2
    )
    blendMode(NORMAL)
    physics.gravity(Gravity)
    rope:updateForces()
    rope:draw()
    pushMatrix()
     translate(player.x, player.y)
     rotate(player.angle)
     sprite("Platformer Art:Battor Flap 2", 0,0,60)
    popMatrix()
    
  
end

function touched(touch)
    if rope:touched(touch) == false then
        
    end
end

function collide(contact)
    if contact.state == BEGAN and
       contact.bodyA and contact.bodyA.info and contact.bodyA.info.name and
       contact.bodyB and contact.bodyB.info and contact.bodyB.info.name 
    then
        local na = contact.bodyA.info.name
        local nb = contact.bodyB.info.name
        if na == "ball" and nb=="player" then
            ball = contact.bodyA
        elseif nb == "ball" and na == "player" then
            ball = contact.bodyB
        end
        
        if player and ball then 
            if #player.joints==0 and ball.info.dragged then
                player.x = ball.x
                player.y = ball.y
                player.info.joint = physics.joint(
                 REVOLUTE, ball, player, ball.position, player.position
                )
                print("Attached")
            
            end
        end
    end
end


--# ElasticRope
ElasticRope = class()
function ElasticRope:init(x,y)
    self.touchMap= {}
    self.touchBod= {}
    self.touchBut= {}
    self.joints  = {}
    self.base = createBox(x, y, 60, 80)
    self.base.info = {
      btnFunction = function()
        -- dettach ball from base
        if player.info.joint then
            player.info.joint:destroy()
            player.info.joint = nil
        end
      end
    }
    self.base.type = STATIC
    self.ball = createCircle(self.base.x,self.base.y - 150, 30)
    self.ball.density = 0.75
    self.ball.info = {name = "ball", dragged = false}
    self.ball.fixedRotation = true
    self.ball.angularDamping = 0.1
    
    self.rope = physics.joint(
        ROPE, self.base, self.ball, self.base.position,
         self.ball.position, 200
    )
    
    
    self.links = {}
    local lc = 10
    local len = (self.base.y - self.ball.y) / lc
    local p = self.base.position - vec2(0, len/2)
    local prev = self.base
    for i = 1,lc do
        local link = createBox(p.x, p.y, 5, len)
        link.mask = {10}
        local j = physics.joint(REVOLUTE, prev, link, p + vec2(0, len/2))
        prev = link
        table.insert(self.joints,j )
        link.info = {joint = j} -- save joint to stay alive in the physics system
        table.insert(self.links, link)
        p.y = p.y - len
    end
    local j = physics.joint(REVOLUTE, prev, self.ball, self.ball.position)
    prev.info = {joint = j}
    -- add bodies to interactive touch maps
    table.insert(self.touchBod, self.ball)
    table.insert(self.touchBut, self.base)
end


function ElasticRope:draw() 
--[[  if the base can be moved:  
    local gain = 250 * self.base.mass
    local damp = 0.75
    local vel = self.base.linearVelocity
    self.base:applyForce(gain * self.dir - damp * vel)
    self.base.linearDamping = vel:len() / 50
 --]]   
    pushMatrix()
     translate(self.base.x, self.base.y)
     rotate(self.base.angle)
     if player.info.joint then
      sprite("Platformer Art:Block Special", 0, 0, 60)
     else
      sprite("Platformer Art:Block Special Brick", 0, 0, 60)
     end
    popMatrix()  
    
    pushMatrix()
     translate(self.ball.x, self.ball.y)
     rotate(self.ball.angle)
     sprite("Platformer Art:Coin", 0, 0, self.ball.radius*2.5)
    popMatrix()   
    tint(214, 149, 42, 255)
    for k,v in ipairs(self.links) do
        pushMatrix() 
        translate(v.x,v.y)
        rotate(v.angle)
        sprite("Space Art:Beam", 0,0,15,25)     
        popMatrix()
    end
    noTint()
    
end

function ElasticRope:touched(touch)
    local touchPoint = vec2(touch.x, touch.y)
    if touch.state == BEGAN then
        -- first check buttons
        for i,body in ipairs(self.touchBut) do
            if body:testPoint(touchPoint) then
                -- call button function
                if body.info and body.info.btnFunction then
                    body.info.btnFunction()
                    return true
                end
            end
        end
        for i,body in ipairs(self.touchBod) do
            if body.type == DYNAMIC and body:testPoint(touchPoint) then
                body.info.dragged = true
                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].body.info.dragged = false
        self.touchMap[touch.id] = nil
        return true;
    end
    return false
end

function ElasticRope:updateForces()
    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)
    end
end

function ElasticRope:hit(other, contact, first)
    if contact.state == BEGAN then
        local imp = math.max(0, (contact.normalImpulse-25) / 25)
        if imp > 0.0 then
            sound(SOUND_HIT, 47051, imp)   
            if other and other.damage then other:damage(imp) end
        end
    end
end

You have to catch the fly with the coin xD

Comments

  • Posts: 2,820

    Sweet. What is it with you writing code I was planning on writing. Thanks!

Sign In or Register to comment.