Professional Documents
Culture Documents
Page 29
Page 29
Box2D tutorials | Cocos2d-x resources | Box2D JSON loader | R.U.B.E Box2D editor | R.U.B.E free | My apps | Blog ! | 日本語はこちら | Box2D中文教程
Conveyor belts The catch is, we'll need to make a little tweak to Box2D's source code to help this work. It's just a small change
Projected trajectory though, nothing to worry about.
Sticky projectiles
Hovercar suspension
Top-down car physics
Change to Box2D code
One-way walls Since we will be using BeginContact event which only occurs one time per collision, we can only use SetEnabled once to alter
Buoyancy the behavior of the contact. The problem is the contact will revert to being enabled again after each step. We could make a
Explosions note of which contacts we have disabled and then check the list of them every time in PreSolve, but that is kinda inefficient
Physics-driven particles and more work than I can be bothered with today. Or any day actually :)
Other topics So we'll just quietly sneak into b2Contact.cpp and comment out the line at the beginning of the Update function which re-
enables the contact. After you're done it should look like this:
Setting up (iPhone)
1 // Re-enable this contact.
2 //m_flags |= e_enabledFlag;
https://www.iforce2d.net/b2dtut/one-way-walls 1/5
9/4/2020 One-way walls - Box2D tutorials - iforce2d
That's it!
Note: Obviously you might not want to do this if you are relying on the default behavior for some reason...
First let's take a look at how we could handle the simplest case, where the platform is static and level, and the player can
move up through it but not downwards. For the rest of this topic we'll reuse the platform shape, and we'll signify the 'back'
(the side from which it can be passed through ) with the small pointed corner, so the flat side should be solid. For the rest of
this topic I will be calling the flat side the 'front' or the 'face' of the platform, and when I say 'into the platform' I mean
approaching from the front side.
Here is some basic setup - keep a reference to the fixture and body in a class variable so we can use them later:
All we need to do in BeginContact is check whether each contact point is moving up or down. If either of them is moving
down, the platform should be solid. Just as a reminder that there could be two contact points, and also to help explain why
we need to check both of them, consider this case where the box is rotating as it hits the platform:
Obviously in this exaggerated screenshot the left corner is not touching the platform so there would be no contact point
there, but imagine that this angle is much smaller and both corners of the box are still touching the platform whilst the box is
rotating - rare, but it can happen.
Here is how a BeginContact could decide whether an incoming contact to the platform should be disabled.
https://www.iforce2d.net/b2dtut/one-way-walls 2/5
9/4/2020 One-way walls - Box2D tutorials - iforce2d
9 if ( fixtureA == m_platformFixture ) {
10 platformFixture = fixtureA;
11 otherFixture = fixtureB;
12 }
13 else if ( fixtureB == m_platformFixture ) {
14 platformFixture = fixtureB;
15 otherFixture = fixtureA;
16 }
17
18 if ( !platformFixture )
19 return;
20
21 b2Body* platformBody = platformFixture->GetBody();
22 b2Body* otherBody = otherFixture->GetBody();
23
24 int numPoints = contact->GetManifold()->pointCount;
25 b2WorldManifold worldManifold;
26 contact->GetWorldManifold( &worldManifold );
27
28 //check if contact points are moving downward
29 for (int i = 0; i < numPoints; i++) {
30 b2Vec2 pointVel =
31 otherBody->GetLinearVelocityFromWorldPoint( worldManifold.points[i] );
32 if ( pointVel.y < 0 )
33 return;//point is moving down, leave contact solid and exit
34 }
35
36 //no points are moving downward, contact should not be solid
37 contact->SetEnabled(false);
38 }
39
40 void EndContact(b2Contact* contact)
41 {
42 //reset the default state of the contact in case it comes back for more
43 contact->SetEnabled(true);
44 }
As you can see, most of this code is taken up with the usual chores of finding out which fixture is which etc. The two main
points to note are: we use GetLinearVelocityFromWorldPoint instead of the velocity of the body itself, so that we can check
the direction of movement of the relevant parts that collided with the platform. This is necessary if you allow your player body
to rotate.
The other important thing is to return the contact to the default (enabled) state when an EndContact occurs. This is
necessary because contacts exists as long as the AABBs of two fixtures continue to overlap, even if the fixtures themselves do
not overlap. If the player jumps just high enough to clear the top of the platform, but not high enough for the AABBs to
separate, the contact will remain disabled and he will fall back down again.
By 'general case', I mean situations where the player and platform bodies can both be rotated or moving around. This is a
little tricker but still not too hard. We can't just check the y-component of the velocity of the contact point directly, we need
to convert that velocity into a relative velocity from the point of view of the platform, and then check it. Since the platform
itself can be moving or rotating, this means we'll also need to take into account the velocity of the contact point in the
platform.
Unfortunately when I tried this in a more game-like scenario, there was one rather annoying little issue that showed up when
the other body approaches the platform at a very shallow angle. Consider how we are handling things so far, by checking the
vertical component of the approach velocity:
https://www.iforce2d.net/b2dtut/one-way-walls 3/5
9/4/2020 One-way walls - Box2D tutorials - iforce2d
At the red line there is a point where we abruptly switch between saying the platform is solid or not. For the most part this is
fine, the problem comes about when we have a body approaching from a very shallow angle, almost right along the red line,
and it is moving upwards but we want the platform to be solid. But when would we ever want that?
Actually, for a platform game using tiles we want that almost ALL the time. Even if the player body is moving along flat
ground, it is moving up and down a tiny bit as the collision response works to keep it on top of the ground fixture. Here is an
exaggerated image of this situation:
Suppose that the player body was moving upwards just a tiiiny bit at it hits the next tile. Our check in BeginContact would
disable that contact and the player would fall through the ground. So unfortunately, simply checking the approach velocity is
not quite enough - we'll need to use some other information as well.
For the other information, I decided to use the location of the contact point to decide whether the player should be allowed to
stand on the platform when approaching from a very shallow angle like this. The relevant part of BeginContact now looks like
this:
Here we do the extra check when the approach velocity is less than 1 m/s in or out of the platform, and allow the platform to
stay solid if the contact point is within 5cm of the top of the platform... and that is the annoying part. Until now we could
treat all of our one-sided fixtures in the same way, but for this extra check we now need to know the 'height' of the front face
of the platform. Sure, we know this because we set the fixtures up ourselves, but it's extra work to have this info required in
the contact listener.
https://www.iforce2d.net/b2dtut/one-way-walls 4/5
9/4/2020 One-way walls - Box2D tutorials - iforce2d
Improvements
This method takes the rather simplistic assumption that the 'front' face of the platform/wall always has the normal (0,1) in
the local coordinates of the body, and our check results in approximately a 180 degree range around the front face in which
the platform/wall will be solid. If you wanted to restrict or extend the range in which the wall is solid, you could use a method
like that in the conveyor belts topic to check if the contact was inside the range you are interested in.
As I mentioned earlier, there does not seem to be any cut and dried solution for handling one-sided walls in Box2D. I think
this one does ok, but it's not as free from tweak-requiring as I had hoped. Check out the downloads below for a scene
involving a bunch of things commonly encountered in platform games. You may find the occasional glitch here and there :)
Source code
Here is the source code for those who would like to try it out for themselves. This is a 'test' for the testbed, based on Box2D
v2.3.0.
Linux binary
Windows binary
YouTube video
Not seeing the any comments below? Try changing the 'https' in the page URL to just 'http'
Click here: http://www.iforce2d.net/b2dtut/one-way-walls
https://www.iforce2d.net/b2dtut/one-way-walls 5/5