Professional Documents
Culture Documents
Page 32
Page 32
Box2D tutorials | Cocos2d-x resources | Box2D JSON loader | R.U.B.E Box2D editor | R.U.B.E free | My apps | Blog ! | 日本語はこちら | Box2D中文教程
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.
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:
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:
Of course, you would replace the ... with whatever you need to decide which material the fixture is, for example checking the
user data etc.
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:
The trade-off for using the nasty macro, is that now our BeginContact/EndContact functions are quite succinct:
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:
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:
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.
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
https://www.iforce2d.net/b2dtut/particles 5/5