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

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.
Post Reply
nullset2
Prole
Posts: 2
Joined: Mon Sep 27, 2021 8:54 pm

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

Post 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?
Attachments
verlet.love
(2.83 KiB) Downloaded 45 times
User avatar
pgimeno
Party member
Posts: 2820
Joined: Sun Oct 18, 2015 2:58 pm

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

Post 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.
User avatar
pgimeno
Party member
Posts: 2820
Joined: Sun Oct 18, 2015 2:58 pm

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

Post 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
Attachments
cup_and_ball.love
(1.57 KiB) Downloaded 59 times
nullset2
Prole
Posts: 2
Joined: Mon Sep 27, 2021 8:54 pm

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

Post 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.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 39 guests