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
- fast to calculate
- constrained in length, at least visibly (i.e. doesn't look like a rubber band)
- somewhat realistic-looking
- without love.physics