Attaching physics body to another via "chain"

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
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Attaching physics body to another via "chain"

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

Re: Attaching physics body to another via "chain"

Post 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
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Re: Attaching physics body to another via "chain"

Post by TRI99ER »

Thank you for the answer. I will try it. Somehow I didn't notice love.physics has this functionality.
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Attaching physics body to another via "chain"

Post by darkfrei »

I really like this channel:
Coding Challenge #160: Spring Forces
https://youtu.be/Rr-5HiXquhw?t=1859
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Re: Attaching physics body to another via "chain"

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

Re: Attaching physics body to another via "chain"

Post 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
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Re: Attaching physics body to another via "chain"

Post 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.
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Re: Attaching physics body to another via "chain"

Post 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.
Last edited by TRI99ER on Wed Jun 09, 2021 10:25 am, edited 1 time in total.
TRI99ER
Prole
Posts: 13
Joined: Fri May 28, 2021 3:28 pm

Re: Attaching physics body to another via "chain"

Post by TRI99ER »

Too bad you can't commend people on this forum.
User avatar
Gunroar:Cannon()
Party member
Posts: 1085
Joined: Thu Dec 10, 2020 1:57 am

Re: Attaching physics body to another via "chain"

Post 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
The risk I took was calculated,
but man, am I bad at math.

-How to be saved and born again :huh:
Post Reply

Who is online

Users browsing this forum: No registered users and 61 guests