Page 1 of 1

Attaching physics body to another via "chain"

Posted: Fri May 28, 2021 3:47 pm
by TRI99ER
Hello, you might guess, I am new here and new to Love2d in general.
I am interested to know, how can I replicate certain behavior, I will provide abstraction below.

There are two objects, one "big" A and another "small" B. I would like the B object to be attached to A object in a way that it always stays within a certain radius of it.
You can imagine a person with a dog on a leash, and they interact as follows: person can go whenever they want and dog will be "dragged" along with them, but on the other hand dog can't drag the person anywhere. So a one way interaction.
But I still want both bodies to be of "dynamic" type.

I actually tried doing it in a way, that I update position of B object to stay within certain radius of the A object, not based on physics, just on vector math and if statements. However, I found that it doesn't work quite right and is not appropriate. You need to split code to different functions and do the same stuff and a lot of checks in different places, also it disregards any physics and works against code that sets movement through linear velocity. I don't think this is a viable approach, even if I make it work.

I was looking for a way to create the desired behavior using physics, but I couldn't find any relevant posts at all.
I thought of doing a hollow circular shape, that would trace the circle of desired radius, but again I think I overcomplicate it and that would add a lot of complexity, that I believe is not necessary.

Maybe I missed some way to replicate attaching objects together this way, but I can't find it. Perhaps someone can hint me in right direction. Any help would be appreciated.

Re: Attaching physics body to another via "chain"

Posted: Sat May 29, 2021 12:20 pm
by pgimeno
You can try assigning the dog a very small mass in relation to the person, and attach them with a rope joint. It will still drag the person, but hopefully the effect will be small enough to be good. As for the limitation of distance, a RopeJoint does just that.

Example (save as main.lua and use A, D, Space to control):

Code: Select all

-- Object parameters
local personX, personY = 0, 0
local dogX, dogY = -30, 0
local floorX, floorY = 0, -50
local ropeLen = 80

-- Movement parameters
local forceOfKeys = 500
local dragCoef = -2
local jumpImpulse = 300


love.physics.setMeter(20)
local world = love.physics.newWorld(0, 20*-9.8)
local bodyPerson = love.physics.newBody(world, personX, personY, "dynamic")
bodyPerson:setFixedRotation(true)
local shapePerson = love.physics.newRectangleShape(21, 41)
local fixturePerson = love.physics.newFixture(bodyPerson, shapePerson)
fixturePerson:setCategory(2) -- Persons are category 2

local bodyDog = love.physics.newBody(world, dogX, dogY, "dynamic")
bodyDog:setFixedRotation(true)
bodyDog:setMass(0.01)  -- <-- This!!!
local shapeDog = love.physics.newRectangleShape(13, 9)
local fixtureDog = love.physics.newFixture(bodyDog, shapeDog)
fixtureDog:setCategory(3) -- Dogs are category 3
fixtureDog:setMask(2) -- Dogs don't collide with Persons

local bodyFloor = love.physics.newBody(world, floorX, floorY, "static")
local shapeFloor = love.physics.newRectangleShape(601, 31)
local fixtureFloor = love.physics.newFixture(bodyFloor, shapeFloor)

local joint = love.physics.newRopeJoint(bodyPerson, bodyDog, personX, personY, dogX, dogY, ropeLen)

local pressed = love.keyboard.isScancodeDown
local pressedSpace = false

function love.update(dt)
  if pressed("a") then
    bodyPerson:applyForce(-forceOfKeys, 0)
  end
  if pressed("d") then
    bodyPerson:applyForce(forceOfKeys, 0)
  end
  -- Apply drag, to limit velocity
  local velX, velY = bodyPerson:getLinearVelocity()
  bodyPerson:applyForce(velX * dragCoef, velY * dragCoef)

  -- Jump
  local previousSpace = pressedSpace
  pressedSpace = pressed("space")
  if pressedSpace and not previousSpace then
    bodyPerson:applyLinearImpulse(0, jumpImpulse)
  end

  -- Update
  world:update(dt < 0.1 and dt or 0.1)
end

function love.draw()
  love.graphics.translate(400, 300)
  love.graphics.scale(1, -1)
  love.graphics.polygon("line", bodyPerson:getWorldPoints(shapePerson:getPoints()))
  love.graphics.polygon("line", bodyDog:getWorldPoints(shapeDog:getPoints()))
  love.graphics.polygon("line", bodyFloor:getWorldPoints(shapeFloor:getPoints()))
  love.graphics.line(joint:getAnchors())
end

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

Re: Attaching physics body to another via "chain"

Posted: Mon Jun 07, 2021 7:21 am
by TRI99ER
Thank you for the answer. I will try it. Somehow I didn't notice love.physics has this functionality.

Re: Attaching physics body to another via "chain"

Posted: Mon Jun 07, 2021 8:34 am
by darkfrei
I really like this channel:
Coding Challenge #160: Spring Forces
https://youtu.be/Rr-5HiXquhw?t=1859

Re: Attaching physics body to another via "chain"

Posted: Mon Jun 07, 2021 10:06 am
by TRI99ER
There's another thing. I got it to work like you specified. Thank you for that. But I would like to make it so player controls the "dog" and that player is unable to drag the "person" after themselves. I was able to make it work with making "person" kinematic or static object, but that is not the point, as "person" needs to preferably be a dynamic object. In this case changing mass doesn't affect anything either.
Is it at all possible, or do I need to change "person" to kinematic type just to make it work? "Person" needs to collide with static and kinematic objects in any case, so I don't know what to do if I need to make it kinematic.

Re: Attaching physics body to another via "chain"

Posted: Mon Jun 07, 2021 11:51 am
by pgimeno
Real physics work that way, and Box2D is a real physics engine. At best you can try to make the mass of the dog as negligible as you can with respect to the person, either by decreasing the mass of the dog, or by increasing the mass of the person; otherwise you'll have to apply your own physics.

In the example above, when I print the mass of the dog after a world:update(), I actually get 0.2925; it seems that Box2D has a lower limit. But increasing the mass of the person works, up to the point where the dog is no longer able to beat the friction with the floor of the person. With standard physics, that's the best you can do: to make the mass of the dog as negligible as possible with respect to the person. Of course you have to adjust forces accordingly: if you apply the same force that you used to move the person to the dog, the dog may still be able to drag the person. Since it's much lighter, the force applied to it needs to be reduced proportionally.

Revised example:

Code: Select all

-- Object parameters
local personX, personY = 0, 0
local dogX, dogY = -30, 0
local floorX, floorY = 0, -50
local ropeLen = 80

-- Movement parameters
local forceOfKeys = 50
local dragCoef = -2
local jumpImpulse = 30


love.physics.setMeter(20)
local world = love.physics.newWorld(0, 20*-9.8)
local bodyPerson = love.physics.newBody(world, personX, personY, "dynamic")
bodyPerson:setFixedRotation(true)
local shapePerson = love.physics.newRectangleShape(21, 41)
local fixturePerson = love.physics.newFixture(bodyPerson, shapePerson)
fixturePerson:setCategory(2) -- Persons are category 2
bodyPerson:setMass(100)  -- <-- This!!!

local bodyDog = love.physics.newBody(world, dogX, dogY, "dynamic")
bodyDog:setFixedRotation(true)
bodyDog:setMass(0.2925)
local shapeDog = love.physics.newRectangleShape(13, 9)
local fixtureDog = love.physics.newFixture(bodyDog, shapeDog)
fixtureDog:setCategory(3) -- Dogs are category 3
fixtureDog:setMask(2) -- Dogs don't collide with Persons

local bodyFloor = love.physics.newBody(world, floorX, floorY, "static")
local shapeFloor = love.physics.newRectangleShape(601, 31)
local fixtureFloor = love.physics.newFixture(bodyFloor, shapeFloor)

local joint = love.physics.newRopeJoint(bodyPerson, bodyDog, personX, personY, dogX, dogY, ropeLen)

local pressed = love.keyboard.isScancodeDown
local pressedSpace = false

function love.update(dt)
  if pressed("a") then
    bodyDog:applyForce(-forceOfKeys, 0)
  end
  if pressed("d") then
    bodyDog:applyForce(forceOfKeys, 0)
  end
  -- Apply drag, to limit velocity
  local velX, velY = bodyPerson:getLinearVelocity()
  bodyDog:applyForce(velX * dragCoef, velY * dragCoef)

  -- Jump
  local previousSpace = pressedSpace
  pressedSpace = pressed("space")
  if pressedSpace and not previousSpace then
    bodyDog:applyLinearImpulse(0, jumpImpulse)
  end

  -- Update
  world:update(dt < 0.1 and dt or 0.1)
end

function love.draw()
  love.graphics.translate(400, 300)
  love.graphics.scale(1, -1)
  love.graphics.polygon("line", bodyPerson:getWorldPoints(shapePerson:getPoints()))
  love.graphics.polygon("line", bodyDog:getWorldPoints(shapeDog:getPoints()))
  love.graphics.polygon("line", bodyFloor:getWorldPoints(shapeFloor:getPoints()))
  love.graphics.line(joint:getAnchors())
end

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

Re: Attaching physics body to another via "chain"

Posted: Wed Jun 09, 2021 9:47 am
by TRI99ER
I actually already tried that. I set person's mass to be 100000 and dog's mass to be 0.0001, just to test if it would make a difference. It did not. Even with absurd difference like that. Also I printed out the mass and it was correct.

Console log for print of getMass of both bodies:

Code: Select all

9.9999997473788e-005
100000
Also I tried setting force to be miniscule and it still was able to drag the heavy person without even getting slowed down. That's why I said that mass won't change anything.

It seems that somehow rope joint just sets the speed of the body that drags to the one that is at rest.

Re: Attaching physics body to another via "chain"

Posted: Wed Jun 09, 2021 10:21 am
by TRI99ER
Ok I was able to replicate it. Your example obviously worked. So basically the point was to apply drag based off of persons velocity, not dogs velocity. Thank you.

I also applied damping to the person.

Re: Attaching physics body to another via "chain"

Posted: Wed Jun 09, 2021 10:24 am
by TRI99ER
Too bad you can't commend people on this forum.

Re: Attaching physics body to another via "chain"

Posted: Wed Jun 09, 2021 11:45 am
by Gunroar:Cannon()
TRI99ER wrote: Wed Jun 09, 2021 10:24 am Too bad you can't commend people on this forum.
You could before with something called karma , I think. But it's like they said to put it back would break the site after they updated it so it was just left as is. :P