Home > TutorialsTechniques
4.4. Particle System - Missile 
In this tutorial you will further use the effects framework of BetaCell; in this case, the effect is a missile.



Click here to go to the forum discussion of this tutorial


 

4.4.1. Prerequisites
default.jpg
Before you start this tutorial make sure that you have:
  • Installed XNA Game Studio 3.1
  • Downloaded the starter project from the downloads section
  • Successfully run the starter project
  • Finish tutorial 4.1.
 

4.4.2. Game Assets 
Game assets [2.3.2] for this tutorial:

 

4.4.3. The using statements
Besides the using statements from the starter project, this tutorial needs the following using statements:

using BetaCell.Environment.Particles;
using BetaCell.Environment.Texture;
using BetaCell.Environment.Particles.Initializers;
using BetaCell.Util.RenderStates;
using BetaCell.Environment.Texture.Feeder;
using BetaCell.Environment.Particles.Parts;


 

4.4.4. Game Attributes 
The attributes that come with the starter project are explained in tutorial section 1.1.4.

For this tutorial we are going to be needing the following attributes:

BCParticleSystem particleSystem;
BCTraceInitializer traceInitializer;


 

4.4.5. The Initialize method 

The first worth mentioning part of the initialize method is the use of a different initializer:

traceInitializer = new BCTraceInitializer(
    2.5f, new Color(192, 192, 192, 64),
    10, 15,
    0.01f, 0.1f,
    new Vector4(-0.1f, 0, 0, 0), new Vector4(0.1f, 0, 0, 0),
    new Vector4(0, 0, 0, 1), new Vector4(0, 0, 0, 1)
);

The parameters are:
  1. The time in seconds that a particle will live
  2. The initial color of the particles
  3. The minimum size in pixels of a particle
  4. The maximum size in pixels of a particle
  5. The minimum custom
  6. The maximum custom
  7. The last known position of the initializer
  8. The new position of the initializer
The other part worth mentioning is the use of the BCSubstractAlphaRenderStrategy. What it does is to make darker the spots where two or more particles are drawn at the same time.


 

Advertisement
 

4.4.7. The Update method
The only new part of the update method updates the position of the trace initializer.

Vector4 newPos = traceInitializer.currentPosition;
if (newPos.X > 1)
{
    newPos.X = 0;
    newPos.Y = 0;
    newPos.Z = 0;
    newPos.W = 1;
}
else
{
    newPos.X += 0.005f;
    newPos.Y += 0.005f;
    newPos.Z += 0.005f;
    newPos.W = 1;
}
traceInitializer.update(newPos);


default.jpg
The difference between a trace and a fixed initializer can be seen in the figure. The trace initializer is shown in the upper part, each frame lasts 1/60 seconds but the time between each particle's emission is much faster (six times faster in this example), so per frame the rocket advances some distance and emits 6 particles. If we use a fixed initializer and update it's position, all six particles would be emitted in the same spot as shown in the lower part of the figure. On the other hand, on the top part of the image, the trace initializer disperse the particles across the distance that has been traveled by the rocket. 

 

4.4.6. The Draw method
The method disables the cull mode, draws the particle system and enables back the cull:

CullMode currentCullMode = device.RenderState.CullMode;
device.RenderState.CullMode = CullMode.None;
particleSystem.Draw(gameTime);
device.RenderState.CullMode = currentCullMode;

 

4.4.7. Conclusion
Look how with a very little change, you achieved a different particle effect, try adding other parts to see the behavior.

Click here to go to the forum discussion of this tutorial
 
 

Advertisement
 

4.4.8. Complete source code
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using BetaCell.Debug;
using BetaCell.Util.GlobalInfo.Content;
using BetaCell.Environment.Camera;
using BetaCell.Util.GlobalInfo;


using BetaCell.Environment.Particles;
using BetaCell.Environment.Texture;
using BetaCell.Environment.Particles.Initializers;
using BetaCell.Util.RenderStates;
using BetaCell.Environment.Texture.Feeder;
using BetaCell.Environment.Particles.Parts;

namespace Starter
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Game attributes
        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        ContentManager content;

        BCFirstPersonHumanCamera camera;
        //end game attributes

        //This tutorial's attributes
        BCParticleSystem particleSystem;
        BCTraceInitializer traceInitializer;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;
            content = new ContentManager(Services);
#if(XBOX360)
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 768;
#else
            graphics.PreferredBackBufferWidth = 852;
            graphics.PreferredBackBufferHeight = 480;
#endif
        }

        protected override void Initialize()
        {
            //base initialization
            device = graphics.GraphicsDevice;

            BCLogger.instance.init(device, content);
            BCInitializationManager.initialize(content);

            OnActivated(null, null);

            //-----
            //Particle system
            //-----

            BCMemoryTextureStrategy smoke = new BCMemoryTextureStrategy(
                content.Load<Texture2D>("Content/gameAssets/smoke"));

            traceInitializer = new BCTraceInitializer(
                2.5f, new Color(192, 192, 192, 64),
                10, 15,
                0.01f, 0.1f,
                new Vector4(-0.1f, 0, 0, 0), new Vector4(0.1f, 0, 0, 0),
                new Vector4(0, 0, 0, 1), new Vector4(0, 0, 0, 1)
            );

            particleSystem = new BCParticleSystem(
                device,
                smoke,
                0,
                null,
                150,
                0.01f,
                "Particles.Base",
                traceInitializer,
                new BCSubstractAlphaRenderStrategy()
            );

            BCSampler sampler = new BCSampler(
                "LINEAR", "LINEAR", "POINT", "4",
                "CLAMP", "CLAMP", "0xffffffff");

            BCParticleDistanceResizePart resizeDistance =
                new BCParticleDistanceResizePart("Particles.Parts.DistanceResize", 800);
            resizeDistance.linearDecimator = 1;

            BCParticlePow2ResizePart pow2Resize =
                new BCParticlePow2ResizePart("Particles.Parts.ParticlePow2Resize", 100);

            BCParticleFade fade = new BCParticleFade("Particles.Parts.Fade");

            particleSystem.initEffect(device, "Shader.Texture.Sprite", sampler,
                pow2Resize, resizeDistance, fade);

            base.Initialize();
        }

        protected override void OnActivated(object sender, EventArgs args)
        {
            buildViewMatrix();
            base.OnActivated(sender, args);
        }

        void buildViewMatrix()
        {
            Vector3 pos = new Vector3(0, 0, -4);
            Vector3 look = new Vector3(0, 0, 1);
            look.Normalize();

            camera = new BCFirstPersonHumanCamera(look, pos,
                MathHelper.PiOver4,
                (float)this.Window.ClientBounds.Width / (float)this.Window.ClientBounds.Height,
                1f, 200);

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.VIEW_INDEX,
                camera.getViewMatrix(-1)
            );

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.PROJECTION_INDEX,
                camera.getProjectionMatrix(-1)
            );
        }

        protected override void Update(GameTime gameTime)
        {
            Vector4 newPos = traceInitializer.currentPosition;
            if (newPos.X > 1)
            {
                newPos.X = 0;
                newPos.Y = 0;
                newPos.Z = 0;
                newPos.W = 1;
            }
            else
            {
                newPos.X += 0.005f;
                newPos.Y += 0.005f;
                newPos.Z += 0.005f;
                newPos.W = 1;
            }
            traceInitializer.update(newPos);

            particleSystem.update((float)gameTime.TotalGameTime.TotalSeconds);

            moveCamera(gameTime);
        }

        private void moveCamera(GameTime gameTime)
        {

            float df = 0;
            float ds = 0;
            float dy = 0;
            float pitch = 0;
            float angleY = 0;

            float speed = 5f;
#if (XBOX360)
            GamePadState gamepad = GamePad.GetState(PlayerIndex.One);

            df = gamepad.ThumbSticks.Left.Y;
            ds = gamepad.ThumbSticks.Left.X;
            pitch = -gamepad.ThumbSticks.Right.Y * 0.05f;
            angleY = gamepad.ThumbSticks.Right.X * 0.05f;

            if (gamepad.IsButtonDown(Buttons.RightShoulder))
            {
                dy += 0.25f;
            }
            if (gamepad.IsButtonDown(Buttons.LeftShoulder))
            {
                dy -= 0.25f;
            }

            speed = new Vector3(df, ds, dy).Length() * 10;
#else

            KeyboardState keys = Keyboard.GetState();
            MouseState mouse = Mouse.GetState();

            if (keys.IsKeyDown(Keys.W))
            {
                df += 1;
            }
            if (keys.IsKeyDown(Keys.S))
            {
                df -= 1;
            }
            if (keys.IsKeyDown(Keys.D))
            {
                ds += 1;
            }
            if (keys.IsKeyDown(Keys.A))
            {
                ds -= 1;
            }
            if (keys.IsKeyDown(Keys.Q))
            {
                dy += 1;
            }
            if (keys.IsKeyDown(Keys.E))
            {
                dy -= 1;
            }
            speed = 5f;
            if (keys.IsKeyDown(Keys.LeftControl))
            {
                speed = 10f;
            }

            angleY = ((float)this.Window.ClientBounds.Width / 2.0f - (float)mouse.X) * -0.05f;
            pitch = ((float)this.Window.ClientBounds.Height / 2.0f - (float)mouse.Y) * -0.05f;

            Mouse.SetPosition(this.Window.ClientBounds.Width / 2, this.Window.ClientBounds.Height / 2);

#endif
            camera.update(df, dy, ds, pitch, angleY, speed, (float)gameTime.ElapsedGameTime.TotalSeconds);

            BCGlobalInfo.instance.setMatrix(BCGlobalInfo.VIEW_INDEX, camera.getViewMatrix(-1));
        }

        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);

            CullMode currentCullMode = device.RenderState.CullMode;
            device.RenderState.CullMode = CullMode.None;
            particleSystem.Draw(gameTime);
            device.RenderState.CullMode = currentCullMode;

            BCLogger.instance.printFPS(gameTime);
            BCLogger.instance.flush();
            base.Draw(gameTime);
        }
    }
}