2
\$\begingroup\$

I try to rotate a static body of a PolygonShape, from another origin, and set the position regarding this new origin.

Why ? Because I create bodies from TiledMapEditor object. And origins of objects in this editor are in the left upper corner.

Regarding the documentation, I can set origin using the setAsBox method. But then when I set the position, the body is not where it is supposed to be. (an offset is apply between the current position and the expected position)

I assume that the origin is used for the rotation, but not the position.

How can I set the position from another origin, and rotating from this same origin ? (the upper left corner and not the center of the shape)

My attempt :

  val rotation = 45f // rotation angle (in degree)
  val shape = PolygonShape()
  shape.setAsBox(rectangle.width*0.5f, // half width
                rectangle.height*0.5f, // half height
                Vector2(0f, rectangle.height), // new origin
                rotation * MathUtils.degreesToRadians) // angle to radians

   val bodyDef = BodyDef()
   bodyDef.type = BodyDef.BodyType.StaticBody
   bodyDef.position.set(rectangle.x, rectangle.y) // set the position

Regards

\$\endgroup\$
1
  • \$\begingroup\$ I'm not sure if static bodies are intended to be rotated, you may try dynamic bodies instead. Before I can be sure what the solution is, I need to see how your graphics software is interacting with box2d. You should move a collidable object towards your static body and see if they over lap to ensure it's not a drawing problem. But typically, when I need to rotate I use body.tranform. (x) Mind you there is also an issue with radians vs degrees, you may need to use deg-to-rad. \$\endgroup\$ Nov 24, 2016 at 19:01

2 Answers 2

1
\$\begingroup\$

Regarding the documentation, I can set origin using the setAsBox method. But then when I set the position, the body is not where it is supposed to be. (an offset is apply between the current position and the expected position)

Seems like there's some confusion going on about what does what.

I assume that the origin is used for the rotation, but not the position.

To quote a line from a movie I love: "get specific Bob"!

How can I set the position from another origin, and rotating from this same origin ? (the upper left corner and not the center of the shape)

Allow me to explain...

In Box2D 2.3.2, there is a notion of world coordinates and there is the notion of local coordinates.

World coordinates are just that. They're 2-D cartesian coordinate locations within an instance of a Box2D world. As cartesian coordinates, they have an origin. In Box2D the world origin is at location (0, 0). In a sense, this origin can be shifted by calling the b2World::ShiftOrigin(const b2Vec2& newOrigin) C++ method. But in another sense, that's just moving everything within the world over by the newOrigin amount and the origin is essentially still and always at (0, 0). For the sake of this answer though, lets agree to say that the world origin is at (0, 0) in the 2-D world space.

Local coordinates meanwhile, are coordinates that are relative to a Box2D body's location. Specifically they're relative to the location returned by the b2Body::GetPosition() C++ method (which is given by the positional portion of the body's transformation). Thus the origin of local coordinates is effectively the world location of the body the local coordinates are relative to. Let's then agree to call this origin the local origin of that body, say that it's values are also (0, 0), and recognize that this local origin is relative to the world location of the body.

With these understandings and definitions, recognize that the third and fourth parameters to shape.setAsBox are relative to the transformation of the body (the body's combined translation and rotation matrices) that the shape is associated with. In the Box2D 2.3.2 C++ sources for the polygon shape you're using, is the following documentation that goes with this method:

/// Build vertices to represent an oriented box.
/// @param hx the half-width.
/// @param hy the half-height.
/// @param center the center of the box in local coordinates.
/// @param angle the rotation of the box in local coordinates.
void SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle);

In your code example that reads as:

shape.setAsBox(rectangle.width*0.5f, // half width
               rectangle.height*0.5f, // half height
               Vector2(0f, rectangle.height), // new origin
               rotation * MathUtils.degreesToRadians) // angle to radians

Here you are configuring a rectangle that's centered at local coordinates of (0, rectangle.height) and rotated about this center by rotation radians. And this is all relative to the transformation of the associated body.

So now let's move on to a later piece of code you provided:

bodyDef.position.set(rectangle.x, rectangle.y) // set the position

Now you're setting the world location of the body to be at (rectangle.x, rectangle.y). This means that the rectangular polygon shape that you set up is - in terms of world coordinates - now centered at (rectangle.x, rectangle.y + rectangle.height). It also means, that if the body's angular orientation is changed, the rectangle will be both translated and rotated since the rectangle is vertically offset from the center of the body. You can think of the rectangle as being like the metal head of a hammer that's swung about the end of its handle now.

It sounds however like what you want is to have the rectangle centered on the local origin of the body but you also want the rectangle initially angularly oriented by 45 degrees counter-clockwise from the local X axis. Taking care of the center should then just be a matter of calling shape.setAsBox with the third parameter - the "center" - set to Vector2(0f, 0f). As to the angular orientation then, you have two choices:

  1. Set the angular orientation of the rectangle via the fourth parameter to shape.setAsBox (like you're doing); or
  2. instead set the angular orientation of the body via the angle parameter of your bodyDef structure.

Personally, I prefer choice #2 for bodies that are only going to have a single associated shape. That way when the body has an angular position of 0 radians, everything is level like I'd expect it to be.

So lets revisit the three parts I quoted from your question:

  1. The body is where you told it to be and the shape associated with that body is where you told it to be as well. The shape however isn't where you expected it to be presumably because the interfaces you're using didn't make it clear enough for you to use them correctly. While I'm not the original author of Box2D nor the author of the port of Box2D that you're using, I can empathize with any frustration anyone has with unknowingly misusing an API. I agree with Scott Meyer's assertion of "Make interfaces easy to use correctly and hard to use incorrectly". I'm not sure how this API could be changed however to make it harder to use incorrectly insofar as body and shape positioning is concerned.
  2. The local origin is used for local locations or local angles except in this case where the local center is changed making it effectively the new local origin as far as the local angle is concerned.
  3. The world coordinate location of a shape can be set by either setting its body's location or by setting the center of the rectangle to be offset from the body's location enough so that in sum the location of the rectangle is where you want it. Regarding "rotating" the shape, it can be rotated about its center using the fourth parameter of shape.setAsBox or if it's center is (0, 0) then it can also be rotated about its center by rotating the body's angular position.

Lastly, people have suggested that static bodies can't be rotated or moved. That's not necessarily correct. That depends on what people expect from this rotation or movement. There's nothing in the Box2D 2.3.2 implementation of the b2Body::SetTransform method for instance that will reject changing a body's transform based on it being a static body (nor other type). The body's linear and/or angular position can be changed regardless of what type of body it is. The problem however is like the comment for this method states:

Manipulating a body's transform may cause non-physical behavior.

In practice, calling this method is like making the body go through a Star Trek like transporter. The body is instantly at the new linear and angular position it's told to be at. If we think of a straight line between where it was and where it is after setting its transform, physical behavior would have required anything in this path to have been collided with but that won't happen when using the set transform method.

If you don't want non-physical behavior however, then like has been suggested, you'll want to use one of the other body types as others have addressed.

Hope this makes things clearer and addresses what it seems you were really wondering about.

\$\endgroup\$
1
\$\begingroup\$

Simple! Use a kinematic body!

"A dynamic body is a body which is affected by world forces and react to collisions. And you already met a million of them."

"A static body is a body which isn’t affected by world forces it does not react to collisions. It can’t be moved. Fullstop."

"A kinematic body is an hybrid body which is not affected by forces and collisions like a static body but can moved with a linear velocity like a dynamic body."

-Source: http://www.emanueleferonato.com/2012/05/11/understanding-box2d-kinematic-bodies/

To put it another way, kinematic bodies are very similar to static bodies, however, you can't translate, rotate, move, etc. static bodies because static bodies are... static! It should be very simple to update your code, just replace StaticBody with KinematicBody and then the objects should be moveable.

Kinematic bodies are the in-between of dynamic bodies and static bodies. Static bodies are frozen and unaffected by outside physics, while dynamic bodies are affected by forces, gravity, angular momentum, etc. Kinematic bodies should be used only when necessary for an object to be manipulated through code. Remember that you can't apply a force to a kinematic body but it can be translated and rotated.

\$\endgroup\$
1
  • \$\begingroup\$ @Gnemlock Good point, I will edit the answer, and I think you meant to say "and with a kinematic body", additionally, once I fix the answer, could you remove the down vote if you see fit \$\endgroup\$ Dec 25, 2016 at 3:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .