I am new to Box2D and is currently trying to detect if an object (Ball) has managed to pass through another object (Goal) by using b2Contact. For this, I have implemented b2ContactListener and everything is working fine.
However, I was wondering, is there any way for me to set to detect collision for one direction only? For example, only from left to right, collision will not count if the ball passes through from right to left. I want to implement something like this from here as could be seen from the documentation.
In the documentation, they have detected the direction of the collision by using the points from the object as such
int numPoints = contact->GetManifold()->pointCount;
b2WorldManifold worldManifold;
contact->GetWorldManifold( &worldManifold );
However, for my case, I am using a b2CircleShape for my ball object, which indirectly returns the numPoints to be 0. Thus, I am unable to detect the collision using the above method. Is there any way I could detect the points from the circle? Or is there any way I could detect the direction of the collision?
Maybe this will help you.
b2WorldManifold manifold;
contact->GetWorldManifold(&manifold);
manifold.normal.Normalize();
if(manifold.normal.x > 0){
[...]
}
Related
By default, it seems PhysX sets the "friction" on your feet to infinity, so that calling move with a downwards force (like gravity) doesn't cause you to slide down when standing on a sloped surface.
That's exactly the opposite of what I want though. I know of PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING, but that doesn't seem to use any sort of gravity or acceleration, and I'm not using the built-in slopeLimit value. There's also setPreventVerticalSlidingAgainstCeiling(), so it seems kind of odd that something similar wouldn't exist for the floor.
So, is there any built-in way to disable this artificial friction, and allow sliding vertically in the same way the CCT can slide along walls and ceilings? And if not, how could I generate a new downward movement vector that follows the surface normal? I'm pretty terrible at anything related to math, so (psuedo-)code examples would be appreciated.
Turns out I can't read, and the answer to my problem was right in the SDK Guide.
I simply had to implement a PxControllerBehaviorCallback, and have it return PxControllerBehaviorFlag::eCCT_SLIDE for each function.
virtual physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxShape& shape, const physx::PxActor& actor) {
return physx::PxControllerBehaviorFlag::eCCT_SLIDE;
}
virtual physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxController& controller) {
return physx::PxControllerBehaviorFlag::eCCT_SLIDE;
}
virtual physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxObstacle& obstacle) {
return physx::PxControllerBehaviorFlag::eCCT_SLIDE;
}
then set PxControllerDesc->behaviorCallback to an instance of my PxControllerBehaviorCallback class.
I'm currently working on integrating Bullet SDK into my game engine. Currently I'm trying to apply a force to my objects using a mouse click. So far I've failed.
Here is the code that is called when an object is clicked on:
void PhysicsComponent::ApplyForce(glm::vec3& hitPoint, glm::vec3& direction, float amount)
{
if (!m_body)
return;
m_body->activate(true);
btVector3 force = btVector3(0, 1, 0) * amount;
m_body->applyImpulse(force, centerOfMass);
}
If I use the same idea in my update() function, it works fine. Why is this?
Solved it.
Issue had nothing to do with Bullet. I was storing my Physics Components in a vector as references and attaching &(m_physicsComponents.back()) to my Objects. Evey time the vector resized 1 object would break. I just hadn't noticed the issue until now, since the broken object happened to be what my test object was sitting on. Changed my vector to store pointers and everything works.
I am working on realtime movement detection and object tracking in OpenCV/C++ and am currently stuck at the tracking part.
Matlab-Example of what I want to do: http://www.mathworks.de/de/help/vision/examples/motion-based-multiple-object-tracking.html (I'm troubled with the tracking part and how to transfer it to C++/OpenCV)
My motion part works with OpenCVs BackgroundSubtractor MOG2 which makes it possible to find contours and filter smaller contours out.
For tracking I am currently trying to use the KalmanFilter (with a similar implementation to this) which right now is getting called in every frame if a moving object was found and draws a line on it's path.
My Detection & Tracking part looks something like this:
BackgroundSubtractorMOG2 bg;
bg.operator()(frame, threshold);
bg.getBackgroundImage(background);
... //morphological operations to remove noise etc.
findContours(threshold, ...);
... //filtering to reject contours which are too smalle/too big
for(int i = 0; i < contours.size(); i++){
approxPolyDP(...);
boundRect = boundingRect(...);
x = boundRect.x + boundRect.width/2;
y = boundRect.y + boundRect.height/2;}
kalmanFilter.track(x,y);
kalmanFilter.draw(frame);
Current Problem:
I have a scene with 0 moving objects, then 1 object moves in, gets detected through contours and gets tracked. Then a 2nd object moves in sight, gets detected and makes the tracker jump to it instead of following the first or marking both individually (which I want).
The current tracker takes x & y coordinates of the found object. Like this, once another object gets detected, the tracker still assumes it is the same object but with other coordinates than anticipated.
As one can see, there's no function to assign "tracks" to a certain object which is probably the biggest problem. I read about the hungarian algorithm but am not quite sure on how to implement it within my functions.
What would be a good method to make the tracking work for multiple objects?
My idea was that if I had each object uniquely identified I could check if the ID is still the same and if not let the tracker know that it's a new object to track separately from the other. Not sure if this is necessary or even useful though and if so, how to do it.
try something like this:
for each contour:
if its already tracked with ID 'A': kalmanFilter_with_id_A.track(x,y);
else createNewKalmanFilterWithID A
you need some mechanism to decide whether it is already tracked. In simple tracking you just decide by measuring distances to contours in last frame and if its close enough, it's the old object. This is quite erroneous so you might want to have a look at better tracking methods, e.g. probabilistic tracking.
so simple mode:
for each contour 'currentFrameC':
for each contour 'lastFrameC'
if distance(currentFrameC, lastFrameC) is smallest and < threshold
currentFrameC is the same object as lastFrameC so give it the same ID
if no shortest contour with dist < thres was found, create a new object with a new ID and create a new KalmanFilter with this same ID for that object
call kalmanFilter with ID for each found contour ID
I've had to completely revamp this question as I don't think I was explicit enough about my problem.
I'm attempting to learn the ropes of Box2D Web. I started having problems when I wanted to learn how to put multiple shapes in one rigid body (to form responsive concave bodies). One of the assumptions I made was that this kind of feature would only really be useful if I could change the positions of the shapes (so that I can be in control of what the overall rigid body looked like). An example would be creating an 'L' body with two rectangle shapes, one of which was positioned below and to-the-right of the first shape.
I've gotten that far in so-far-as I've found the SetAsOrientedBox method where you can pass the box its position in the 3rd argument (center).
All well and good. But when I tried to create two circle shapes in one rigid body, I found undesirable behaviour. My instinct was to use the SetLocalPosition method (found in the b2CircleShape class). This seems to work to an extent. In the debug draw, the body responds physically as it should do, but visually (within the debug) it doesn't seem to be drawing the shapes in their position. It simply draws the circle shapes at the centre position. I'm aware that this is probably a problem with Box2D's debug draw logic - but it seems strange to me that there is no online-patter regarding this issue. One would think that creating two circle shapes at different positions in the body's coordinate space would be a popular and well-documented phenomina. Clearly not.
Below is the code I'm using to create the bodies. Assume that the world has been passed to this scope effectively:
// first circle shape and def
var fix_def1 = new b2FixtureDef;
fix_def1.density = 1.0;
fix_def1.friction = 0.5;
fix_def1.restitution = .65;
fix_def1.bullet = false;
var shape1 = new b2CircleShape();
fix_def1.shape = shape1;
fix_def1.shape.SetLocalPosition(new b2Vec2(-.5, -.5));
fix_def1.shape.SetRadius(.3);
// second circle def and shape
var fix_def2 = new b2FixtureDef;
fix_def2.density = 1.0;
fix_def2.friction = 0.5;
fix_def2.restitution = .65;
fix_def2.bullet = false;
var shape2 = new b2CircleShape();
fix_def2.shape = shape2;
fix_def2.shape.SetLocalPosition(new b2Vec2(.5, .5));
fix_def2.shape.SetRadius(.3);
// creating the body
var body_def = new b2BodyDef();
body_def.type = b2Body.b2_dynamicBody;
body_def.position.Set(5, 1);
var b = world.CreateBody( body_def );
b.CreateFixture(fix_def1);
b.CreateFixture(fix_def2);
Please note that I'm using Box2D Web ( http://code.google.com/p/box2dweb/ ) with the HTML5 canvas.
It looks like you are not actually using the standard debug draw at all, but a function that you have written yourself - which explains the lack of online-patter about it (pastebin for posterity).
Take a look in the box2dweb source and look at these functions for a working reference:
b2World.prototype.DrawDebugData
b2World.prototype.DrawShape
b2DebugDraw.prototype.DrawSolidCircle
You can use the canvas context 'arc' function to avoid the need for calculating points with sin/cos and then drawing individual lines to make a circle. It also lets the browser use the most efficient way it knows of to render the curve, eg. hardware support on some browsers.
Since it seems like you want to do custom rendering, another pitfall to watch out for is the different call signatures for DrawCircle and DrawSolidCircle. The second of these takes a parameter for the axis direction, so if you mistakenly use the three parameter version Javascript will silently use the color parameter for the axis, leaving you with an undefined color parameter. Hours of fun!
DrawCircle(center, radius, color)
DrawSolidCircle(center, radius, axis, color)
I've been working on an iOS project, using Cocos2D 1.0 and Box2D, and I've run into a bit of a problem.
What I need to be able to do is determine the orientation of a surface my player has hit. For example, if we have a rectangular platform, and the player collides with it, I need to know whether the player has hit the left, right, top, or bottom face of it. ALL the objects in the game are square, and the ONLY one moving is the player.
I'm currently using a b2ContactListener in Box2D (well, my own subclass of one, anyway), and have been playing around with the local normal of the manifold from the contact in BeginContact. The main problem I have is that that normal seems to be affected by the rotation of the player body (e.g. the player has rotated 90 degrees, OR the player is spinning wildly on impact - both situations are giving me trouble), and I seem to end up with ambiguity (i.e. collisions with different faces that give the same normal...) if I try to allow for that - although of course I could just be doing something horribly wrong. Now, I don't understand manifolds very well, so it's possible that my problem stems from that, or maybe I'm missing something obvious.
Any suggestions?
I would prefer to do this in the cleanest and least ugly manner possible. Bear in mind that the main categorisation I care about is "player is landing on something from above" vs "everything else", but I may end up needing the exact
If you need more information or clarification about anything, just ask.
EDIT: Just to clarify, I am aware that the normal points from A to B (in a collision between A and B) by convention in Box2D, and my code does check to see which one is the player and takes this into account before doing any calculations to determine which face has been hit.
So, I feel a little awkward about answering my own question, but apparently it's officially encouraged.
Anyway, the problem with the way I was approaching things was twofold. Firstly, I was using the contact manifold's local normal instead of the world normal. Secondly, my code for reversing the object transformations was buggy (I would never have needed to do this if I had been using the world manifold).
The world manifold takes into account object transformations and sizes and as such contains data more easily applicable to the world co-ordinate system.
By convention in Box2d, the collision normal (for both the world manifold and the contact manifold) points from A to B - this has to be taken into account for some uses, since the normal from A to B is the inverse of the normal from B to A, so you can't just assume that one body will always be A.
So, the solution is to use get the world manifold for each collision, examine its normal, and then make whatever decisions you want to make.
For example, in the BeginContact method of a b2ContactListener subclass (if you have no idea what I'm talking about then check out part 2 of this tutorial):
void ContactListener::BeginContact(b2Contact* contact)
{
b2WorldManifold worldManifold;
contact->GetWorldManifold(&worldManifold); // this method calls b2WorldManifold::Initialize with the appropriate transforms and radii so you don't have to worry about that
b2Vec2 worldNormal = worldManifold.normal;
// inspect it or do whatever you want based on that...
}
Since you'll likely need to check what bodies are colliding, and which one is A and which one is B, you may want to keep a vector of structs containing the fixtures that collided (as in that tutorial) and the normal, and iterate over the vector in your tick() method or similar. (You can get these out of the contact with contact->GetFixtureA() and contact->GetFixtureB().)
Now, you could get the point data from the world manifold, and make your decisions based on that, but why would you when the normal is already available, since in this particular case the normal (combined with which shapes the normal points from and to) is all that is needed.
Edit (for #iBradApps):
First, I'm assuming here that you have followed the tutorial I linked to and have a contact listener set up. If you haven't, follow it because Ray explains it in depth quite well.
Second, I want to point out that there is no absolute guarantee which object is A and which is B (well, it depends on what kind of Box2D objects they are; suffice to say if they can both move, you can't guarantee the ordering, at least as far as I know), so in my case I wanted to see if the player object had hit something, so I created a class variable (b2Fixture *playerF) in my contact listener that stored a reference to the player object so I could determine whether contact A or contact B was the player.
You asked about detecting a collision where something else collided with the top of B. Something like the following should work, although I haven't had a chance to test it for you:
In your ContactListener.h:
public:
b2Fixture *playerF;
// along with the vector etc mentioned in Ray's tutorial
// and anything else you want
When you make the ContactListener in your init() (assuming you called it _contactListener):
_contactListener->playerF = playerFixture; // or whatever you called the player body fixture
BeginContact method:
void ContactListener::BeginContact(b2Contact* contact)
{
b2WorldManifold worldManifold;
contact->GetWorldManifold(&worldManifold); // this method calls b2WorldManifold::Initialize with the appropriate transforms and radii so you don't have to worry about that
b2Vec2 worldNormal = worldManifold.normal; // this points from A to B
if (playerF == contact->GetFixtureA()) {
// note that +ve y-axis is "up" in Box2D but down in OpenGL and Cocos2D
if (worldNormal.y < -0.707) { // use a constant for performance reasons
// if the y component is less than -1/sqrt(2) (approximately -0.707),
// then the normal points more downwards than across, so A must be hitting B
// from roughly above. You could tune this more towards the top by increasing
// towards -1 if you want but it worked fine for me like this last time and
// you might run into issues with missing hits
NSLog(#"Player (A) hit B roughly on the top side!");
// here you can set any class variables you want to check in
// your update()/tick(), such as flags for whether the player has died from
// falling or whatever
}
} else if (playerF == contact->GetFixtureB()) {
if (worldNormal.y > 0.707) {
NSLog(#"Player (B) hit A roughly on the top side!");
}
} else {
// it's something else hitting something else and we don't care about it
}
}
As for doing it in your tick() method instead, yes, you can. I actually did all my stuff in PostSolve in the contact listener because I needed to know how hard the player hit, but all I cared about beyond that was whether the player had hit hard enough to kill them, so I didn't need or want to iterate over all the contacts in my tick() - I just set a flag in the contact listener that said the player had suffered a fatal impact.
If you want to do this all in the update method, then starting from what Ray has, add a b2Vec2 to the MyContact struct, and in BeginContact, add both the two fixtures (like Ray does) and get the collision normal (as I do) and add it too.
The modified MyContact struct:
struct MyContact {
b2Fixture *fixtureA;
b2Fixture *fixtureB;
b2Vec2 normal;
bool operator==(const MyContact& other) const
{
return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);
}
};
The new BeginContact method:
void MyContactListener::BeginContact(b2Contact* contact) {
b2WorldManifold wordManifold;
contact->GetWorldManifold(&worldManifold);
MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB(), worldManifold.normal };
_contacts.push_back(myContact);
}
This will give you all the information you need to do the checking I initially described in your tick().
Edit again:
Your tick() method might contain something like this if you want to do the processing there, assuming you have called the player fixture (or ball fixture, like in the tutorial, or whatever it is you're interested in) _playerFixture, that you've got a contact listener with the same name as in the tutorial, that you added the b2Vec2 normal to the MyContact struct, that you are adding contacts to the vector (as above) in BeginContact, and that you are deleting contacts from the vector in the EndContact (as shown in the tutorial - it's probably fine as is):
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin(); pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
if (_playerFixture == contact.fixtureA && contact.normal.y < -0.707) {
NSLog(#"Player (A) hit B roughly on the top side!");
} else if (_playerFixture == contact.fixtureB && contact.normal.y > 0.707) {
NSLog(#"Player (B) hit A roughly on the top side!");
} else {
// it's something else hitting something else and we don't care about it
}
}