You are on page 1of 5

9/4/2020 Introduction - Box2D tutorials - iforce2d

iforce2d <# insert awesome header graphics #>

Crushing immovable objects since 2011

Box2D tutorials | Cocos2d-x resources | Box2D JSON loader | R.U.B.E Box2D editor | R.U.B.E free | My apps | Blog ! | 日本語はこちら | Box2D中文教程

News Mar 20: R.U.B.E v1.7 released. Instanciable objects!


Feb 21: Added RUBE sample loader for Cocos2d-X v3.4.
Feb 17: Funny Fists released in iOS App store and Google Play store. (YouTube video)
2014
May 25: R.U.B.E v1.6 released. Samplers!
May 12: "On-the-fly" resource updating for Cocos2d-x
Apr 22: New blog post: Inverted pendulum control
Apr 7: On-screen logging class for Cocos2d-x
Mar 22: Downhill Supreme 2 released for iOS and Android
Jan 2: YouTube video: Making soft-body wheels in RUBE
2013
Oct 6: Check out Supplyfront RTS, my 7dRTS entry continued.
Sep 26: R.U.B.E v1.5 released. Customizable item labels, snap-to-grid, export filtering, full-text help search.
Sep 18: Added RUBE sample loader for Cocos2d-X.
More...

Box2D tutorial topics


Box2D C++ tutorials - Introduction
Last edited: July 24 2013
Introduction
Setting up (Linux)
Setting up (Windows)
Halt! You should have a good understanding of
Setting up (Mac OSX) the basic tutorials before venturing further.
Testbed structure
Making a test
Bodies
Fixtures
Physics-driven particles
World settings In the anatomy of a collision topic we looked at how to get information about collisions. One piece of information was the
Forces and impulses contact point, where the impulse will be applied to separate the two fixtures that collided. This topic will look at how we can
Custom gravity use the relative velocity of the two fixtures at the contact point, combined with the friction between them, to generate
Moving at constant speed particles like smoke, dust, sparks etc.
Rotating to a given angle
Jumping
Using debug draw
Drawing your own objects
User data
Anatomy of a collision
Collision callbacks
Collision filtering
Sensors
Ray casting
World querying
Removing bodies safely
The 'can I jump' question
Ghost vertices The basics
Joints - overview
Joints - revolute We need to keep track of different sets of contacts, for each pair of material types. For this example we will have four
Joints - prismatic material types: steel, rubber, concrete and dirt. The combinations of these rubbing on each other will generate three particle
types: smoke, sparks and dirt. Even with only four material types, there are 10 possible combinations of material pairs, so
FAQ / gotchas this type of management can grow large very quickly. In this topic we will only generate particles in four cases:

Tutorial source code steel rubber concrete dirt


Advanced topics dirt dirt dirt - -

Conveyor belts concrete sparks smoke -


Projected trajectory rubber - -
Sticky projectiles
steel -
Hovercar suspension
Top-down car physics
The general procedure for keeping track of particle-generating contacts can be done like this:
One-way walls
Buoyancy have a set of contacts for each particle type (smoke, sparks, dirt)
Explosions when BeginContact occurs, add the contact to the appropriate set
when EndContact occurs, remove the contact from the set
Physics-driven particles
every time step, look at each contact in the set and check the relative velocity of the fixtures at the contact point to see if
particles should be generated
Other topics
Obviously that last point is the main focus of this topic, but we'll also look at how you could organize the contact listener to
Setting up (iPhone)
keep track of the contacting material pairs.

https://www.iforce2d.net/b2dtut/particles 1/5
9/4/2020 Introduction - Box2D tutorials - iforce2d
Sets of contacts

We need some type of list to store all the active contacts, for which I find std::set to be quite useful.

1 //references to currently active contacts


2 set<b2Contact*> m_steelToConcreteContacts;
3 set<b2Contact*> m_rubberToConcreteContacts;
4 set<b2Contact*> m_steelToDirtContacts;
5 set<b2Contact*> m_rubberToDirtContacts;

In this example, both the steelToDirt and rubberToDirt contacts will generate dirt particles using the same conditions (relative
velocity, friction etc) and the same characteristics (speed, direction, quantity etc), so we don't really need to keep separate
lists of contacts for them. However, if you wanted to do something different depending on what the two materials were, you
would need to keep separate lists like this.

Next we need to check in BeginContact/EndContact to see if each contact is one that we're interested in. For example if one
fixture is steel and the other is concrete, we'll add/remove that contact in m_steelToConcreteContacts. In the contact listener,
either one of fixtureA or fixtureB could be the steel, so we have to make checks like:

if (( A is steel and B is concrete ) or ( A is concrete and B is steel ))

For a large number of material combinations this can become quite messy and tedious, especially when the "is steel" part of
the check also involves a lot of code, and checking user data etc. There are various ways you could tackle this problem,
although none of them are very pleasant. You might like to read some discussion about this on Stack Overflow: How can I
track all of my Box2D collisions in a clean, manageable manner?

For this example I used a nasty pre-processor macro to help out, so I will mention that here. It is a handy solution, but don't
take it as a recommendation (it's only applicable for C/C++ anyway). First, a function is made for each material type, to
decide whether a fixture is made from that material:

1 bool fixtureIsSteel(b2Fixture* f) { ... }


2 bool fixtureIsConcrete(b2Fixture* f) { ... }
3 bool fixtureIsDirt(b2Fixture* f) { ... }
4 bool fixtureIsRubber(b2Fixture* f) { ... }

Of course, you would replace the ... with whatever you need to decide which material the fixture is, for example checking the
user data etc.

Now we can write a function to decide whether a contact is steelVsConcrete:

1 bool contactIsSteelVsConcrete(b2Contact* contact) {


2 b2Fixture* fA = contact->GetFixtureA();
3 b2Fixture* fB = contact->GetFixtureB();
4 if ( fixtureIsSteel(fA) && fixtureIsConcrete(fB) )
5 return true;
6 if ( fixtureIsSteel(fB) && fixtureIsConcrete(fA) )
7 return true;
8 return false;
9 }

The problem is, we need a function like this for every combination of material pairs, which is a lot of similar functions. Here's
where we can make use of the nasty pre-processor macro to do this for us. The original function can be made into a #define
with just the material names being substituted, and then that macro is used for each of the pairs we need:

1 #define CHECK_MAT_VS_MAT(mat1, mat2)\


2 bool contactIs##mat1##Vs##mat2(b2Contact* contact) {\
3 b2Fixture* fA = contact->GetFixtureA();\
4 b2Fixture* fB = contact->GetFixtureB();\
5 if ( fixtureIs##mat1(fA) && fixtureIs##mat2(fB) )\
6 return true;\
7 if ( fixtureIs##mat1(fB) && fixtureIs##mat2(fA) )\
8 return true;\
9 return false;\
10 }
11
12 CHECK_MAT_VS_MAT(Steel, Concrete)
13 CHECK_MAT_VS_MAT(Rubber, Concrete)
14 CHECK_MAT_VS_MAT(Steel, Dirt)
15 CHECK_MAT_VS_MAT(Rubber, Dirt)

The trade-off for using the nasty macro, is that now our BeginContact/EndContact functions are quite succinct:

1 void BeginContact(b2Contact* contact)


2 {
3 if ( contactIsSteelVsConcrete(contact) ) m_steelToConcreteContacts.insert(contact);
4 if ( contactIsRubberVsConcrete(contact) ) m_rubberToConcreteContacts.insert(contact);
5 if ( contactIsSteelVsDirt(contact) ) m_steelToDirtContacts.insert(contact);
6 if ( contactIsRubberVsDirt(contact) ) m_rubberToDirtContacts.insert(contact);
7 }
8
9 void EndContact(b2Contact* contact)
10 {
11 if ( contactIsSteelVsConcrete(contact) ) m_steelToConcreteContacts.erase(contact);
12 if ( contactIsRubberVsConcrete(contact) ) m_rubberToConcreteContacts.erase(contact);
13 if ( contactIsSteelVsDirt(contact) ) m_steelToDirtContacts.erase(contact);
14 if ( contactIsRubberVsDirt(contact) ) m_rubberToDirtContacts.erase(contact);
15 }

https://www.iforce2d.net/b2dtut/particles 2/5
9/4/2020 Introduction - Box2D tutorials - iforce2d

Generating particles

Now that we have a list of the current contacts for each pair of material types, we can check those contacts every time step
to see if there is enough movement and friction between them to generate some particles. If you're using std::set, the outer
part of the loop to do this would look like:

1 // replace "theSet" with eg. m_steelToConcreteContacts etc


2 for (set<b2Contact*>::iterator it = theSet.begin(); it != theSet.end(); ++it) {
3 b2Contact* contact = *it;
4 if ( contact->GetManifold()->pointCount < 1 )
5 continue;
6
7 // particle generation goes here
8 }

The particle generation will rely on having at least one point in the contact manifold, so the pointCount check is just there to
be safe.

Now, the main particle generation part goes like this: we get the contact point from the manifold, which is a point in world
coordinates. Then we use that point to find the velocity of each of the two fixtures at that point. Comparing those velocities
tells us how fast the fixtures are rubbing against each other. We also look at the friction of the two fixtures, to come up with
single number to represent a total umm... 'intensity' value. If the intensity is above a certain threshold, we generate a
particle, otherwise we don't.

1 b2Fixture* fA = contact->GetFixtureA();
2 b2Fixture* fB = contact->GetFixtureB();
3 b2Body* bA = fA->GetBody();
4 b2Body* bB = fB->GetBody();
5
6 // get the contact point in world coordinates
7 b2WorldManifold worldManifold;
8 contact->GetWorldManifold( &worldManifold );
9 b2Vec2 worldPoint = worldManifold.points[0];
10
11 // find the relative speed of the fixtures at that point
12 b2Vec2 velA = bA->GetLinearVelocityFromWorldPoint(worldPoint);
13 b2Vec2 velB = bB->GetLinearVelocityFromWorldPoint(worldPoint);
14 float relativeSpeed = (velA - velB).Length();
15
16 // overall friction of contact
17 float totalFriction = fA->GetFriction() * fB->GetFriction();
18
19 // check if this speed and friction is enough to generate particles
20 float intensity = relativeSpeed * totalFriction;
21 if ( intensity > threshold )
22 spawnParticle( worldPoint, velA, velB, intensity );

It's important to use the friction of the fixtures as well as their relative movement, because low friction surfaces should have
a lower intensity. You could think of this 'intensity' as the heat generated by rubbing two things together - low friction
surfaces would require a higher speed to generate the same heat as high friction surfaces.

The creation and management of particles is not really the subject of this tutorial. Much has already been written about
particle systems, and there are many ready-made solutions out there to use. The spawnParticle function in the code above is
just to show how you might use these values from the Box2D world to tell the particle system what to do.

In the accompanying source code for this topic, you can find a rudimentary particle system using this structure:

1 struct simpleParticle {
2 b2Body* body;
3 float life;
4 };

The particles are rendered as simple points, and properties such as linear damping, gravity scale, restitution etc are changed
for different particle behaviors. This has the benefit that the particles will interact with the physics world, and makes for less
coding in the sample code, but please don't take it as a recommendation.

Problems

As you may have noticed from the anatomy of a collision topic, the contact point is actually inside both of the fixtures
involved in the collision, like this:

https://www.iforce2d.net/b2dtut/particles 3/5
9/4/2020 Introduction - Box2D tutorials - iforce2d
Depending on what type of particles you are generating, this could be a problem. For example, in the sample source code for
this topic the particles are using Box2D bodies, and if the body for the particle starts embedded in another body, it can affect
the starting velocity. Or even worse, if one of the fixtures is an edge/chain fixture, the particle can start on the wrong side of
the chain and never be able to come back.

I can't think of a nice way to solve this, other than raycasting to find the desired surface, and making sure the particle starts
on the outside of it. For example in the case above, if the circle was a car tire you would want the particle to start on the
outside of the polygon fixture, so you could raycast from the circle center to the contact point to find an appropriate position:

(In the source code below, particles are only ever generated on the ground, so a raycast vertically downwards from the sky is
used to find the start position.)

Other considerations

This topic and the accompanying source code shows a really basic example of detecting where and how to generate particles.
There are so many ways you can tweak and improve things that you could spend all day on it, but this tutorial is mainly to
cover the Box2D part of the procedure. So I will just briefly mention some areas where improvements could be made:

both manifold points could be checked (for polygon vs polygon)


you could consider tangential relative velocity only
you could use a better friction mix, eg. b2MixFriction
you could use PostSolve to consider the pressure between the fixtures
different thresholds for each material pair

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.

Testbed test: iforce2d_physicsDrivenParticles.zip


*** To compile this, you will need to make the b2World::DrawShape function public ***

Linux 32-bit binary


Linux 64-bit binary
Windows binary
MacOSX binary

RUBE file for the test scene: physicsDrivenParticlesRUBEScene.zip

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/particles

https://www.iforce2d.net/b2dtut/particles 4/5
9/4/2020 Introduction - Box2D tutorials - iforce2d

Want to promote your Box2D game here for free?


Contact gmail 'iforce2d'
If you find this site useful please consider supporting it :)

https://www.iforce2d.net/b2dtut/particles 5/5

You might also like