body:getposition with multiple shapes

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.
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

body:getposition with multiple shapes

Post by zell2002 »

Ive had some issues with rectangle shaped character "sticking" to floors where 2 boxes meet.
I read about trying a circle for feet, and this worked well - my character now glides over those bits.

I wanted to use a rectangle and circle to create a kind of "capsule" shape. However, when I do this, I get odd shape positions and body:getposition always returns whereever the circle shape was placed. I thought maybe body:getposition would be relative to the first shape, but it isnt - or doesn't appear to be.

(Green line is where I'd expect getposition() to return)
(Red line is where getposition() Is actually returning)

Examples:
Original character, just rectangle. Correct position - as I've been coding past couple of years.
Image

So this is creating a body at object.x, object.y then adding rectangle shape at 0,0 offset.


New capsule:
Image

So this again is creating the body at object.x, object.y, then creating circle shape at (0,0) and rectangle shape (0, -offsetY).

I thought I'd try changing the order of the shapes, but the getposition() is still the centre of the cirlce. So I thought maybe which ever shape is at 0,0 is the most important.

I messed around with shape create order and which one I offset, but kept getting this:
Image

Circle first, with the offsetY, then rectangle 0,0.
Circle first with the offsetY doubled, then rectangle at 0,0.
Tried setting the Circle's offset on the X axis - didn't change anything its position at all.

If I then set Cirlce first to 0,0 then set the rectangle with a Y offset the rectangle lands on the floor, and the circle is positioned relative to the rectangle.
circle 0,0, rectangle with negative Y offset:
Image

circle 0,0, rectangle with positive Y offset:
Image

I don't really understand what's going on - why does getposition always seem to return the circle's origin? Again this happens regardless the ordering of the shape creation.

I hope I've explained this well enough - I assume I'm being dumb tbh...

When using multiple shapes, should I instead be using body:getWorldCenter instead of body:getposition ?
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: body:getposition with multiple shapes

Post by pgimeno »

Works fine for me:

Code: Select all

local lp, lg = love.physics, love.graphics

lp.setMeter(10)
local world = lp.newWorld(0, 10, false)
local floorB = lp.newBody(world, 0, 80)
local floorS = lp.newRectangleShape(0, 10, 301, 21)
local floorF = lp.newFixture(floorB, floorS)

local playerB = lp.newBody(world, 0, 0, "dynamic")
local playerS1 = lp.newRectangleShape(0, 0, 31, 71)
local playerF1 = lp.newFixture(playerB, playerS1)
local playerS2 = lp.newCircleShape(0, 35, 15.5)
local playerF2 = lp.newFixture(playerB, playerS2)

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

function love.draw()
  lg.translate(lg.getWidth()/2, lg.getHeight()/2)
  lg.polygon("line", floorB:getWorldPoints(floorS:getPoints()))
  lg.polygon("line", playerB:getWorldPoints(playerS1:getPoints()))
  do
    local x, y = playerB:getWorldPoints(playerS2:getPoint())
    lg.circle("line", x, y, playerS2:getRadius())
  end

  -- Draw cross on body position
  do
    local x, y = playerB:getPosition()
    lg.rectangle("fill", x - 3, y, 7, 1)
    lg.rectangle("fill", x, y - 3, 1, 7)
  end
end

function love.keypressed(k) return k == "escape" and love.event.quit() end
Can you show a runnable example?

EDIT: I think I get what you want. I don't know exactly why, but the origin of a rectangular PolygonShape, i.e. its (0, 0), depends on the dimensions of the rectangle. It probably is relative to the centroid of the polygon, but I haven't checked to be sure. If you specify (0, 0), that means the centre of the rectangle matches the position of the body, therefore the (0, 0) of the shape is displaced (-width/2, -height/2) with respect to the origin of the body.

It seems to me, after rereading your post, that you want/expect the body position to be at the centre of the bounding box of your shape. I may have misunderstood, though, so please correct me if I'm wrong.

If that's the case, you need some calculations to position both the rectangle and the circle. Given a total playerWidth and playerHeight:

Code: Select all

  -- Circle diameter is the full width, therefore the radius is half that.
  local playerCircleRadius = playerWidth / 2
  -- The total height includes the rectangle height plus the circle radius.
  -- Calculate the rectangle height based on that.
  local playerRectHeight = playerHeight - playerCircleRadius
  -- Origin is at the centre of the rectangle. Move rectangle up by half the circle radius. (*)
  local playerRectY = -playerCircleRadius / 2
  -- Centre of circle is at the bottom of the rectangle. Calculate that as centre of rectangle
  -- plus distance from rectangle centre to rectangle bottom.
  local playerCircleY = playerRectY + playerRectHeight / 2
  -- Create the shapes with the calculated values.
  local playerRectShape = love.physics.newRectangleShape(0, playerRectY, playerWidth, playerRectHeight)
  local playerCircleShape = love.physics.newCircleShape(0, playerCircleY, playerCircleRadius)
The step marked (*) may require a bit of explanation. If you had the rectangle centred in the body, i.e. at (0, 0), and the circle at the bottom of the rectangle, then the distance from centre to top would be half the height of the rectangle, and the distance from centre to bottom would be half the height of the rectangle, plus the radius of the circle. To centre them, both must move up by half the radius of the circle. That way, the distance from centre to top is the same as the distance from centre to bottom.
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

Re: body:getposition with multiple shapes

Post by zell2002 »

Yeah this is exactly what I found - and your assumption of what I was expecting is correct.
Thanks for the code example, I fully get what youre suggesting here and it sounds exactly like what I need to do. Will have a dabble later with my code.

Cheers!

It is a bit odd right? The positioning of the shapes
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: body:getposition with multiple shapes

Post by pgimeno »

zell2002 wrote: Sun Sep 08, 2019 5:59 pmIt is a bit odd right? The positioning of the shapes
Yes, and I haven't found anything about it in the documentation.
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

Re: body:getposition with multiple shapes

Post by zell2002 »

pgimeno wrote: Sun Sep 08, 2019 9:51 pm
zell2002 wrote: Sun Sep 08, 2019 5:59 pmIt is a bit odd right? The positioning of the shapes
Yes, and I haven't found anything about it in the documentation.
I'm so glad it's not just me, thought I was going mental. Was even harder to google about - tried looking through some box2d stuff as well but with no luck. Not going to lie, wasn't too sure what it was I was searching for.

Wonder if there is anyone on here who could explain it specifically
Ross
Citizen
Posts: 97
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: body:getposition with multiple shapes

Post by Ross »

I think it would be more strange if the body's position changed as you added fixtures to it, or if the order that you added them in mattered. What if you did that at runtime? All of a sudden all your calculations could be broken. The way it is now, it only does exactly what you tell it to.

Maybe you're thinking of the center of mass? (but this isn't the same as the bounding box center either). You can use Body:getWorldCenter to get the Body's center of mass (in world coordinates, use Body:getLocalCenter to get it in local coords).

Each Body has a position. You set this when you create the Body. You also get and set it with Body:getPosition and Body:setPosition, and of course for dynamic bodies it moves with the simulation (gravity, etc.).

When you create a circle or rectangle Shape, you also set a position (or it's assumed to be 0, 0). This is the Local position of the shape—its offset from the Body position. At the top of the doc for love.physics.newRectangleShape it says: "By default, the local origin is located at the center of the rectangle as opposed to the top left for graphics. " I guess it's personal preference, but for me it's much more convenient this way. So any circle or rectangle Shape that you place at 0, 0, will be centered on the Body position.

All this lets you control the origin/pivot of your Body. If you're making a platformer character, maybe you care the most about the position of the character's "feet", the bottom of the body. You can do this:

Code: Select all

body = love.physics.newBody(world, -30, -10, "dynamic")

local radius = 20
local rectWidth = radius * 2
local rectHeight = 100

local shape1 = love.physics.newCircleShape(0, -radius, radius)
love.physics.newFixture(body, shape1)

local shape2 = love.physics.newRectangleShape(0, -radius - rectHeight/2, rectWidth,, rectHeight)
love.physics.newFixture(body, shape2)
. . . and get this:
Image

If you set the angle of the body, it rotates around the Body position.
Image

[Edit] Oh, and for static and kinematic bodies, it doesn't bother to calculate the center of mass, it stays the same as the Body position.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: body:getposition with multiple shapes

Post by pgimeno »

Thanks, Ross, I missed that bit in the docs of newRectangleShape.

Now I have tested the behaviour of newPolygonShape, and it uses body local coordinates just as expected. So this behaviour is pretty much unique to newRectangleShape's 4+1 parameter version.

I agree it's convenient that the default makes the rectangle centred in the body, but I don't agree that it's convenient that the four/five parameter version uses an offset with respect to this default, for several reasons: because it introduces inconsistencies in the API, because the origin is variable and different from the newPolygonShape values, because it produces confusing results (as evident in this thread and as happened in another recent one), and because it's harder to handle that way as evidenced in this thread.

My understanding is that zell2002 started with a plain WxH rectangle for the player, and when he noticed issues when hitting seams (a frequent problem), he just tried to make the base rounded without altering the rest of the program. This meant the centre had to stay in the same place and everything else needed to be basically the same, so that the rest of his code would not need any alteration; but when trying to do this, he ran into issues (@zell2002, please correct me if I'm wrong). I don't know how he performed the rest of experiments, to obtain these strange results, but I don't think that matters any longer.

What I didn't think about when I explained a solution, however, is that the new shape distribution would make the centre of mass change position and the total mass change. That can be fixed by varying the density of the circle. I haven't checked, but if my math is right, it would be something like:

Code: Select all

  -- Let W,H be the dimensions of the bounding box, h=H-W/2 the height of the rectangle.
  -- Let R be the y position of the centre of mass, d1 the density of the rectangle, d2 the density of the circle.
  -- In Box2D, mass equals area times density (not sure if it also depends on the setMeter value but that doesn't matter here).
  -- The centre of mass satisfies:
  -- R = (m1 * r1 + m2 * r2) / (m1 + m2)
  -- We want R = 0 in local coordinates, therefore we need to solve m1 * r1 + m2 * r2 = 0, where:
  -- m1 = mass of rectangle = W * h * d1
  -- r1 = vertical position of rectangle's CoM = -W/4
  -- m2 = mass of circle = math.pi * (W/2)^2 * d2
  -- r2 = vertical position of circle's CoM = -W/4 + h/2
  -- Solving for d2 gives: d2 = d1 * (4 * h) / (math.pi * (2 * h - W))
  fixtureCircle:setDensity(fixtureRectangle:getDensity() * rectangleHeight * 4 / (math.pi * (2 * rectangleHeight - Width)))
It's probably irrelevant if the body can't rotate, though, unless he's using Body:getLocalCenter for something, or he depends on the exact mass. [EDIT: My bad. The mass is still not the same.]
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

Re: body:getposition with multiple shapes

Post by zell2002 »

pgimeno wrote: Tue Sep 10, 2019 3:21 pm My understanding is that zell2002 started with a plain WxH rectangle for the player, and when he noticed issues when hitting seams (a frequent problem), he just tried to make the base rounded without altering the rest of the program. This meant the centre had to stay in the same place and everything else needed to be basically the same, so that the rest of his code would not need any alteration; but when trying to do this, he ran into issues (@zell2002, please correct me if I'm wrong). I don't know how he performed the rest of experiments, to obtain these strange results, but I don't think that matters any longer.
@ross
"The way it is now, it only does exactly what you tell it to."
I would say, it is ignoring the offset params passed to newCircleShape().

Sorry for late reply! I struggle to locate my previous posts/comments in this forum.
You are 100% correct, and I was about to post this issue again but with a specifc demo.love to show the issue.

So this is a barebones demo - a world with a static box, then if you press spacebar it will create a "player" this player will be a rectangle with 2 extra shapes added to the same body. One shape is another rectangle and its offset is taken into account, the other is a circle and it doesnt matter what offsets you give it, they are not used at all. Even changing the ordering of when the shapes are made do not change where the circle is built. It is always built at 0,0 of the body.
If you offset the main body rectangle, however, the circle suddenly "drops" to the bottom - again, ignoring its offsets, while the other recantgle obeys its offsets.

@pgimeno , Back to your guess about my situation, Im just trying to build a capsule shape for my player character. I feel circle + rectangle + circle, is a fair way of doing it?

Perhaps I've misunderstood something, but I would say this is wrong. newCircleShape behaves differently to newRectangleShape
Attachments
test-bug.love
(4.11 KiB) Downloaded 138 times
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

Re: body:getposition with multiple shapes

Post by zell2002 »

Ross wrote: Tue Sep 10, 2019 1:20 pm I think it would be more strange if the body's position changed as you added fixtures to it, or if the order that you added them in mattered. What if you did that at runtime? All of a sudden all your calculations could be broken. The way it is now, it only does exactly what you tell it to.

Maybe you're thinking of the center of mass? (but this isn't the same as the bounding box center either). You can use Body:getWorldCenter to get the Body's center of mass (in world coordinates, use Body:getLocalCenter to get it in local coords).

Each Body has a position. You set this when you create the Body. You also get and set it with Body:getPosition and Body:setPosition, and of course for dynamic bodies it moves with the simulation (gravity, etc.).

When you create a circle or rectangle Shape, you also set a position (or it's assumed to be 0, 0). This is the Local position of the shape—its offset from the Body position. At the top of the doc for love.physics.newRectangleShape it says: "By default, the local origin is located at the center of the rectangle as opposed to the top left for graphics. " I guess it's personal preference, but for me it's much more convenient this way. So any circle or rectangle Shape that you place at 0, 0, will be centered on the Body position.

All this lets you control the origin/pivot of your Body. If you're making a platformer character, maybe you care the most about the position of the character's "feet", the bottom of the body. You can do this:

Code: Select all

body = love.physics.newBody(world, -30, -10, "dynamic")

local radius = 20
local rectWidth = radius * 2
local rectHeight = 100

local shape1 = love.physics.newCircleShape(0, -radius, radius)
love.physics.newFixture(body, shape1)

local shape2 = love.physics.newRectangleShape(0, -radius - rectHeight/2, rectWidth,, rectHeight)
love.physics.newFixture(body, shape2)
. . . and get this:
Image

If you set the angle of the body, it rotates around the Body position.
Image

[Edit] Oh, and for static and kinematic bodies, it doesn't bother to calculate the center of mass, it stays the same as the Body position.
Cheers for this example Ross!
I dont know if you want to check my demo.love and see what you think about moving the circleshape around
zell2002
Citizen
Posts: 75
Joined: Sun Feb 23, 2014 9:22 pm

Re: body:getposition with multiple shapes

Post by zell2002 »

Hey guys, ive added another demo.

So I build 4 shapes:

Code: Select all

  
  local shape = love.physics.newRectangleShape(0, -width, width, height)
  local fix = love.physics.newFixture(player, shape, 1) 

  local shape = love.physics.newRectangleShape(8, -height, width/2, height/4)
  local fix = love.physics.newFixture(player, shape, 1) 
  
  local shape = love.physics.newCircleShape(0, 0, width/2)
  local fix = love.physics.newFixture(player, shape, 1) 

  local shape = love.physics.newCircleShape(0, height * 2, width/2)
  local fix = love.physics.newFixture(player, shape, 1) 
If you change shape1's Y offset from -width to 0 you will see the 2nd shape's Y offset (-height) puts it into a different place. Which doesnt make sense if the offset is meant to be relative to the body position, not to each other. If the shape's offsets were from the body position, then it should build in the same position each time ?
I assume i've misunderstood something...?

And as for the 2nd circle, I cant get the circle to appear at the "head" of the rectangle, but I can at the bottom (1st circle). Only if I tell the rectangle to move up out of the way.
If you comment out the 2nd circle, then set its Y offset to something like height*3 or -width, it is at the same position :S

I hope this has all made sense!
Cheers for any further feedback guys
Attachments
test-bug.love
(4.14 KiB) Downloaded 126 times
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 37 guests