Box2D creating rectangular bounding boxes around angled line bodies - c++

I'm having a lot of trouble detecting collisions in a zero-G space game. Hopefully this image will help me explain:
http://i.stack.imgur.com/f7AHO.png
The white rectangle is a static body with a b2PolygonShape fixture attached, as such:
// Create the line physics body definition
b2BodyDef wallBodyDef;
wallBodyDef.position.Set(0.0f, 0.0f);
// Create the line physics body in the physics world
wallBodyDef.type = b2_staticBody; // Set as a static body
m_Body = world->CreateBody(&wallBodyDef);
// Create the vertex array which will be used to make the physics shape
b2Vec2 vertices[4];
vertices[0].Set(m_Point1.x, m_Point1.y); // Point 1
vertices[1].Set(m_Point1.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point1.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 2
vertices[2].Set(m_Point2.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point2.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 3
vertices[3].Set(m_Point2.x, m_Point2.y); // Point 3
int32 count = 4; // Vertex count
b2PolygonShape wallShape; // Create the line physics shape
wallShape.Set(vertices, count); // Set the physics shape using the vertex array above
// Define the dynamic body fixture
b2FixtureDef fixtureDef;
fixtureDef.shape = &wallShape; // Set the line shape
fixtureDef.density = 0.0f; // Set the density
fixtureDef.friction = 0.0f; // Set the friction
fixtureDef.restitution = 0.5f; // Set the restitution
// Add the shape to the body
m_Fixture = m_Body->CreateFixture(&fixtureDef);
m_Fixture->SetUserData("Wall");[/code]
You'll have to trust me that that makes the shape in the image. The physics simulation works perfectly, the player (small triangle) collides with the body with pixel perfect precision. However, I come to a problem when I try to determine when a collision takes place so I can remove health and what-not. The code I am using for this is as follows:
/*------ Check for collisions ------*/
if (m_Physics->GetWorld()->GetContactCount() > 0)
{
if (m_Physics->GetWorld()->GetContactList()->GetFixtureA()->GetUserData() == "Player" &&
m_Physics->GetWorld()->GetContactList()->GetFixtureB()->GetUserData() == "Wall")
{
m_Player->CollideWall();
}
}
I'm aware there are probably better ways to do collisions, but I'm just a beginner and haven't found anywhere that explains how to do listeners and callbacks well enough for me to understand. The problem I have is that GetContactCount() shows a contact whenever the player body enters the purple box above. Obviously there is a rectangular bounding box being created that encompasses the white rectangle.
I've tried making the fixture an EdgeShape, and the same thing happens. Does anyone have any idea what is going on here? I'd really like to get collision nailed so I can move on to other things. Thank you very much for any help.

The bounding box is an AABB (axis aligned bounding box) which means it will always be aligned with the the Cartesian axes. AABBs are normally used for broadphase collision detection because it's a relatively simple (and inexpensive) computation.
You need to make sure that you're testing against the OBB (oriented bounding box) for the objects if you want more accurate (but not pixel perfect, as Micah pointed out) results.
Also, I agree with Micah's answer that you will most likely need a more general system for handling collisions. Even if you only ever have just walls and the player, there's no guarantee that which object will be A and which will be B. And as you add other object types, this will quickly unravel.

Creating the contact listener isn't terribly difficult, from the docs (added to attempt to handle your situation):
class MyContactListener:public b2ContactListener
{
private:
PlayerClass *m_Player;
public:
MyContactListener(PlayerClass *player) : m_Player(player)
{ }
void BeginContact(b2Contact* contact)
{ /* handle begin event */ }
void EndContact(b2Contact* contact)
{
if (contact->GetFixtureA()->GetUserData() == m_Player
|| contact->GetFixtureB()->GetUserData() == m_Player)
{
m_Player->CollideWall();
}
}
/* we're not interested in these for the time being */
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{ /* handle pre-solve event */ }
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{ /* handle post-solve event */ }
};
This requires you to assign m_Player to the player's fixture's user data field. Then you can use the contact listener like so:
m_Physics->GetWorld()->SetContactListener(new MyContactListener(m_Player));

How do you know GetFixtureA is the player and B is the wall? Could it be reversed? Could there be an FixtureC? I would think you would need a more generic solution.
I've used a similar graphics framework (Qt) and it had something so you could grab any two objects and call something like 'hasCollided' which would return a bool. You could get away with not using a callback and just call it in the drawScene() or check it periodically.

In Box2D the existence of a contact just means that the AABBs of two fixtures overlaps. It does not necessarily mean that the shapes of the fixtures themselves are touching.
You can use the IsTouching() function of a contact to check if the shapes are actually touching, but the preferred way to deal with collisions is to use the callback feature to have the engine tell you whenever two fixtures start/stop touching. Using callbacks is much more efficient and easier to manage in the long run, though it may be a little more effort to set up initially and there are a few things to be careful about - see here for an example: http://www.iforce2d.net/b2dtut/collision-callbacks

Related

How do I accomplish proper trajectory with a Cocos2d-x node using Chipmunk 2D impulses and rotation?

I'm building a game with Cocos2d-x version 3.13.1 and I've decided to go with the built-in physics engine (Chipmunk 2D) to accomplish animations and collision detection. I have a simple projectile called BulletUnit that inherits from cocos2d::Node. It has a child sprite that displays artwork, and a rectangular physics body with the same dimensions as the artwork.
The BulletUnit has a method called fireAtPoint, which determines the angle between itself and the point specified, then sets the initial velocity based on the angle. On each update cycle, acceleration is applied to the projectile. This is done by applying impulses to the body based on an acceleration variable and the angle calculated in fireAtPoint. Here's the code:
bool BulletUnit::init() {
if (!Unit::init()) return false;
displaySprite_ = Sprite::createWithSpriteFrameName(frameName_);
this->addChild(displaySprite_);
auto physicsBody = PhysicsBody::createBox(displaySprite_->getContentSize());
physicsBody->setCollisionBitmask(0);
this->setPhysicsBody(physicsBody);
return true;
}
void BulletUnit::update(float dt) {
auto mass = this->getPhysicsBody()->getMass();
this->getPhysicsBody()->applyImpulse({
acceleration_ * mass * cosf(angle_),
acceleration_ * mass * sinf(angle_)
});
}
void BulletUnit::fireAtPoint(const Point &point) {
angle_ = Trig::angleBetweenPoints(this->getPosition(), point);
auto physicsBody = this->getPhysicsBody();
physicsBody->setVelocityLimit(maxSpeed_);
physicsBody->setVelocity({
startingSpeed_ * cosf(angle_),
startingSpeed_ * sinf(angle_)
});
}
This works exactly as I want it to. You can see in the image below, my bullets are accelerating as planned and traveling directly towards my mouse clicks.
But, there's one obvious flaw: the bullet is remaining flat instead of rotating to "point" towards the target. So, I adjust fireAtPoint to apply a rotation to the node. Here's the updated method:
void BulletUnit::fireAtPoint(const Point &point) {
angle_ = Trig::angleBetweenPoints(this->getPosition(), point);
// This rotates the node to make it point towards the target
this->setRotation(angle_ * -180.0f/M_PI);
auto physicsBody = this->getPhysicsBody();
physicsBody->setVelocityLimit(maxSpeed_);
physicsBody->setVelocity({
startingSpeed_ * cosf(angle_),
startingSpeed_ * sinf(angle_)
});
}
This almost works. The bullet is pointing in the right direction, but the trajectory is now way off and seems to be arcing away from the target as a result of the rotation: the more drastic the rotation, the more drastic the arcing. The following image illustrates what's happening:
So, it seems that setting the rotation is causing the physics engine to behave in a way I hadn't originally expected. I've been racking my brain on ways to correct the flight path, but so far, no luck! Any suggestions would be greatly apprecitated. Thanks!

How to move a sprite with dynamic physics body with constant speed

Until lately I've been just changing the x coordinate of my sprite on each update and I was happy with it. But yesterday when being in the debugDraw mode, I found out that after certain speed physics body wouldn't align correctly with the sprite ,like this:
Later I got told that, (by Birkemose in cocos2d forum) the preferred way to move a physics body from A to B is to apply impulse to it. But I have no idea how to achieve constant speed this way. This is the code I used to move it without applying any impulse:
-(void)update:(CCTime)delta{
rollingHero.position=ccp(rollingHero.position.x+scrollSpeed*delta,
rollingHero.position.y);
physicsNode.position=ccp(physicsNode.position.x-scrollSpeed*delta,
physicsNode.position.y);
}
So to create a feeling of moving I scroll the physics node and the hero in opposite directions with the same scrolling speed.
I tried lots of different variants of applying impulse, but I never got it moving with constant speed. The speed accelerates and the hero gets offscreen. I would appreciate it very much if someone would post a sample code.
The reason impulse isn't working to keep your character at a constant speed is because impulse translates directly into a change in momentum (and thus a change in velocity). So if you were to try to maintain a constant velocity through impulse, you would have to check your sprite's velocity first, and although you could get pretty close to a constant velocity, it wouldn't be truly constant.
static const float kRollingHeroMoveSpeed = 10.f;
static const float kRollingHeroAccelConstant = 10.f;
-(void)update:(CCTime)delta {
// check velocity of sprite
if(_rollingHero.physicsBody.velocity.x < kRollingHeroMoveSpeed) {
// if velocity is under limit, push character
[_rollingHero.physicsBody applyImpulse:ccp(kRollingHeroAccelConstant, 0)];
}
}
The better way to do this is to step into the C level of the Chipmunk2D physics engine that powers Cocos2D physics.
-(void)onEnter {
[super onEnter];
// tell physics engine to use our C function to update physics body
_rollingHero.physicsBody.body.body->velocity_func = playerUpdateVelocity;
}
static void playerUpdateVelocity(cpBody *body,
cpVect gravity,
cpFloat damping,
cpFloat dt) {
// check validity of cpBody
cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i);
// update velocity and angular velocity
body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt));
body->w = body->w*damping + body->t*body->i_inv*dt;
// reset force vector
body->f = cpvzero;
// reset torque
body->t = 0.0f;
// set body's velocity to desired velocity
body->v.x = kRollingHeroMoveSpeed;
}
Here's cpBody.h on Github.

openGL object real movement simulation

I need to simulate the movement of a row(oar). The oar object is loaded into eclipse with the min3D library which works with openGL.
At this moment, I make the oar move in the 3 axis x, y and z, but I'm not able to control this movement and to make the oar move in the desired way.
(Don't take care of the values, aren't real values)
This is the class which loads the oar, places it in the screen and moves it:
public class Obj3DView extends RendererFragment {
private Object3dContainer rowObject3D;
/** Called when the activity is first created. */
#Override
public void initScene() {
scene.lights().add(new Light());
scene.lights().add(new Light());
Light myLight = new Light();
myLight.position.setZ(150);
scene.lights().add(myLight);
IParser myParser = Parser.createParser(Parser.Type.OBJ, getResources(), "com.masermic.rowingsoft:raw/row_obj",true);
myParser.parse();
rowObject3D = myParser.getParsedObject();
rowObject3D.position().x = rowObject3D.position().y = rowObject3D.position().z = 0;
rowObject3D.scale().x = rowObject3D.scale().y = rowObject3D.scale().z = 0.28f;
scene.addChild(rowObject3D);
}
//THIS MAKES THE OAR MOVE
#Override
public void updateScene() {
rowObject3D.rotation().x += 1; //pitch
rowObject3D.rotation().z += 1; //roll
rowObject3D.rotation().y += 0.5; //yaw
}
roation() method definition: X/Y/Z euler rotation of object, using Euler angles. Units should be in degrees, to match OpenGL usage.
So the question is about how could I define the values that make the oar simulate a real movement?
This looks more like a mathematics question.
I'll present some general tips;
On positioning:
The fixed point of the oar is where the oar is held on the boat, so the oar's rotation is relative to that point, not the center of the oar.
And on top of that, the boat is moving, so is the oar's "fixed" point.
The order for positioning should be:
Translate to the boat position.
Translate the oar so it's center is relative to the correct spot of the boat.
Apply the oar rotation.
Draw it.
On animation:
It will be easier to animate if you alter your model so the origin is at the point where the oar is fixed, but it may complicate other animations/calculae if you later pretend to do more complex manipulations on the oar.
Interpolation of Euler rotation is a mess, I suggest quartenions. You can grab the angles from that nice picture and interpolate. (if you need Euler, still, you can convert the end result to Euler)
For simple animations, (say you just want the oar to repeatedly rotate in some pattern), hardly you will find a better method then key-frames, that is, create a list of coordinates/angles along the path you want the oar to do, and iterate through them, interpolating.
With enough points, a simple linear interpolation will do just fine.

Box2D - When creating a body at runtime, the body does not collide

I've been working on a game with destructible environments and I've come up with a solution where I check for possible destruction within my ContactListener object. Obviously because this is taking place within Step(), I postpone processing the destruction until the moment after the step. I do this by pooling "destruction events" that need to be processed within the contact listener, and then immediately after Step() calling something like contactListener->processDestructionEvents();.
The way I do this is by capturing the colliding fixtures within the beginContact event and then determining the angle of impact, then using that angle to raycast on the fixture itself. I then grap the vertices from the b2PolygonShape of the fixture, then generate two new shapes which are split based on the impact and exit points of the ray. The original fixture is destroyed on the body, and then a new fixture is generated for the first new shape and added to the original body. For the second shape, a new body is generated and that shape is added to this new body.
Anyway everything works great, in debug view I can see that the new shapes have been generated and are all in place, as they should be. However, I get really screwed up behavior at this point. As soon as this process is complete, neither the original nor the newly generated body will collide with anything. If I enable continuous physics, SOMETIMES a dynamic object will collide with one of the edges of these bodies/fixtures, but not always. I'm wondering if it's something I'm doing wrong in my approach to rebuilding bodies/fixtures at runtime. Here is the code for generating the new objects, any help would be greatly appreciated.
void PhysicsContactListener::processDestructionEvents() {
if(!hasDestructionEvents) {return;}
for(destructionEventsIterator = destructionEvents.begin(); destructionEventsIterator != destructionEvents.end(); ++destructionEventsIterator) {
b2Filter f1, f2;
f1.groupIndex = destructionEventsIterator->originalFixture->GetFilterData().groupIndex;
f1.categoryBits = destructionEventsIterator->originalFixture->GetFilterData().categoryBits;
f1.maskBits = destructionEventsIterator->originalFixture->GetFilterData().maskBits;
f2.groupIndex = destructionEventsIterator->originalFixture->GetFilterData().groupIndex;
f2.categoryBits = destructionEventsIterator->originalFixture->GetFilterData().categoryBits;
f2.maskBits = destructionEventsIterator->originalFixture->GetFilterData().maskBits;
b2PolygonShape newShape0 = destructionEventsIterator->newFixtures[0];
b2FixtureDef fixture0Def;
fixture0Def.shape = &newShape0;
fixture0Def.density = 1.0f;
fixture0Def.restitution = 0.2f;
b2Fixture* fixture1 = destructionEventsIterator->hostBody->CreateFixture(&fixture0Def);
fixture1->SetFilterData(f1);
//destructionEventsIterator->hostBody->SetAwake(true);
destructionEventsIterator->hostBody->ResetMassData();
//destructionEventsIterator->hostBody->SetActive(true);
destructionEventsIterator->hostBody->SetTransform(destructionEventsIterator->hostBody->GetPosition(), destructionEventsIterator->hostBody->GetAngle());
b2BodyDef bd;
bd.position = destructionEventsIterator->hostBody->GetPosition();
bd.angle = destructionEventsIterator->hostBody->GetAngle();
bd.type = destructionEventsIterator->hostBody->GetType();
b2Body* newBody = destructionEventsIterator->hostBody->GetWorld()->CreateBody(&bd);
b2PolygonShape* newShape1 = (b2PolygonShape*)(&destructionEventsIterator->newFixtures[1]);
b2Fixture* fixture2 = newBody->CreateFixture(newShape1, destructionEventsIterator->hostBodyDensity);
fixture2->SetFilterData(f2);
newBody->SetAngularVelocity(destructionEventsIterator->hostBody->GetAngularVelocity());
newBody->SetLinearVelocity(destructionEventsIterator->hostBody->GetLinearVelocity());
//newBody->SetAwake(true);
newBody->ResetMassData();
//newBody->SetActive(true);
newBody->SetTransform(destructionEventsIterator->hostBody->GetPosition(), destructionEventsIterator->hostBody->GetAngle());
destructionEventsIterator->hostBody->DestroyFixture(destructionEventsIterator->originalFixture);
}
The two pieces don't collide with each other? Take a look at the categoryBits and maskBits values that each fixture ends up with - looks like each piece is given the same values for these. My guess is you are just overlooking the fact that these masks are checked against each other both ways, eg. from the Box2D source code:
bool collide =
(filterA.maskBits & filterB.categoryBits) != 0 &&
(filterA.categoryBits & filterB.maskBits) != 0;
On the other hand if you mean the pieces collide with nothing at all and simply fall through the ground and down forever except for SOMETIMES, then I might suspect an incorrect polygon winding.
btw a b2Filter holds only primitives so you could assign those directly:
b2Filter f1 = destructionEventsIterator->originalFixture->GetFilterData();
...also, the first SetTransform and the second ResetMassData are redundant.

Adjust volume of sound effect based on speed of collision

I am using Cocos2D with Box2D to create a simple physics game. I want to adjust the volume of a collision sound effect depending on the speed of the colliding body. The faster the body is travelling when it collides, the louder the sound. I am using the SimpleAudioEngine library which has a playSound method with a gain parameter. Is there a way to convert the speed of the colliding body (a b2Body object) to a value between 0 and 1 that I can apply to the gain?
In the post solve function get an impulse value, divide it by 100 perhaps? I'm not sure what the levels of impulse you get are.
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
void* userDataA = fixtureA->GetBody()->GetUserData();
CCNode *myActorA = (CCNode*)userDataA;
void* userDataB = fixtureB->GetBody()->GetUserData();
CCNode *myActorB = (CCNode*)userDataB;
// stuff above will allow you to work out which objects are hitting each other
// get the impulse
int impulseInt = impulse->normalImpulses[0];
}