If there's one thing I really appreciate, it's explosions that have a visceral and almost palpable presence in a game.
Much of the presence of an explosion tends to come from the more subtle in-game artifacts, such as a deep, complex, sound design and shaking of the camera, and for a lot of games this is more than enough. But sometimes, something that is a little more satisfying is required visually, such as the rolling fireball covered in this recipe and pictured in the following illustration:
Getting ready
In this special effect, you'll need a textured sphere with a flame or lava appearance.
In the following code, there are references to a GeometricBuffer class that can be substituted with any other sort of mesh container. You can find the details of how to construct this kind of mesh programmatically in Chapter 3, Procedural Modeling.
A textured sphere mesh from a modeling package would work equally well.
How to do it...
To create an explosion within the Reach profile:
As with other particle-based effects, we begin with a new particle class:
class ReachExplosionParticle
{
Start the new class by adding some instance variables to hold its position, age, and other details:
public Microsoft.Xna.Framework.Matrix World;
public float Size;
public float HorizontalAngle;
public float HorizontalOffset;
public float VerticleOffset;
public float Roll;
public float Age;
public float AgeDelta;
Next, define the class that will render each particle:
class ReachExplosionParticleDisplay
{
Add some instance-level variables to hold the mesh, effect, and lifetime details of the particle displayed:
And completing the class, we come to rendering the particles onscreen:
public void Draw(
Matrix view,
Matrix projection,
GameTime gameTime)
{
if (!exploding)
{
return;
}
foreach (var particle in particles)
{
if (particle.Age > 1)
{
continue;
}
particleDisplay.Draw(
view,
projection,
particle.World,
particle.Age);
}
}
To complete the example, add a factory class to create a new instance of the particle mesh. Here's an example using the GeometricBuffer classes from Chapter 3, Procedural Modeling:
class ReachExplosionParticleDisplayFactory
{
public static ReachExplosionParticleDisplay Create(GraphicsDevice graphicsDevice, ContentManager content)
{
var sphereEffect = new BasicEffect(graphicsDevice)
{
SpecularColor = Color.Black.ToVector3(),
DiffuseColor = Color.White.ToVector3(),
Texture = content.Load<Texture2D>("lava"),
TextureEnabled = true
};
var factory = new VertexPositionNormalTextureGeometricBufferFactory();
var radius = 1f;
var vStep = -MathHelper.Pi / 8f;
var uStep = -MathHelper.TwoPi / 8f;
for (var v = MathHelper.PiOver2;
v > -MathHelper.PiOver2;
v += vStep)
{
var nextV = v + vStep;
var vY = radius * (float)Math.Sin(v);
var nextVY = (float)Math.Sin(nextV);
var bandRadius = radius * (float)Math.Cos(v);
var nextBandRadius = radius *
(float)Math.Cos(nextV);
var top = new Vector3(
bandRadius,
vY,
bandRadius);
var bottom = new Vector3(
nextBandRadius,
nextVY,
nextBandRadius);
for (var u = MathHelper.Pi;
u > -MathHelper.Pi;
u += uStep)
{
var nextU = u + uStep;
var uX = (float)Math.Sin(u);
var nextUX = (float)Math.Sin(nextU);
var uZ = (float)Math.Cos(u);
var nextUZ = (float)Math.Cos(nextU);
var right = new Vector3(uX, 1f, uZ);
var left = new Vector3(nextUX, 1f, nextUZ);
var topLeft = top * left;
var topRight = top * right;
var bottomRight = bottom * right;
var bottomLeft = bottom * left;
var textureLeft = (float)(
(nextU + MathHelper.Pi) /
MathHelper.TwoPi);
var textureRight = (float)(
(u + MathHelper.Pi) /
MathHelper.TwoPi);
var textureTop = 1f - (float)(
(v + MathHelper.PiOver2) /
Math.PI);
var textureBottom = 1f - (float)(
(nextV + MathHelper.PiOver2) /
Math.PI);
var topLeftNormal =
Vector3.Normalize(topLeft);
var topRightNormal =
Vector3.Normalize(topRight);
var bottomRightNormal =
Vector3.Normalize(bottomRight);
var bottomLeftNormal =
Vector3.Normalize(bottomLeft);
factory.AddPane(
topLeft,
new Vector2(textureLeft, textureTop),
topLeftNormal,
topRight,
new Vector2(textureRight, textureTop),
topRightNormal,
bottomRight,
new Vector2(textureRight, textureBottom),
bottomRightNormal,
bottomLeft,
new Vector2(textureLeft, textureBottom),
bottomLeftNormal);
}
}
var sphereBuffer = factory.Create(graphicsDevice);
sphereBuffer.IsTextureTransparent = true;
return new ReachExplosionParticleDisplay(
sphereBuffer, sphereEffect);
}
}
How it works...
The heart of the ReachExplosion class lies with the use of the XNA Curve class.
Via the Curve class, we can take the relatively random nature of the particle stream and mold it into the visually satisfying shape of a gaseous explosion.
Two instances of the Curve class are created within the constructor of the ReachExplosion class.
The first curve determines the shape of the explosion by expanding or contracting the distance between each particle and the center of the explosion. The second curve determines how quickly the particles rise.
Another two instances of the Curve class can be observed in the ReachExplosionParticleDisplay class controlling the luminescence and transparency of each particle, thereby simulating the particle's transition from flame, to smoke, to thin air.
There's more...
Just like in film, the difference between a convincing explosion and one less so can usually be found not in the explosion itself, but in the impact it has on the world around it.
A deep bass explosion sound along with a small shake of the virtual camera, possibly also achieved via the Curve class, would go a long way towards enhancing the realism of this effect.