Rope Physics Help

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Rope Physics Help

Post by pgimeno »

I'm trying to model a rope, but so far I have been unsuccessful. It doesn't behave quite like a rope.

The rope is modelled by using multiple line segments. It's an isolated main.lua file so I can work out the details comfortably.

I'm pretty sure I'm calculating the forces wrong. Just in case, I'm calculating the centre of mass of all the segments starting from the last, because I felt it was important, but I couldn't do anything useful with it so I can remove it if it's not used.

I'm using Verlet integration because of its simplicity.

Can someone help me with the force calculation?

Code: Select all

local ropeNSegs = 15
local ropeSegLen = 30
local ropeSegMass = 1
local gravity = 100
local dragx, dragy = 0.001, 0.001

local rope = {}
local pts = {400, 50}


function love.load()
  -- Uncomment for having a stable testcase for debugging
  --love.math.setRandomSeed(8)

  for i = 1, ropeNSegs do
    local angle = love.math.random() * (math.pi - 0.2) + 0.1
    local seg = {
      x = math.cos(angle) * ropeSegLen,
      y = math.sin(angle) * ropeSegLen,
      oldx = 0,
      oldy = 0
    }
    -- Initialize to zero velocity
    seg.oldx = seg.x
    seg.oldy = seg.y
    rope[i] = seg
  end
  love.graphics.setLineJoin("none")
end

function love.update(dt)
  local dt2 = dt * dt * 0.5

  -- nsegs is the number of segments hanging from this pivot
  -- 1 for rope[ropeNSegs], 2 for rope[ropeNSegs-1] etc.
  -- Remember the segments are expressed as vectors.
  local nsegs = 1
  -- SumX/Y is the running sum of all the individual CoMs so far
  local SumX = 0
  local SumY = 0
  -- Loop in reverse
  for i = ropeNSegs, 1, -1 do
    local seg = rope[i]
    -- The minus 0.5 gives us a point in the centre of the segment
    SumX = SumX + seg.x * (nsegs - 0.5)
    SumY = SumY + seg.y * (nsegs - 0.5)

    -- Centre of mass relative to this pivot point
    local CoMX = SumX / nsegs
    local CoMY = SumY / nsegs

    -- Calculate the force on this pivot
    local ForceX = 0
    local ForceY = nsegs * ropeSegMass * gravity

    -- Apply Verlet to this endpoint, with mass ropeSegMass * nsegs
    local newsegx = seg.x +  (seg.x - seg.oldx) * (1 - dragx) + ForceX / (nsegs * ropeSegMass) * dt2
    local newsegy = seg.y + ((seg.y - seg.oldy) * (1 - dragy) + ForceY / (nsegs * ropeSegMass) * dt2)

    -- Constrain length
    local seglen = math.sqrt(newsegx^2 + newsegy^2)
    if seglen < 0.00001 then seglen = 1 end
    newsegx = newsegx / seglen * ropeSegLen
    newsegy = newsegy / seglen * ropeSegLen

    -- "Rotate" the values: old <- current <- new
    seg.oldx = seg.x
    seg.oldy = seg.y
    seg.x = newsegx
    seg.y = newsegy

    nsegs = nsegs + 1
  end
end

function love.draw()
  for i = 1, ropeNSegs do
    pts[i + i + 1] = pts[i + i - 1] + rope[i].x
    pts[i + i + 2] = pts[i + i - 0] + rope[i].y
  end
  love.graphics.line(pts)
end

function love.keypressed(k) if k == "escape" then love.event.quit() end end
Edit: To clarify, what I'm after is a rope that meets these criteria:
  1. fast to calculate
  2. constrained in length, at least visibly (i.e. doesn't look like a rubber band)
  3. somewhat realistic-looking
  4. without love.physics
I got 1, 2 and 4, but failed 3.
Last edited by pgimeno on Fri Mar 09, 2018 11:39 am, edited 1 time in total.
lachlaan
Prole
Posts: 30
Joined: Sun Jun 30, 2013 7:23 pm

Re: Rope Physics Help

Post by lachlaan »

It behaves pretty rope like considering it's still line segments in the end. Consider a rope is made up of infinitesimaly small segments and there's rotation/bend tension between every single small segment you could pick. That could be a thing your rope is lacking, segments being too long and acting as tiny pendulums while not looking as curvy, as well as a lack of rotational/bend friction or resistance that'd make it smooth out sooner.

Having stared at it a bit it seems fairly okay. About as accurate as you can get a bunch of segments. Make it so it falls down from a position closer to the side so you see it swing. If you think of letting a rope uncoil all of a sudden it'd do mostly what your segments are doing and accordeon as they distribute the wave along the length. The one force to look out would be that, the rotational tension/friction. You have some sort of x and y component of drag but i'm not sure it's what you're looking for, it just slows it all down.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Rope Physics Help

Post by pgimeno »

Thanks for your input. I know it's possible to do better using segments. https://gamedev.stackexchange.com/quest ... reate-rope

Of course my segments will be shortened in the final version; this is a sort of debug version. Adding more segments and making them shorter doesn't help; it's nothing like the animated gif in the link above. Multiplying the vertical force by nsegs seems to help a bit, but not enough. It still doesn't feel realistic.

I feel it fails the "pendulum law" where the period of the segments that are higher up should be roughly longer than the ones lower down. In other words, the lower end should wiggle more than the top.
lachlaan
Prole
Posts: 30
Joined: Sun Jun 30, 2013 7:23 pm

Re: Rope Physics Help

Post by lachlaan »

That guy mentions he handles it like a spring, so perhaps you're omitting the tension of each segment on the segments below them, pulling back upward, rather than the segments themselves just being constrained to dangle from the above joint without being affected by it much?

Basically he said it pulls up on each joint provided there's an opposing force, otherwise it dangles loosely.

Edit: Also sorry for the lazy replies, it's a fascinating thing to think about but i'm too lazy atm to remember how2physics xD
Ostego160
Prole
Posts: 14
Joined: Tue Oct 17, 2017 7:18 pm

Re: Rope Physics Help

Post by Ostego160 »

Hi,

Attached is a rope simulator that I threw together using verlet integration. Its very imperfect and not time stepped but the effect is decent.
verlet.love
(3.07 KiB) Downloaded 465 times
Hope this helps!
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Rope Physics Help

Post by pgimeno »

Thank you. That rope looks more natural, but it looks too much like a rubber band with a weight or something like that. I've updated the OP with the criteria I am aiming for.

Your rope meets 1, 3 and 4 but fails 2. A search in the forums revealed this thread, which looks great: viewtopic.php?f=5&t=12330 but a look at the code shows that it meets 2, 3 and 4 but it fails 1 because it applies 40 iterations to every node every frame.

I've also found several attempts using physics, which fail 4.

@lachlaan I'm pretty sure it's that, but I don't see how to apply that force there. Perhaps my model of having a chain of relative vectors instead of absolute positions is not a good idea.

Edit: I've tried with love.physics and it's quite straightforward. I guess people having trouble with this are using the wrong approach regarding bodies/joints, as I've seen them use of DistanceJoint or RopeJoint. Making the body a thin rectangle for the segment (with some damping to avoid too much wiggle), and the joint be a simple revolute joint, the effect is satisfactory; unfortunately, that doesn't help me because I need to do it without physics.

Code: Select all

--local drawWorld = require 'debugWorldDraw_mod'
local world

local ropeNSegs = 15
local ropeSegLen = 30
local ropeSegMass = 1
local gravity = 10
local dragx, dragy = 0.001, 0.001
local k = 200

local rope = {}

function love.load()
  world = love.physics.newWorld(0, 1000)
  local posx = 400
  local posy = 50
  rope[0] = {}
  rope[0].body = love.physics.newBody(world, posx, posy, "static")
  for i = 1, ropeNSegs do
    local angle = love.math.random() * (math.pi - 0.2) + 0.1
    local seg = {}
    local newposx = posx + math.cos(angle) * ropeSegLen
    local newposy = posy + math.sin(angle) * ropeSegLen
    seg.body = love.physics.newBody(world, newposx, newposy, "dynamic")
    seg.shape = love.physics.newPolygonShape(0, -0.1, 0, 0.1, -ropeSegLen, 0.1, -ropeSegLen, -0.1)
    seg.body:setAngle(angle)
    seg.fix = love.physics.newFixture(seg.body, seg.shape, 1)
    seg.joint = love.physics.newRevoluteJoint(seg.body, rope[i-1].body, posx, posy)
    seg.body:setLinearDamping(0.1)
    seg.body:setAngularDamping(10)
    rope[i] = seg
    posx = newposx
    posy = newposy
  end
end

function love.update(dt)
  world:update(dt)
end

function love.draw()
--  love.graphics.clear(255,255,255)
--  drawWorld(world, 0, 0, 800, 600)
  local line = love.graphics.line
  for i = 1, ropeNSegs do
    local body = rope[i].body
    local angle = body:getAngle()
    local posx, posy = body:getPosition()
    local prevposx = posx - math.cos(angle) * ropeSegLen
    local prevposy = posy - math.sin(angle) * ropeSegLen
    line(posx, posy, prevposx, prevposy)
  end
end
User avatar
NotARaptor
Citizen
Posts: 59
Joined: Thu Feb 22, 2018 3:15 pm

Re: Rope Physics Help

Post by NotARaptor »

I just threw this together quickly - looks to be pretty rope-ish to me, doesn't rely on love.physics.
screenshot.png
screenshot.png (28.04 KiB) Viewed 9208 times
It doesn't do collisions or anything, but that could be added easily enough.

I added two ropes - one has zero drag, the other does have drag, so you can see the difference.

The first point of both ropes is static, the last point can be free or static (hold the spacebar to fix them, release to free them)
rope-test.love
(3.66 KiB) Downloaded 282 times
Hope this helps!

UPDATED : Fixed case issue.
Attachments
rope-test.love
(4.25 KiB) Downloaded 258 times
Last edited by NotARaptor on Tue Mar 13, 2018 4:30 pm, edited 1 time in total.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Rope Physics Help

Post by pgimeno »

Thanks. That uses the same technique as in the thread I linked. It makes 20 passes. I was aiming for something faster. Reducing the count to 1 pass keeps it fairly realistic but too elastic. Even with the 20 passes the length oscillates a little bit, though not visibly.

I've tried several variations. Switching off vsync makes the damping much more noticeable, which suggests that it needs to be compensated for by using dt. Fixing the lengths after they are altered results in disaster, which makes me think that Verlet is a bad approach for correcting the lengths because it misrepresents the speed in the next timestep. I will try switching to a different integrator.
User avatar
NotARaptor
Citizen
Posts: 59
Joined: Thu Feb 22, 2018 3:15 pm

Re: Rope Physics Help

Post by NotARaptor »

You can adjust the number of passes to suit. Decreasing it a lot will give the rope a more springy feel. But with 50 segments and 50 passes, I could still simulate 100 ropes at 60fps, so it's not slow, and the maths could be optimised, but as a beginner in LUA I'm not sure how best to do it - in C I'd replace the square root with an approximation, for example.
User avatar
Jasoco
Inner party member
Posts: 3725
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Rope Physics Help

Post by Jasoco »

NotARaptor wrote: Fri Mar 09, 2018 2:03 pm I just threw this together quickly - looks to be pretty rope-ish to me, doesn't rely on love.physics.

screenshot.png

It doesn't do collisions or anything, but that could be added easily enough.

I added two ropes - one has zero drag, the other does have drag, so you can see the difference.

The first point of both ropes is static, the last point can be free or static (hold the spacebar to fix them, release to free them)

rope-test.love

Hope this helps!
ZIP files are case sensitive. You told it to require "Rope". But the file is named "rope". Heads up to anyone downloading this to unzip it and change that code to make it run.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 49 guests