Page 1 of 1

[Verlet Integration] Cannot add collision in Rope and Ball situation.

Posted: Mon Sep 27, 2021 9:01 pm
by nullset2
Hi! I've been playing around with the Verlet integration API and I have hit a snag.

Based off of the Fantastic Verlet example from this thread, I have setup a rope and ball with cup simulator.

Based on the positions of the first and last nodes, I have set up Love to create a body and a shape corresponding to the cup and ball, accordingly. My understanding is that this talks to the Love2D API and should automatically handle collision, like in the physics tutorial https://love2d.org/wiki/Tutorial:Physics

However, I haven't been able to make it so this registers collisions. I am very certain I am just missing just one little detail. What am I missing?

Re: [Verlet Integration] Cannot add collision in Rope and Ball situation.

Posted: Tue Sep 28, 2021 1:38 am
by pgimeno
There are several issues here.

First, love.physics.newPolygonShape has a limit of 8 points, and the polygons must be convex. Yours has more points and isn't convex. You can solve that by using four rectangles and four fixtures for the cup's body.

Second, you made a typo: you used self.objects.ball.body:setY when you should be using self.objects.cup.body:setY (i.e. you used ball instead of cup for the Y coordinate of the cup).

Third, you don't use anything from the love.physics world. You add some objects and move them but never query where they are or anything. You could as well not create the physical world and nothing would change.

Fourth, even if all that were fixed, you can't expect love.physics to react to the collisions if you don't let it move things according to physics. If you want proper collision reaction, you need to let the love.physics world do the movement.

In the thread you've linked, I wanted to avoid love.physics, but since that doesn't seem to be your case, note that you can make a rubber string (or a rigid-ish string) with love.physics.

I can't write an example right now and I'll be busy for about 24 hours, but I'll try to write a PoC as soon as I can.

Re: [Verlet Integration] Cannot add collision in Rope and Ball situation.

Posted: Wed Sep 29, 2021 12:43 am
by pgimeno
And here it is:

Code: Select all

local metre = 64
local lp = love.physics
local lg = love.graphics

local nSegments = 20
local initialCupX = 400
local initialCupY = 0
local initialBallX = initialCupX
local initialBallY = 500

local function lerp(a, b, t)
  return t < 0.5 and a + (b - a) * t or b + (a - b) * (1 - t)
end

local objs = {}

lp.setMeter(metre)
local world = lp.newWorld(0, 9.81*metre, true)

objs.cup = {}
objs.cup.body = lp.newBody(world, initialCupX, initialCupY, "dynamic")
objs.cup.body:setFixedRotation(true) -- don't rotate the cup
-- The position of the rectangle is the centre, not the top left, so we add
-- half the width/height to the top left of each rectangle to get the centre
objs.cup.shapes = {}
objs.cup.shapes[1] = lp.newRectangleShape(-60+20/2, -60+40/2,   20, 40)
objs.cup.shapes[2] = lp.newRectangleShape( 40+20/2, -60+40/2,   20, 40)
objs.cup.shapes[3] = lp.newRectangleShape(-60+120/2, -20+20/2,  120, 20)
objs.cup.shapes[4] = lp.newRectangleShape(-10+20/2,   0+60/2,   20, 60)

objs.cup.fixtures = {}
for i = 1, #objs.cup.shapes do
  objs.cup.fixtures[i] = lp.newFixture(objs.cup.body, objs.cup.shapes[i])
  -- Don't bounce too much.
  objs.cup.fixtures[i]:setRestitution(0.5)
end

-- Add a mouse joint for the cup.
objs.cup.joint = lp.newMouseJoint(objs.cup.body, objs.cup.body:getPosition())

objs.ball = {}
objs.ball.body = lp.newBody(world, initialBallX, initialBallY, "dynamic")
objs.ball.shape = lp.newCircleShape(25)
objs.ball.fixture = lp.newFixture(objs.ball.body, objs.ball.shape)

objs.string = {}
-- Make the string from the bottom of the cup to the ball
local prevX, prevY = initialCupX, initialCupY+60
for i = 1, nSegments-1 do
  local segment = {}
  local x, y = lerp(initialCupX, initialBallX, i/nSegments),
               lerp(initialCupY+60, initialBallY, i/nSegments)
  segment.body = lp.newBody(world, x, y, "dynamic")
  -- A distance joint has some spring effect already; you can play with
  -- DistanceJoint:setFrequency to make it more elastic.
  segment.joint = lp.newDistanceJoint(
    i == 1 and objs.cup.body or objs.string[i-1].body, segment.body,
    prevX, prevY, x, y
  )
  -- This causes issues, not sure why.
--  segment.shape = lp.newRectangleShape(1, 1)
--  segment.fixture = lp.newFixture(segment.body, segment.shape)

  objs.string[i] = segment
  prevX, prevY = x, y
end

do
  -- Final segment goes from the last body to the ball
  local x, y = objs.ball.body:getPosition()
  objs.ball.joint = lp.newDistanceJoint(
    objs.string[nSegments-1].body, objs.ball.body, prevX, prevY, x, y
  )
end

function love.keypressed(k) return k == "escape" and love.event.quit() end


-- Avoid jerks before the mouse is moved for the first time
-- (at the cost of stealing the mouse once)
love.mouse.setPosition(0, 0)
love.event.pump()
love.mouse.setPosition(400, 300)
objs.cup.body:setPosition(400.5, 300.5)

function love.update(dt)
  objs.cup.joint:setTarget(love.mouse.getPosition())
  world:update(dt)
end

function love.draw()
  for i = 1, #objs.cup.shapes do
    lg.polygon("line", objs.cup.body:getWorldPoints(objs.cup.shapes[i]:getPoints()))
  end
  lg.circle("line", objs.ball.body:getX(), objs.ball.body:getY(), objs.ball.shape:getRadius())
  for i = 0, nSegments - 1 do
    local x1, y1, x2, y2
    if i == 0 then
      x1, y1 = objs.cup.body:getPosition()
      y1 = y1 + 60
    else
      x1, y1 = objs.string[i].body:getPosition()
    end
    if i == nSegments - 1 then
      x2, y2 = objs.ball.body:getPosition()
    else
      x2, y2 = objs.string[i+1].body:getPosition()
    end
    lg.line(x1, y1, x2, y2)
  end
end

Re: [Verlet Integration] Cannot add collision in Rope and Ball situation.

Posted: Sun Oct 10, 2021 8:56 pm
by nullset2
This is fantastic by the way. I would had liked to use the exact same physics I got from the verlet integration but this approach works, will just tinker some of the parameters to get the right kind of "feel" to it and finish building my game.

Thank you so much, your advice is truly appreciated! You rule.