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.y). This means that the rectangular polygon shape that you set up is - in terms of world coordinates - now centered at (
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:
- Set the angular orientation of the rectangle via the fourth parameter to
shape.setAsBox (like you're doing); or
- instead set the angular orientation of the body via the angle parameter of your
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:
- 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.
- 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.
- 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.