#region File Description
//-----------------------------------------------------------------------------
// Game.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Particle3DSample
{
///
/// Sample showing how to implement a particle system entirely
/// on the GPU, using the vertex shader to animate particles.
///
public class Particle3DSampleGame : Microsoft.Xna.Framework.Game
{
#region Fields
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont font;
//Model grid;
// This sample uses five different particle systems.
ParticleSystem explosionParticles;
ParticleSystem explosionSmokeParticles;
ParticleSystem projectileTrailParticles;
ParticleSystem smokePlumeParticles;
ParticleSystem fireParticles;
// The sample can switch between three different visual effects.
enum ParticleState
{
Explosions,
SmokePlume,
RingOfFire,
};
ParticleState currentState = ParticleState.Explosions;
// The explosions effect works by firing projectiles up into the
// air, so we need to keep track of all the active projectiles.
List projectiles = new List ();
TimeSpan timeToNextProjectile = TimeSpan.Zero;
// Random number generator for the fire effect.
Random random = new Random ();
// Input state.
KeyboardState currentKeyboardState;
GamePadState currentGamePadState;
KeyboardState lastKeyboardState;
GamePadState lastGamePadState;
// Camera state.
float cameraArc = -5;
float cameraRotation = 0;
float cameraDistance = 200;
#endregion
#region Initialization
///
/// Constructor.
///
public Particle3DSampleGame ()
{
graphics = new GraphicsDeviceManager (this);
Content.RootDirectory = "Content";
// Construct our particle system components.
explosionParticles = new ParticleSystem (this, Content, "ExplosionSettings");
explosionSmokeParticles = new ParticleSystem (this, Content, "ExplosionSmokeSettings");
projectileTrailParticles = new ParticleSystem (this, Content, "ProjectileTrailSettings");
smokePlumeParticles = new ParticleSystem (this, Content, "SmokePlumeSettings");
fireParticles = new ParticleSystem (this, Content, "FireSettings");
// Set the draw order so the explosions and fire
// will appear over the top of the smoke.
smokePlumeParticles.DrawOrder = 100;
explosionSmokeParticles.DrawOrder = 200;
projectileTrailParticles.DrawOrder = 300;
explosionParticles.DrawOrder = 400;
fireParticles.DrawOrder = 500;
// Register the particle system components.
Components.Add (explosionParticles);
Components.Add (explosionSmokeParticles);
Components.Add (projectileTrailParticles);
Components.Add (smokePlumeParticles);
Components.Add (fireParticles);
}
///
/// Load your graphics content.
///
protected override void LoadContent ()
{
spriteBatch = new SpriteBatch (graphics.GraphicsDevice);
//font = Content.Load ("Arial");
font = Content.Load ("font");
//grid = Content.Load ("grid");
}
#endregion
#region Update and Draw
///
/// Allows the game to run logic.
///
protected override void Update (GameTime gameTime)
{
HandleInput ();
UpdateCamera (gameTime);
switch (currentState) {
case ParticleState.Explosions:
UpdateExplosions (gameTime);
break;
case ParticleState.SmokePlume:
UpdateSmokePlume ();
break;
case ParticleState.RingOfFire:
UpdateFire ();
break;
}
UpdateProjectiles (gameTime);
base.Update (gameTime);
}
///
/// Helper for updating the explosions effect.
///
void UpdateExplosions (GameTime gameTime)
{
timeToNextProjectile -= gameTime.ElapsedGameTime;
if (timeToNextProjectile <= TimeSpan.Zero) {
// Create a new projectile once per second. The real work of moving
// and creating particles is handled inside the Projectile class.
projectiles.Add (new Projectile (explosionParticles,
explosionSmokeParticles,
projectileTrailParticles));
timeToNextProjectile += TimeSpan.FromSeconds (1);
}
}
///
/// Helper for updating the list of active projectiles.
///
void UpdateProjectiles (GameTime gameTime)
{
int i = 0;
while (i < projectiles.Count) {
if (!projectiles [i].Update (gameTime)) {
// Remove projectiles at the end of their life.
projectiles.RemoveAt (i);
} else {
// Advance to the next projectile.
i++;
}
}
}
///
/// Helper for updating the smoke plume effect.
///
void UpdateSmokePlume ()
{
// This is trivial: we just create one new smoke particle per frame.
smokePlumeParticles.AddParticle (Vector3.Zero, Vector3.Zero);
}
///
/// Helper for updating the fire effect.
///
void UpdateFire ()
{
const
int fireParticlesPerFrame = 20 ;
// Create a number of fire particles, randomly positioned around a circle.
for (int i = 0; i < fireParticlesPerFrame; i++) {
fireParticles.AddParticle (RandomPointOnCircle (), Vector3.Zero);
}
// Create one smoke particle per frmae, too.
smokePlumeParticles.AddParticle (RandomPointOnCircle (), Vector3.Zero);
}
///
/// Helper used by the UpdateFire method. Chooses a random location
/// around a circle, at which a fire particle will be created.
///
Vector3 RandomPointOnCircle ()
{
const
float radius = 30 ;
const
float height = 40 ;
double angle = random.NextDouble () * Math.PI * 2;
float x = (float)Math.Cos (angle);
float y = (float)Math.Sin (angle);
return new Vector3 (x * radius, y * radius + height, 0);
}
///
/// This is called when the game should draw itself.
///
protected override void Draw (GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.Clear (Color.CornflowerBlue);
// Compute camera matrices.
float aspectRatio = (float)device.Viewport.Width /
(float)device.Viewport.Height;
Matrix view = Matrix.CreateTranslation (0, -25, 0) *
Matrix.CreateRotationY (MathHelper.ToRadians (cameraRotation)) *
Matrix.CreateRotationX (MathHelper.ToRadians (cameraArc)) *
Matrix.CreateLookAt (new Vector3 (0, 0, -cameraDistance),
new Vector3 (0, 0, 0), Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView (MathHelper.PiOver4,
aspectRatio,
1, 10000);
// Pass camera matrices through to the particle system components.
explosionParticles.SetCamera (view, projection);
explosionSmokeParticles.SetCamera (view, projection);
projectileTrailParticles.SetCamera (view, projection);
smokePlumeParticles.SetCamera (view, projection);
fireParticles.SetCamera (view, projection);
// Draw our background grid and message text.
DrawGrid (view, projection);
DrawMessage ();
// This will draw the particle system components.
base.Draw (gameTime);
}
///
/// Helper for drawing the background grid model.
///
void DrawGrid (Matrix view, Matrix projection)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
device.SamplerStates [0] = SamplerState.LinearWrap;
//grid.Draw (Matrix.Identity, view, projection);
}
///
/// Helper for drawing our message text.
///
void DrawMessage ()
{
string message = string.Format ("Current effect: {0}!!!\n" +
"Hit the A button or space bar to switch.",
currentState);
spriteBatch.Begin ();
spriteBatch.DrawString (font, message, new Vector2 (50, 50), Color.White);
spriteBatch.End ();
}
#endregion
#region Handle Input
///
/// Handles input for quitting the game and cycling
/// through the different particle effects.
///
void HandleInput ()
{
lastKeyboardState = currentKeyboardState;
lastGamePadState = currentGamePadState;
currentKeyboardState = Keyboard.GetState ();
currentGamePadState = GamePad.GetState (PlayerIndex.One);
// Check for exit.
if (currentKeyboardState.IsKeyDown (Keys.Escape) ||
currentGamePadState.Buttons.Back == ButtonState.Pressed) {
Exit ();
}
// Check for changing the active particle effect.
if (((currentKeyboardState.IsKeyDown (Keys.Space) &&
(lastKeyboardState.IsKeyUp (Keys.Space))) ||
((currentGamePadState.Buttons.A == ButtonState.Pressed)) &&
(lastGamePadState.Buttons.A == ButtonState.Released))) {
currentState++;
if (currentState > ParticleState.RingOfFire)
currentState = 0;
}
}
///
/// Handles input for moving the camera.
///
void UpdateCamera (GameTime gameTime)
{
float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
// Check for input to rotate the camera up and down around the model.
if (currentKeyboardState.IsKeyDown (Keys.Up) ||
currentKeyboardState.IsKeyDown (Keys.W)) {
cameraArc += time * 0.025f;
}
if (currentKeyboardState.IsKeyDown (Keys.Down) ||
currentKeyboardState.IsKeyDown (Keys.S)) {
cameraArc -= time * 0.025f;
}
cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.05f;
// Limit the arc movement.
if (cameraArc > 90.0f)
cameraArc = 90.0f;
else if (cameraArc < -90.0f)
cameraArc = -90.0f;
// Check for input to rotate the camera around the model.
if (currentKeyboardState.IsKeyDown (Keys.Right) ||
currentKeyboardState.IsKeyDown (Keys.D)) {
cameraRotation += time * 0.05f;
}
if (currentKeyboardState.IsKeyDown (Keys.Left) ||
currentKeyboardState.IsKeyDown (Keys.A)) {
cameraRotation -= time * 0.05f;
}
cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.1f;
// Check for input to zoom camera in and out.
if (currentKeyboardState.IsKeyDown (Keys.Z))
cameraDistance += time * 0.25f;
if (currentKeyboardState.IsKeyDown (Keys.X))
cameraDistance -= time * 0.25f;
cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;
// Limit the camera distance.
if (cameraDistance > 500)
cameraDistance = 500;
else if (cameraDistance < 10)
cameraDistance = 10;
if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
currentKeyboardState.IsKeyDown (Keys.R)) {
cameraArc = -5;
cameraRotation = 0;
cameraDistance = 200;
}
}
#endregion
}
#region Entry Point
// ///
// /// The main entry point for the application.
// ///
// static class Program
// {
// static void Main()
// {
// using (Particle3DSampleGame game = new Particle3DSampleGame())
// {
// game.Run();
// }
// }
// }
#endregion
}