Making a chain of physics bodies

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
xordspar0
Prole
Posts: 10
Joined: Thu Nov 21, 2013 5:36 am

Making a chain of physics bodies

Post by xordspar0 »

I'm trying to make a chain with the physics module (not too different from the chains in the Box2D testbed). However, something has gone horribly wrong. After a few seconds, the segments of the chain appear disjointed and move jerkily. After a minute...chain links go flying everywhere, and most of them are off of the screen.

I'm not sure if the problem is in the part where I set up the physics bodies/fixtures or if it's how I'm drawing the bodies on the screen. I've looked at the source code from the Box2D testbed chains example, but I can't get to the bottom of it.

Here's my setup code:

Code: Select all

function love.load()
    world = love.physics.newWorld(0, 9.8)

    floor = love.physics.newBody(world, love.graphics.getWidth()/2, 300, "static")
    love.physics.newFixture(floor, love.physics.newRectangleShape(love.graphics.getWidth(), 1), 1)

    local anchorX, anchorY = love.graphics.getWidth()/2, love.graphics.getHeight()/2
    anchor = love.physics.newBody(world, anchorX, anchorY)
    segments = {}
    segmentLength = 30
    for i = 1,6 do
        table.insert(segments, makeSegment(anchorX+segmentLength*i, anchorY))
    end
    head = makeHead(anchorX+segmentLength*7, anchorY)

    love.physics.newRevoluteJoint(
        anchor, segments[1].body,
        anchorX, anchorY
    )
    for i = 1,#segments-1 do
        love.physics.newRevoluteJoint(
            segments[i].body, segments[i+1].body,
            segments[i]:rightJoint()
        )
    end
    love.physics.newRevoluteJoint(
        segments[#segments].body, head.body,
        segments[#segments]:rightJoint()
    )
end

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

function love.draw(dt)
    for _, segment in pairs(segments) do
        love.graphics.setColor(100, 100, 100)
        love.graphics.polygon('fill', segment:getPoints())
        love.graphics.setColor(180, 180, 180)
        love.graphics.polygon('line', segment:getPoints())
    end
    love.graphics.setColor(100, 100, 100)
    love.graphics.polygon('fill', head:getPoints())
    love.graphics.setColor(180, 180, 180)
    love.graphics.polygon('line', head:getPoints())

    love.graphics.setColor(255, 255, 255)
    love.graphics.print(love.timer.getFPS(), 10, 10)

    love.graphics.line(love.graphics.getWidth()/2, love.graphics.getHeight()/2,
                       segments[1].body:getX(), segments[1].body:getY())
    love.graphics.setColor(255, 0, 255)
    love.graphics.points(love.graphics.getWidth()/2, love.graphics.getHeight()/2)
end
Here's the code for generating segments (the head is the same except that it has 3 points instead of 4):

Code: Select all

function makeSegment(x, y)
    local segment = {}

    segment.length = 30
    segment.height = 10

    function segment:getPoints()
        local x, y = x, y
        local r = 0
        if self.body then
            x = self.body:getX()
            y = self.body:getY()
            r = self.body:getAngle()
        end
        local length = self.length
        local height = self.height

        local sinr = math.sin(r)
        local cosr = math.cos(r)
        return {
            x + (-length/2 * cosr) - (-height/2 * sinr), y + (-length/2 * sinr) + (-height/2 * cosr),
            x + ( length/2 * cosr) - (-height/2 * sinr), y + ( length/2 * sinr) + (-height/2 * cosr),
            x + ( length/2 * cosr) - ( height/2 * sinr), y + ( length/2 * sinr) + ( height/2 * cosr),
            x + (-length/2 * cosr) - ( height/2 * sinr), y + (-length/2 * sinr) + ( height/2 * cosr)
        }
    end

    function segment:leftJoint()
        local length = self.length
        local r = self.body:getAngle()
        local sinr = math.sin(r)
        local cosr = math.cos(r)
        return x + (-length/2 * cosr) - 0, y + (-length/2 * sinr) + 0
    end

    function segment:rightJoint()
        local length = self.length
        local r = self.body:getAngle()
        local sinr = math.sin(r)
        local cosr = math.cos(r)
        return x + ( length/2 * cosr) - 0, y + ( length/2 * sinr) + 0
    end

    local body = love.physics.newBody(world, x, y, 'dynamic')
    local fixture = love.physics.newFixture(
        body,
        love.physics.newPolygonShape(segment:getPoints())
    )
    segment.body = body

    return segment
end
Here are a couple videos showing the situations in the screenshots below.

After a few seconds:
Image

After a minute:
Image
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Making a chain of physics bodies

Post by ivan »

Hello, there are several problems with your approach.

The first problem is that you are using a variable time step. Variable time steps means that your joints will be unstable and may behave inconsistently. So make sure to update the world with a constant time step.

The second problem is the mass. You can't have a super massive object pulling down on your chain. It has to do with the physics constrains: revolute joints are not suited for this sort of thing.
So if you want your chain to be "functional" you might add a single "rope" joint from the tail to the head.
This will ensure that your chain cannot be pulled apart. In fact, if your math is good, you could use the rope joint alone: simply draw the rope as a chain of links and you'll be good to go.

Good luck!
xordspar0
Prole
Posts: 10
Joined: Thu Nov 21, 2013 5:36 am

Re: Making a chain of physics bodies

Post by xordspar0 »

Nope that wasn't it. It turned out to be that you're supposed to use local coordinates for defining shapes. I was using world coordinates.

It makes sense that you should use local coordinates even though most other love physics functions require wolrd coordinates because shapes don't actually exist in the world; they get copied when you create a fixture.
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Making a chain of physics bodies

Post by bobbyjones »

xordspar0 wrote: Sun May 13, 2018 12:01 am Nope that wasn't it. It turned out to be that you're supposed to use local coordinates for defining shapes. I was using world coordinates.

It makes sense that you should use local coordinates even though most other love physics functions require wolrd coordinates because shapes don't actually exist in the world; they get copied when you create a fixture.
Yup. Shapes are relative to the body and the body is relative to the world. The reason for this is that you can add multiple shapes to a fixture with one body.
xordspar0
Prole
Posts: 10
Joined: Thu Nov 21, 2013 5:36 am

Re: Making a chain of physics bodies

Post by xordspar0 »

Yeah, it makes sense, but it's not explicitly called out in the docs.
Post Reply

Who is online

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