Box2d multiple fixtures and positioning - cocos2d-iphone

I'm attempting to create a "U" shape in Box2d (in Cocos2d) by joining 3 rectangles like so: |_|
It sounds like joints are not the correct solution here since I don't want any movement so I have created a main body which is the middle bit and 2 fixtures for the sides. I've added the two sides to the middle bit like this:
mainBody->CreateFixture(&leftFixtureDef);
mainBody->CreateFixture(&rightFixtureDef);
This works, however both side fixtures get added to the center of the mainBody. I can't seem to work out how to position the fixtures relative to the main body. Attaching a sprite/node to the fixture and changing the position doesn't seem to make a difference.
Any ideas?
Many thanks.

it's the property of a shape. I did not find such property for b2CircleShape, but for b2PolygonShape has m_centroid paramter - it's the shape center coordinates relative to the body. Specify it to have a valid position of a shape.
For b2PolyganShape there is a method setAsBox(w, h) but alos there is more complex one:
setAsBox(float32 width, float32 height, const b2Vec2 &center, float32 rotation)
Use this method or specify the centroid manualy.
Here is the code for the U shape
b2BodyDef bDef;
bDef.type = b2_dynamicBody;
bDef.position = b2Vec2(0, 0);
b2Body *body = world_->CreateBody(&bDef);
b2PolygonShape shape;
const float32 density = 10;
shape.SetAsBox(1, 0.1);
body->CreateFixture(&shape, density);
shape.SetAsBox(0.1, 1, b2Vec2(-1 + 0.1, 1), 0);
body->CreateFixture(&shape, density);
shape.SetAsBox(0.1, 1, b2Vec2(1 - 0.1, 1), 0);
body->CreateFixture(&shape, density);

Related

Drawing a sprite on the circumference of a circle based on the position of other objects

I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.

How to find Relative Offset of a point inside a non axis aligned box (box that is arbitrarily rotated)

I'm trying to solve an problem where I cannot find the Relative Offset of a Point inside a Box that exists inside of a space that can be arbitrarily rotated and translated.
I know the WorldSpace Location of the Box (and its 4 Corners, the Coordinates on the Image are Relative) as well as its Rotation. These can be arbitrary (its actually a 3D Trigger Volume within a game, but we are only concerned with it in a 2D plane from top down).
Looking at it Aligned to an Axis the Red Point Relative position would be
0.25, 0.25
If the Box was to be Rotated arbitrarily I cannot seem to figure out how to maintain that given we sample the same Point (its World Location will have changed) its Relative Position doesnt change even though the World Rotation of the Box has.
For reference, the Red Point represents an Object that exists in the scene that the Box is encompassing.
bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
bool bResult = false;
if (MapMarker)
{
const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
float RelativeX = FMath::GetMappedRangeValueClamped(
-FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X)),
FVector2D(0.f, 1.f),
MapMarkerLocation.X
);
float RelativeY = FMath::GetMappedRangeValueClamped(
-FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y)),
FVector2D(0.f, 1.f),
MapMarkerLocation.Y
);
OutPosition.X = FMath::Abs(RelativeX);
OutPosition.Y = FMath::Abs(RelativeY);
bResult = true;
}
return bResult;
}
Currently, you can see with the above code that im only using the Top Left and Bottom Right corners of the Box to try and calculate the offset, I know this is not a sufficient solution as doing this does not allow for Rotation (Id need to use the other 2 corners as well) however I cannot for the life of me work out what I need to do to reach the solution.
FMath::GetMappedRangeValueClamped
This converts one range onto another. (20 - 50) becomes (0 - 1) for example.
Any assistance/advice on how to approach this problem would be much appreciated.
Thanks.
UPDATE
#Voo's comment helped me realize that the solution was much simpler than anticipated.
By knowing the Location of 3 of the Corners of the Box, I'm able to find the points on the 2 lines these 3 Locations create, then simply mapping those points into a 0-1 range gives the appropriate value regardless of how the Box is Translated.
bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
bool bResult = false;
if (MapMarker && GetMapVolume())
{
const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
const FVector TopLeftLocation = GetMapVolume()->GetCornerTopLeftLocation();
const FVector TopRightLocation = GetMapVolume()->GetCornerTopRightLocation();
const FVector BottomLeftLocation = GetMapVolume()->GetCornerBottomLeftLocation();
FVector XPlane = FMath::ClosestPointOnLine(TopLeftLocation, TopRightLocation, MapMarkerLocation);
FVector YPlane = FMath::ClosestPointOnLine(TopLeftLocation, BottomLeftLocation, MapMarkerLocation);
// Convert the X axis into a 0-1 range.
float RelativeX = FMath::GetMappedRangeValueUnclamped(
FVector2D(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerTopRightLocation().X),
FVector2D(0.f, 1.f),
XPlane.X
);
// Convert the Y axis into a 0-1 range.
float RelativeY = FMath::GetMappedRangeValueUnclamped(
FVector2D(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomLeftLocation().Y),
FVector2D(0.f, 1.f),
YPlane.Y
);
OutPosition.X = RelativeX;
OutPosition.Y = RelativeY;
bResult = true;
}
return bResult;
}
The above code is the amended code from the original question with the correct solution.
assume the origin is at (x0, y0), the other three are at (x_x_axis, y_x_axis), (x_y_axis, y_y_axis), (x1, y1), the object is at (x_obj, y_obj)
do these operations to all five points:
(1)translate all five points by (-x0, -y0), to make the origin moved to (0, 0) (after that (x_x_axis, y_x_axis) is moved to (x_x_axis - x0, y_x_axis - y0));
(2)rotate all five points around (0, 0) by -arctan((y_x_axis - y0)/(x_x_axis - x0)), to make the (x_x_axis - x0, y_x_axis - y0) moved to x_axis;
(3)assume the new coordinates are (0, 0), (x_x_axis', 0), (0, y_y_axis'), (x_x_axis', y_y_axis'), (x_obj', y_obj'), then the object's zero-one coordinate is (x_obj'/x_x_axis', y_obj'/y_y_axis');
rotate formula:(x_new, y_new)=(x_old * cos(theta) - y_old * sin(theta), x_old * sin(theta) + y_old * cos(theta))
Update:
Note:
If you use the distance method, you have to take care of the sign of the coordinate if the object might go out of the scene in the future;
If there will be other transformations on the scene in the future (like symmetry transformation if you have mirror magic in the game, or transvection transformation if you have shockwaves, heatwaves or gravitational waves in the game), then the distance method no longer applies and you still have to reverse all the transformations your scene has in order to get the object's coordinate.

How to use Draw Circle In Unreal

I can draw a circle with something like this.
DrawCircle(GetWorld(), GetOwner()->GetActorLocation(),XVector, YVector, DebugLineColor, GetPolarGridElement(i, j, k).X, 100, false, -1.0f, LineDepthInt8, LineThickness);
The problem is, I cant seem to rotate it correctly with the XVector, and YVector values. Here Unreal asks for X Axis vector and Y axis vector. How does this work, whats the math behind it? Say for example I have
FRotator DesiredAngle = FRotator(0,90,45);
How do I convert this to XVector and YVector such that my circle is drawn correctly?
Think of the circle as being drawn on a sheet of paper. The DrawCircle is asking for the 3d vectors that correspond to the edges of the sheet such that it can render it correctly in 3D space.
As a simple example, you can try using the following:
FVector XVector = DesiredAngle.rotateVector(0,1,0);
FVector YVector = DesiredAngle.rotateVector(0,0,1);
I had to look up the axis system of UE4, and I found that for the Y axis is "sideways" and the Z axis is "up", so we rotate these according to DesiredAngle.
Hey finally figured this out thanks to the help from others below. To add to the answer from Botje. If I wanted to draw a circle that follows the rotation of a actor. I do this:
FRotator DesiredAngleZ = GetOwner()->GetActorRotation();
FVector XVector = DesiredAngleZ.RotateVector(FVector(0, 1, 0));
FVector YVector = DesiredAngleZ.RotateVector(FVector(0, 0, 1));
In my case though I wanted to follow the rotation of the actor, but offset by 90 degrees. I thought I could do something like this
FRotator DesiredAngleZ = GetOwner()->GetActorRotation();
DesiredAngleZ += FRotator(0,0,90);
FVector XVector = DesiredAngleZ.RotateVector(FVector(0, 1, 0));
FVector YVector = DesiredAngleZ.RotateVector(FVector(0, 0, 1));
This though fails terribly. Why? because you cant rotate a Rotator like that. My Solution to the problem was to convert both FRotators to quaternions and then multiply them and then convert back. Final solution looks like this:
FQuat tempQuat = GetOwner()->GetActorRotation().Quaternion() *
FRotator(0, 90, 0).Quaternion();
FRotator DesiredAngleZ = tempQuat.Rotator();
FVector XVector = DesiredAngleZ.RotateVector(FVector(0, 1, 0));
FVector YVector = DesiredAngleZ.RotateVector(FVector(0, 0, 1));

Cocos2d Box2d multiple b2circle shape fixture for one body with positioning

Anybody knows how to add 2 circle fixture to one b2body with desired positioning?
I know how to add two polygon fixture to one body by using m_centroid. But how can I do it for circle fixtures.
Any answer will be appreciated. I want to stick some object together. I tried joints but they all are elastic. I want distance static.
Thanks everyone!
You should create two fixtures for your body and the shapes of those fixtures should be b2CircleShape
//Create a body. You'll need a b2BodyDef, but I've assumed you know how to use these since you say you've created bodies successfully before.
b2Body* body = world->CreateBody(&bodyDef);
//Create the first circle shape. It's offset from the center of the body by -2, 0.
b2CircleShape circleShape1;
circleShape1.m_radius = 0.5f;
circleShape1.m_p.Set(-2.0f, 0.0f);
b2FixtureDef circle1FixtureDef;
circle1FixtureDef.shape = &circleShape1;
circle1FixtureDef.density = 1.0f;
//Create the second circle shape. It's offset from the center of the body by 2, 0.
b2CircleShape circleShape2;
circleShape2.m_radius = 0.5f;
circleShape2.m_p.Set(2.0f, 0.0f);
b2FixtureDef circle2FixtureDef;
circle2FixtureDef.shape = &circleShape2;
circle2FixtureDef.density = 1.0f;
//Attach both of these fixtures to the body.
body->CreateFixture(&circle1FixtureDef);
body->CreateFixture(&circle2FixtureDef);

How to get back b2PolygonShape from fixture? [BOX2d]

Hi
In BOX2d I have a fixture I know its a polygon through:
b2Shape *sh = fixture->GetShape();
NSLog(#"Type %d", sh->GetType());
//this returns 1, means its a polygon.
Now how to get the vertices in order to know exactly what shape it has i.e. rectangle square etc.
If you know it's a polygon, cast it to a b2PolygonShape and call GetVertices().
if(sh->GetType()==1)
{
b2PolygonShape* poly = (b2PolygonShape*)sh;
b2Vec2* verts = poly->GetVertices();
}
Docs: http://www.linuxuser.at/elements/doc/box2d/classb2_polygon_shape.htm#0e8a0b73c98b3e57f26772d14f252e8b