Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Microsoft XNA 4.0 Game Development Cookbook

You're reading from   Microsoft XNA 4.0 Game Development Cookbook This book goes further than the basic manuals to help you exploit Microsoft XNA to create fantastic virtual worlds and effects in your 2D or 3D games. Includes 35 essential recipes for game developers.

Arrow left icon
Product type Paperback
Published in Jun 2012
Publisher Packt
ISBN-13 9781849691987
Length 356 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Luke Drumm Luke Drumm
Author Profile Icon Luke Drumm
Luke Drumm
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Microsoft XNA 4.0 Game Development Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
1. Preface
1. Applying Special Effects FREE CHAPTER 2. Building 2D and 3D Terrain 3. Procedural Modeling 4. Creating Water and Sky 5. Non-Player Characters 6. Playing with Animation 7. Creating Vehicles 8. Receiving Player Input 9. Networking

Creating explosions within the Reach profile


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:

  1. 1. As with other particle-based effects, we begin with a new particle class:

    class ReachExplosionParticle
    {
    
  2. 2. 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;
    
  3. 3. Next, define the class that will render each particle:

    class ReachExplosionParticleDisplay
    {
    
  4. 4. Add some instance-level variables to hold the mesh, effect, and lifetime details of the particle displayed:

    GeometricBuffer<VertexPositionNormalTexture> sphereBuffer;
    BasicEffect sphereEffect;
    Curve alphaCurve;
    Curve lightCurve;
    
  5. 5. Create a constructor to initialize everything:

    public ReachExplosionParticleDisplay(
    GeometricBuffer<VertexPositionNormalTexture> sphereBuffer,
    BasicEffect sphereEffect)
    {
    this.sphereBuffer = sphereBuffer;
    this.sphereEffect = sphereEffect;
    alphaCurve = new Curve();
    alphaCurve.Keys.Add(new CurveKey(0, 0.75f));
    alphaCurve.Keys.Add(new CurveKey(0.125f, 0.5f));
    alphaCurve.Keys.Add(new CurveKey(0.35f, 0.125f));
    alphaCurve.Keys.Add(new CurveKey(1f, 0f));
    lightCurve = new Curve();
    lightCurve.Keys.Add(new CurveKey(0, 1f));
    lightCurve.Keys.Add(new CurveKey(0.2f, 1f));
    lightCurve.Keys.Add(new CurveKey(1f, 0.25f));
    }
    
  6. 6. Add the Draw() method to render the particles and finish the class:

    internal void Draw(
    Matrix view,
    Matrix projection,
    Matrix world,
    float age)
    {
    sphereEffect.View = view;
    sphereEffect.Projection = projection;
    sphereEffect.World = world;
    sphereEffect.Alpha = alphaCurve.Evaluate(age);
    sphereEffect.DiffuseColor =
    Vector3.One * lightCurve.Evaluate(age);
    sphereBuffer.Draw(sphereEffect);
    }
    
  7. 7. Create a new class to orchestrate all the particles and their displays:

    class ReachExplosion
    {
    
  8. 8. Add the instance-level variables to hold the display renderer, the particles, and some animation settings:

    ReachExplosionParticleDisplay particleDisplay;
    List<ReachExplosionParticle> particles;
    Random random = new Random();
    Curve horizontalRateCurve;
    Curve verticleRateCurve;
    private bool exploding;
    
  9. 9. Initialize everything in a constructor:

    public ReachExplosion(
    GraphicsDevice graphicsDevice,
    ContentManager content)
    {
    particleDisplay = ReachExplosionParticleDisplayFactory.Create(
    graphicsDevice, content);
    particles = new List<ReachExplosionParticle>();
    for (var index = 0; index < 100; index++)
    {
    particles.Add(new ReachExplosionParticle());
    }
    horizontalRateCurve = new Curve();
    horizontalRateCurve.Keys.Add(new CurveKey(0, 0f));
    horizontalRateCurve.Keys.Add(new CurveKey(0.025f, 0.8f));
    horizontalRateCurve.Keys.Add(new CurveKey(0.25f, 1f));
    verticleRateCurve = new Curve();
    verticleRateCurve.Keys.Add(new CurveKey(0.2f, 0.1f));
    verticleRateCurve.Keys.Add(new CurveKey(0.3f, 0.25f));
    }
    
  10. 10. Create a variable and a method to indicate when things should start exploding:

    public void Explode()
    {
    foreach (var particle in particles)
    {
    Reset(particle);
    }
    exploding = true;
    }
    
  11. 11. Add the ability to reset particles back to an initial position ready to explode:

    private void Reset(ReachExplosionParticle particle)
    {
    particle.Size = (float)random.NextDouble() * 0.2f;
    particle.HorizontalAngle = (float)random.NextDouble() *
    MathHelper.TwoPi;
    particle.HorizontalOffset = (float)random.NextDouble() *
    0.5f;
    particle.Roll = ((0.4f *
    (float)random.NextDouble()) +
    0.6f) * 2f * MathHelper.TwoPi;
    particle.VerticleOffset = (0.2f *
    (float)random.NextDouble());
    particle.Age = 0f;
    particle.AgeDelta = ((0.6f *
    (float)random.NextDouble()) + 0.4f);
    }
    
  12. 12. Update the state of all the particles through the addition of an Update() method:

    public void Update(GameTime gameTime)
    {
    if (!exploding)
    {
    return;
    }
    var liveParticleCount = 0;
    foreach (var particle in particles)
    {
    if (particle.Age > 1)
    {
    continue;
    }
    particle.Age += particle.AgeDelta *
    (float)gameTime.ElapsedGameTime.TotalSeconds;
    particle.VerticleOffset +=
    verticleRateCurve.Evaluate(particle.Age) *
    (float)gameTime.ElapsedGameTime.TotalSeconds *
    5f;
    particle.Roll +=
    (float)gameTime.ElapsedGameTime.TotalSeconds;
    var horizontalOffset =
    horizontalRateCurve.Evaluate(particle.Age) *
    particle.HorizontalOffset *
    Vector3.Backward;
    var verticleOffset = Vector3.Up *
    particle.VerticleOffset;
    particle.World = Matrix.CreateScale(particle.Size) *
    Matrix.CreateRotationX(particle.Roll) *
    Matrix.CreateTranslation(horizontalOffset) *
    Matrix.CreateRotationY(particle.HorizontalAngle) *
    Matrix.CreateTranslation(verticleOffset);
    liveParticleCount++;
    }
    exploding = liveParticleCount > 0;
    }
    
  13. 13. 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);
    }
    }
    
  14. 14. 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.

You have been reading a chapter from
Microsoft XNA 4.0 Game Development Cookbook
Published in: Jun 2012
Publisher: Packt
ISBN-13: 9781849691987
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image