Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Particle System Sample

git-svn-id: https://xnatouch.svn.codeplex.com/svn@47986 ffd33b8c-2492-42e0-bdc5-587b920b7d6d
  • Loading branch information...
commit 67f70c5b4b135a015728931d58db9cebc944553f 1 parent 6e8473f
SND\CartBlanche_cp authored
View
BIN  Samples/ParticleSample1/Content/Arial.xnb
Binary file not shown
View
BIN  Samples/ParticleSample1/Content/explosion.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Samples/ParticleSample1/Content/smoke.bmp
Binary file not shown
View
BIN  Samples/ParticleSample1/Default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
105 Samples/ParticleSample1/ExplosionParticleSystem.cs
@@ -0,0 +1,105 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ExplosionParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+
+#endregion
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// ExplosionParticleSystem is a specialization of ParticleSystem which creates a
+ /// fiery explosion. It should be combined with ExplosionSmokeParticleSystem for
+ /// best effect.
+ /// </summary>
+ public class ExplosionParticleSystem : ParticleSystem
+ {
+ public ExplosionParticleSystem(ParticleSampleGame game, int howManyEffects)
+ : base(game, howManyEffects)
+ {
+ }
+
+ /// <summary>
+ /// Set up the constants that will give this particle system its behavior and
+ /// properties.
+ /// </summary>
+ protected override void InitializeConstants()
+ {
+ textureFilename = "explosion";
+
+ // high initial speed with lots of variance. make the values closer
+ // together to have more consistently circular explosions.
+ minInitialSpeed = 40;
+ maxInitialSpeed = 500;
+
+ // doesn't matter what these values are set to, acceleration is tweaked in
+ // the override of InitializeParticle.
+ minAcceleration = 0;
+ maxAcceleration = 0;
+
+ // explosions should be relatively short lived
+ minLifetime = .5f;
+ maxLifetime = 1.0f;
+
+ minScale = .3f;
+ maxScale = 1.0f;
+
+ minNumParticles = 20;
+ maxNumParticles = 25;
+
+ minRotationSpeed = -MathHelper.PiOver4;
+ maxRotationSpeed = MathHelper.PiOver4;
+
+ // additive blending is very good at creating fiery effects.
+ spriteBlendMode = SpriteBlendMode.Additive;
+
+ DrawOrder = AdditiveDrawOrder;
+ }
+
+ protected override void InitializeParticle(Particle p, Vector2 where)
+ {
+ base.InitializeParticle(p, where);
+
+ // The base works fine except for acceleration. Explosions move outwards,
+ // then slow down and stop because of air resistance. Let's change
+ // acceleration so that when the particle is at max lifetime, the velocity
+ // will be zero.
+
+ // We'll use the equation vt = v0 + (a0 * t). (If you're not familar with
+ // this, it's one of the basic kinematics equations for constant
+ // acceleration, and basically says:
+ // velocity at time t = initial velocity + acceleration * t)
+ // We'll solve the equation for a0, using t = p.Lifetime and vt = 0.
+ p.Acceleration = -p.Velocity / p.Lifetime;
+ }
+ }
+}
View
90 Samples/ParticleSample1/ExplosionSmokeParticleSystem.cs
@@ -0,0 +1,90 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ExplosionSmokeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+#endregion
+
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// ExplosionSmokeParticleSystem is a specialization of ParticleSystem which
+ /// creates a circular pattern of smoke. It should be combined with
+ /// ExplosionParticleSystem for best effect.
+ /// </summary>
+ public class ExplosionSmokeParticleSystem : ParticleSystem
+ {
+ public ExplosionSmokeParticleSystem(ParticleSampleGame game, int howManyEffects)
+ : base(game, howManyEffects)
+ {
+ }
+
+ /// <summary>
+ /// Set up the constants that will give this particle system its behavior and
+ /// properties.
+ /// </summary>
+ protected override void InitializeConstants()
+ {
+ textureFilename = "smoke";
+
+ // less initial speed than the explosion itself
+ minInitialSpeed = 20;
+ maxInitialSpeed = 200;
+
+ // acceleration is negative, so particles will accelerate away from the
+ // initial velocity. this will make them slow down, as if from wind
+ // resistance. we want the smoke to linger a bit and feel wispy, though,
+ // so we don't stop them completely like we do ExplosionParticleSystem
+ // particles.
+ minAcceleration = -10;
+ maxAcceleration = -50;
+
+ // explosion smoke lasts for longer than the explosion itself, but not
+ // as long as the plumes do.
+ minLifetime = 1.0f;
+ maxLifetime = 2.5f;
+
+ minScale = 1.0f;
+ maxScale = 2.0f;
+
+ minNumParticles = 10;
+ maxNumParticles = 20;
+
+ minRotationSpeed = -MathHelper.PiOver4;
+ maxRotationSpeed = MathHelper.PiOver4;
+
+ spriteBlendMode = SpriteBlendMode.AlphaBlend;
+
+ DrawOrder = AlphaBlendDrawOrder;
+ }
+ }
+}
View
BIN  Samples/ParticleSample1/GameThumbnail.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
5 Samples/ParticleSample1/Info.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict />
+</plist>
View
49 Samples/ParticleSample1/Main.cs
@@ -0,0 +1,49 @@
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MonoTouch.Foundation;
+using MonoTouch.UIKit;
+
+namespace ParticleSample
+{
+ #region Entry Point
+#if IPHONE
+ [Register("AppDelegate")]
+ class Program : UIApplicationDelegate
+ {
+ public override void FinishedLaunching(UIApplication app)
+ {
+ // Fun begins..
+ using (ParticleSampleGame game = new ParticleSampleGame())
+ {
+ game.Run();
+ }
+ }
+
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ static void Main(string[] args)
+ {
+ UIApplication.Main(args, null, "AppDelegate");
+ }
+ }
+#else
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ static class Program
+ {
+ static void Main()
+ {
+ using (ParticleSampleGame game = new ParticleSampleGame())
+ {
+ game.Run();
+ }
+ }
+ }
+#endif
+ #endregion
+}
+
View
134 Samples/ParticleSample1/Particle.cs
@@ -0,0 +1,134 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Particle.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+#endregion
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// particles are the little bits that will make up an effect. each effect will
+ /// be comprised of many of these particles. They have basic physical properties,
+ /// such as position, velocity, acceleration, and rotation. They'll be drawn as
+ /// sprites, all layered on top of one another, and will be very pretty.
+ /// </summary>
+ public class Particle
+ {
+ // Position, Velocity, and Acceleration represent exactly what their names
+ // indicate. They are public fields rather than properties so that users
+ // can directly access their .X and .Y properties.
+ public Vector2 Position;
+ public Vector2 Velocity;
+ public Vector2 Acceleration;
+
+ // how long this particle will "live"
+ private float lifetime;
+ public float Lifetime
+ {
+ get { return lifetime; }
+ set { lifetime = value; }
+ }
+
+ // how long it has been since initialize was called
+ private float timeSinceStart;
+ public float TimeSinceStart
+ {
+ get { return timeSinceStart; }
+ set { timeSinceStart = value; }
+ }
+
+ // the scale of this particle
+ private float scale;
+ public float Scale
+ {
+ get { return scale; }
+ set { scale = value; }
+ }
+
+ // its rotation, in radians
+ private float rotation;
+ public float Rotation
+ {
+ get { return rotation; }
+ set { rotation = value; }
+ }
+
+ // how fast does it rotate?
+ private float rotationSpeed;
+ public float RotationSpeed
+ {
+ get { return rotationSpeed; }
+ set { rotationSpeed = value; }
+ }
+
+ // is this particle still alive? once TimeSinceStart becomes greater than
+ // Lifetime, the particle should no longer be drawn or updated.
+ public bool Active
+ {
+ get { return TimeSinceStart < Lifetime; }
+ }
+
+
+ // initialize is called by ParticleSystem to set up the particle, and prepares
+ // the particle for use.
+ public void Initialize(Vector2 position, Vector2 velocity, Vector2 acceleration,
+ float lifetime, float scale, float rotationSpeed)
+ {
+ // set the values to the requested values
+ this.Position = position;
+ this.Velocity = velocity;
+ this.Acceleration = acceleration;
+ this.Lifetime = lifetime;
+ this.Scale = scale;
+ this.RotationSpeed = rotationSpeed;
+
+ // reset TimeSinceStart - we have to do this because particles will be
+ // reused.
+ this.TimeSinceStart = 0.0f;
+
+ // set rotation to some random value between 0 and 360 degrees.
+ this.Rotation = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
+ }
+
+ // update is called by the ParticleSystem on every frame. This is where the
+ // particle's position and that kind of thing get updated.
+ public void Update(float dt)
+ {
+ Velocity += Acceleration * dt;
+ Position += Velocity * dt;
+
+ Rotation += RotationSpeed * dt;
+
+ TimeSinceStart += dt;
+ }
+ }
+}
View
312 Samples/ParticleSample1/ParticleSampleGame.cs
@@ -0,0 +1,312 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleSampleGame.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+#endregion
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// This is the main type for the ParticleSample, and inherits from the Framework's
+ /// Game class. It creates three different kinds of ParticleSystems, and then adds
+ /// them to its components collection. It also has keeps a random number generator,
+ /// a SpriteBatch, and a ContentManager that the different classes in this sample
+ /// can share.
+ /// </summary>
+ public class ParticleSampleGame : Game
+ {
+ #region Fields and Properties
+
+ GraphicsDeviceManager graphics;
+
+ // The particle systems will all need a SpriteBatch to draw their particles,
+ // so let's make one they can share. We'll use this to draw our SpriteFont
+ // too.
+ SpriteBatch spriteBatch;
+ public SpriteBatch SpriteBatch
+ {
+ get { return spriteBatch; }
+ }
+
+ // Used to draw the instructions on the screen.
+ SpriteFont arialFont;
+
+ // a random number generator that the whole sample can share.
+ private static Random random = new Random();
+ public static Random Random
+ {
+ get { return random; }
+ }
+
+ // Here's the really fun part of the sample, the particle systems! These are
+ // drawable game components, so we can just add them to the components
+ // collection. Read more about each particle system in their respective source
+ // files.
+ ExplosionParticleSystem explosion;
+ ExplosionSmokeParticleSystem smoke;
+ SmokePlumeParticleSystem smokePlume;
+
+ // State is an enum that represents which effect we're currently demoing.
+ enum State
+ {
+ Explosions,
+ SmokePlume
+ };
+ // the number of values in the "State" enum.
+ const int NumStates = 2;
+ State currentState = State.Explosions;
+
+ // a timer that will tell us when it's time to trigger another explosion.
+ const float TimeBetweenExplosions = 2.0f;
+ float timeTillExplosion = 0.0f;
+
+ // keep a timer that will tell us when it's time to add more particles to the
+ // smoke plume.
+ const float TimeBetweenSmokePlumePuffs = .5f;
+ float timeTillPuff = 0.0f;
+
+ // keep track of the last frame's keyboard and gamepad state, so that we know
+ // if the user has pressed a button.
+ KeyboardState lastKeyboardState;
+ GamePadState lastGamepadState;
+
+ #endregion
+
+ #region Initialization
+
+ public ParticleSampleGame()
+ {
+ graphics = new GraphicsDeviceManager(this);
+
+ graphics.PreferredBackBufferWidth = 320;
+ graphics.PreferredBackBufferHeight = 480;
+
+ Content.RootDirectory = "Content";
+
+ // create the particle systems and add them to the components list.
+ // we should never see more than one explosion at once
+ explosion = new ExplosionParticleSystem(this, 1);
+ Components.Add(explosion);
+
+ // but the smoke from the explosion lingers a while.
+ smoke = new ExplosionSmokeParticleSystem(this, 2);
+ Components.Add(smoke);
+
+ // we'll see lots of these effects at once; this is ok
+ // because they have a fairly small number of particles per effect.
+ smokePlume = new SmokePlumeParticleSystem(this, 9);
+ Components.Add(smokePlume);
+ }
+
+ /// <summary>
+ /// Load your graphics content.
+ /// </summary>
+ protected override void LoadContent()
+ {
+ spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
+ arialFont = Content.Load<SpriteFont>("Arial");
+ }
+
+ #endregion
+
+ #region Update and Draw
+
+ /// <summary>
+ /// Allows the game to run logic such as updating the world,
+ /// checking for collisions, gathering input and playing audio.
+ /// </summary>
+ /// <param name="gameTime">Provides a snapshot of timing values.</param>
+ protected override void Update(GameTime gameTime)
+ {
+ // check the input devices to see if someone has decided they want to see
+ // the other effect, if they want to quit.
+ HandleInput();
+
+ float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+ switch (currentState)
+ {
+ // if we should be demoing the explosions effect, check to see if it's
+ // time for a new explosion.
+ case State.Explosions:
+ UpdateExplosions(dt);
+ break;
+ // if we're showing off the smoke plume, check to see if it's time for a
+ // new puff of smoke.
+ case State.SmokePlume:
+ UpdateSmokePlume(dt);
+ break;
+ }
+
+ // the base update will handle updating the particle systems themselves,
+ // because we added them to the components collection.
+ base.Update(gameTime);
+ }
+
+ // this function is called when we want to demo the smoke plume effect. it
+ // updates the timeTillPuff timer, and adds more particles to the plume when
+ // necessary.
+ private void UpdateSmokePlume(float dt)
+ {
+ timeTillPuff -= dt;
+ if (timeTillPuff < 0)
+ {
+ Vector2 where = Vector2.Zero;
+ // add more particles at the bottom of the screen, halfway across.
+ where.X = graphics.GraphicsDevice.Viewport.Width / 2;
+ where.Y = graphics.GraphicsDevice.Viewport.Height;
+ smokePlume.AddParticles(where);
+
+ // and then reset the timer.
+ timeTillPuff = TimeBetweenSmokePlumePuffs;
+ }
+ }
+
+ // this function is called when we want to demo the explosion effect. it
+ // updates the timeTillExplosion timer, and starts another explosion effect
+ // when the timer reaches zero.
+ private void UpdateExplosions(float dt)
+ {
+ timeTillExplosion -= dt;
+ if (timeTillExplosion < 0)
+ {
+ Vector2 where = Vector2.Zero;
+ // create the explosion at some random point on the screen.
+ where.X = RandomBetween(0, graphics.GraphicsDevice.Viewport.Width);
+ where.Y = RandomBetween(0, graphics.GraphicsDevice.Viewport.Height);
+
+ // the overall explosion effect is actually comprised of two particle
+ // systems: the fiery bit, and the smoke behind it. add particles to
+ // both of those systems.
+ explosion.AddParticles(where);
+ smoke.AddParticles(where);
+
+ // reset the timer.
+ timeTillExplosion = TimeBetweenExplosions;
+ }
+ }
+
+ /// <summary>
+ /// This is called when the game should draw itself.
+ /// </summary>
+ /// <param name="gameTime">Provides a snapshot of timing values.</param>
+ protected override void Draw(GameTime gameTime)
+ {
+ graphics.GraphicsDevice.Clear(Color.Black);
+
+ spriteBatch.Begin();
+
+ // draw some instructions on the screen
+ string message = string.Format("Touch the Screen\nto switch effects.\n\n" +
+ "Current effect:\n {0}!\n\n" +
+ "Free particles:\n" +
+ "Explosion: {1}\n" +
+ "ExplosionSmoke:{2}\n" +
+ "SmokePlume: {3}",
+ currentState, explosion.FreeParticleCount,
+ smoke.FreeParticleCount, smokePlume.FreeParticleCount );
+ spriteBatch.DrawString(arialFont, message, new Vector2(10, 10), Color.White);
+
+ spriteBatch.End();
+
+ base.Draw(gameTime);
+ }
+
+ // This function will check to see if the user has just pushed the A button or
+ // the space bar. If so, we should go to the next effect.
+ private void HandleInput()
+ {
+ KeyboardState currentKeyboardState = Keyboard.GetState();
+ GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
+ TouchCollection currentTouchCollection = TouchPanel.GetState();
+
+ // Allows the game to exit
+ if (currentGamePadState.Buttons.Back == ButtonState.Pressed ||
+ currentKeyboardState.IsKeyDown(Keys.Escape))
+ this.Exit();
+
+
+ // check to see if someone has just released the space bar.
+ bool keyboardSpace =
+ currentKeyboardState.IsKeyUp(Keys.Space) &&
+ lastKeyboardState.IsKeyDown(Keys.Space);
+
+
+ // check the gamepad to see if someone has just released the A button.
+ bool gamepadA =
+ currentGamePadState.Buttons.A == ButtonState.Pressed &&
+ lastGamepadState.Buttons.A == ButtonState.Released;
+
+ bool touched = false;
+
+ // tap the screen to select
+ foreach (TouchLocation location in currentTouchCollection)
+ {
+ switch (location.State)
+ {
+ case TouchLocationState.Pressed:
+ touched = true;
+ break;
+ case TouchLocationState.Moved:
+ break;
+ case TouchLocationState.Released:
+ break;
+ }
+ }
+
+
+ // if either the A button or the space bar was just released, move to the
+ // next state. Doing modulus by the number of states lets us wrap back
+ // around to the first state.
+ if (keyboardSpace || gamepadA || touched)
+ {
+ currentState = (State)((int)(currentState + 1) % NumStates);
+ }
+
+ lastKeyboardState = currentKeyboardState;
+ lastGamepadState = currentGamePadState;
+ }
+
+ #endregion
+
+ #region Helper Functions
+
+ // a handy little function that gives a random float between two
+ // values. This will be used in several places in the sample, in particilar in
+ // ParticleSystem.InitializeParticle.
+ public static float RandomBetween(float min, float max)
+ {
+ return min + (float)random.NextDouble() * (max - min);
+ }
+
+ #endregion
+ }
+}
View
386 Samples/ParticleSample1/ParticleSystem.cs
@@ -0,0 +1,386 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// SmokePlumeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+#endregion
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// ParticleSystem is an abstract class that provides the basic functionality to
+ /// create a particle effect. Different subclasses will have different effects,
+ /// such as fire, explosions, and plumes of smoke. To use these subclasses,
+ /// simply call AddParticles, and pass in where the particles should exist
+ /// </summary>
+ public abstract class ParticleSystem : DrawableGameComponent
+ {
+ // these two values control the order that particle systems are drawn in.
+ // typically, particles that use additive blending should be drawn on top of
+ // particles that use regular alpha blending. ParticleSystems should therefore
+ // set their DrawOrder to the appropriate value in InitializeConstants, though
+ // it is possible to use other values for more advanced effects.
+ public const int AlphaBlendDrawOrder = 100;
+ public const int AdditiveDrawOrder = 200;
+
+ // a reference to the main game; we'll keep this around because it exposes a
+ // content manager and a sprite batch for us to use.
+ private ParticleSampleGame game;
+
+ // the texture this particle system will use.
+ private Texture2D texture;
+
+ // the origin when we're drawing textures. this will be the middle of the
+ // texture.
+ private Vector2 origin;
+
+ // this number represents the maximum number of effects this particle system
+ // will be expected to draw at one time. this is set in the constructor and is
+ // used to calculate how many particles we will need.
+ private int howManyEffects;
+
+ // the array of particles used by this system. these are reused, so that calling
+ // AddParticles will not cause any allocations.
+ Particle[] particles;
+
+ // the queue of free particles keeps track of particles that are not curently
+ // being used by an effect. when a new effect is requested, particles are taken
+ // from this queue. when particles are finished they are put onto this queue.
+ Queue<Particle> freeParticles;
+ /// <summary>
+ /// returns the number of particles that are available for a new effect.
+ /// </summary>
+ public int FreeParticleCount
+ {
+ get { return freeParticles.Count; }
+ }
+
+
+ // This region of values control the "look" of the particle system, and should
+ // be set by deriving particle systems in the InitializeConstants method. The
+ // values are then used by the virtual function InitializeParticle. Subclasses
+ // can override InitializeParticle for further
+ // customization.
+ #region constants to be set by subclasses
+
+ /// <summary>
+ /// minNumParticles and maxNumParticles control the number of particles that are
+ /// added when AddParticles is called. The number of particles will be a random
+ /// number between minNumParticles and maxNumParticles.
+ /// </summary>
+ protected int minNumParticles;
+ protected int maxNumParticles;
+
+ /// <summary>
+ /// this controls the texture that the particle system uses. It will be used as
+ /// an argument to ContentManager.Load.
+ /// </summary>
+ protected string textureFilename;
+
+ /// <summary>
+ /// minInitialSpeed and maxInitialSpeed are used to control the initial velocity
+ /// of the particles. The particle's initial speed will be a random number
+ /// between these two. The direction is determined by the function
+ /// PickRandomDirection, which can be overriden.
+ /// </summary>
+ protected float minInitialSpeed;
+ protected float maxInitialSpeed;
+
+ /// <summary>
+ /// minAcceleration and maxAcceleration are used to control the acceleration of
+ /// the particles. The particle's acceleration will be a random number between
+ /// these two. By default, the direction of acceleration is the same as the
+ /// direction of the initial velocity.
+ /// </summary>
+ protected float minAcceleration;
+ protected float maxAcceleration;
+
+ /// <summary>
+ /// minRotationSpeed and maxRotationSpeed control the particles' angular
+ /// velocity: the speed at which particles will rotate. Each particle's rotation
+ /// speed will be a random number between minRotationSpeed and maxRotationSpeed.
+ /// Use smaller numbers to make particle systems look calm and wispy, and large
+ /// numbers for more violent effects.
+ /// </summary>
+ protected float minRotationSpeed;
+ protected float maxRotationSpeed;
+
+ /// <summary>
+ /// minLifetime and maxLifetime are used to control the lifetime. Each
+ /// particle's lifetime will be a random number between these two. Lifetime
+ /// is used to determine how long a particle "lasts." Also, in the base
+ /// implementation of Draw, lifetime is also used to calculate alpha and scale
+ /// values to avoid particles suddenly "popping" into view
+ /// </summary>
+ protected float minLifetime;
+ protected float maxLifetime;
+
+ /// <summary>
+ /// to get some additional variance in the appearance of the particles, we give
+ /// them all random scales. the scale is a value between minScale and maxScale,
+ /// and is additionally affected by the particle's lifetime to avoid particles
+ /// "popping" into view.
+ /// </summary>
+ protected float minScale;
+ protected float maxScale;
+
+ /// <summary>
+ /// different effects can use different blend modes. fire and explosions work
+ /// well with additive blending, for example.
+ /// </summary>
+ protected SpriteBlendMode spriteBlendMode;
+
+ #endregion
+
+ /// <summary>
+ /// Constructs a new ParticleSystem.
+ /// </summary>
+ /// <param name="game">The host for this particle system. The game keeps the
+ /// content manager and sprite batch for us.</param>
+ /// <param name="howManyEffects">the maximum number of particle effects that
+ /// are expected on screen at once.</param>
+ /// <remarks>it is tempting to set the value of howManyEffects very high.
+ /// However, this value should be set to the minimum possible, because
+ /// it has a large impact on the amount of memory required, and slows down the
+ /// Update and Draw functions.</remarks>
+ protected ParticleSystem(ParticleSampleGame game, int howManyEffects)
+ : base(game)
+ {
+ this.game = game;
+ this.howManyEffects = howManyEffects;
+ }
+
+ /// <summary>
+ /// override the base class's Initialize to do some additional work; we want to
+ /// call InitializeConstants to let subclasses set the constants that we'll use.
+ ///
+ /// also, the particle array and freeParticles queue are set up here.
+ /// </summary>
+ public override void Initialize()
+ {
+ InitializeConstants();
+
+ // calculate the total number of particles we will ever need, using the
+ // max number of effects and the max number of particles per effect.
+ // once these particles are allocated, they will be reused, so that
+ // we don't put any pressure on the garbage collector.
+ particles = new Particle[howManyEffects * maxNumParticles];
+ freeParticles = new Queue<Particle>(howManyEffects * maxNumParticles);
+ for (int i = 0; i < particles.Length; i++)
+ {
+ particles[i] = new Particle();
+ freeParticles.Enqueue(particles[i]);
+ }
+ base.Initialize();
+ }
+
+ /// <summary>
+ /// this abstract function must be overriden by subclasses of ParticleSystem.
+ /// It's here that they should set all the constants marked in the region
+ /// "constants to be set by subclasses", which give each ParticleSystem its
+ /// specific flavor.
+ /// </summary>
+ protected abstract void InitializeConstants();
+
+ /// <summary>
+ /// Override the base class LoadContent to load the texture. once it's
+ /// loaded, calculate the origin.
+ /// </summary>
+ protected override void LoadContent()
+ {
+ // make sure sub classes properly set textureFilename.
+ if (string.IsNullOrEmpty(textureFilename))
+ {
+ string message = "textureFilename wasn't set properly, so the " +
+ "particle system doesn't know what texture to load. Make " +
+ "sure your particle system's InitializeConstants function " +
+ "properly sets textureFilename.";
+ throw new InvalidOperationException(message);
+ }
+ // load the texture....
+ texture = game.Content.Load<Texture2D>(textureFilename);
+
+ // ... and calculate the center. this'll be used in the draw call, we
+ // always want to rotate and scale around this point.
+ origin.X = texture.Width / 2;
+ origin.Y = texture.Height / 2;
+
+ base.LoadContent();
+ }
+
+ /// <summary>
+ /// AddParticles's job is to add an effect somewhere on the screen. If there
+ /// aren't enough particles in the freeParticles queue, it will use as many as
+ /// it can. This means that if there not enough particles available, calling
+ /// AddParticles will have no effect.
+ /// </summary>
+ /// <param name="where">where the particle effect should be created</param>
+ public void AddParticles(Vector2 where)
+ {
+ // the number of particles we want for this effect is a random number
+ // somewhere between the two constants specified by the subclasses.
+ int numParticles =
+ ParticleSampleGame.Random.Next(minNumParticles, maxNumParticles);
+
+ // create that many particles, if you can.
+ for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
+ {
+ // grab a particle from the freeParticles queue, and Initialize it.
+ Particle p = freeParticles.Dequeue();
+ InitializeParticle(p, where);
+ }
+ }
+
+ /// <summary>
+ /// InitializeParticle randomizes some properties for a particle, then
+ /// calls initialize on it. It can be overriden by subclasses if they
+ /// want to modify the way particles are created. For example,
+ /// SmokePlumeParticleSystem overrides this function make all particles
+ /// accelerate to the right, simulating wind.
+ /// </summary>
+ /// <param name="p">the particle to initialize</param>
+ /// <param name="where">the position on the screen that the particle should be
+ /// </param>
+ protected virtual void InitializeParticle(Particle p, Vector2 where)
+ {
+ // first, call PickRandomDirection to figure out which way the particle
+ // will be moving. velocity and acceleration's values will come from this.
+ Vector2 direction = PickRandomDirection();
+
+ // pick some random values for our particle
+ float velocity =
+ ParticleSampleGame.RandomBetween(minInitialSpeed, maxInitialSpeed);
+ float acceleration =
+ ParticleSampleGame.RandomBetween(minAcceleration, maxAcceleration);
+ float lifetime =
+ ParticleSampleGame.RandomBetween(minLifetime, maxLifetime);
+ float scale =
+ ParticleSampleGame.RandomBetween(minScale, maxScale);
+ float rotationSpeed =
+ ParticleSampleGame.RandomBetween(minRotationSpeed, maxRotationSpeed);
+
+ // then initialize it with those random values. initialize will save those,
+ // and make sure it is marked as active.
+ p.Initialize(
+ where, velocity * direction, acceleration * direction,
+ lifetime, scale, rotationSpeed);
+ }
+
+ /// <summary>
+ /// PickRandomDirection is used by InitializeParticles to decide which direction
+ /// particles will move. The default implementation is a random vector in a
+ /// circular pattern.
+ /// </summary>
+ protected virtual Vector2 PickRandomDirection()
+ {
+ float angle = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
+ return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
+ }
+
+ /// <summary>
+ /// overriden from DrawableGameComponent, Update will update all of the active
+ /// particles.
+ /// </summary>
+ public override void Update(GameTime gameTime)
+ {
+ // calculate dt, the change in the since the last frame. the particle
+ // updates will use this value.
+ float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+ // go through all of the particles...
+ foreach (Particle p in particles)
+ {
+
+ if (p.Active)
+ {
+ // ... and if they're active, update them.
+ p.Update(dt);
+ // if that update finishes them, put them onto the free particles
+ // queue.
+ if (!p.Active)
+ {
+ freeParticles.Enqueue(p);
+ }
+ }
+ }
+
+ base.Update(gameTime);
+ }
+
+ /// <summary>
+ /// overriden from DrawableGameComponent, Draw will use ParticleSampleGame's
+ /// sprite batch to render all of the active particles.
+ /// </summary>
+ public override void Draw(GameTime gameTime)
+ {
+ // tell sprite batch to begin, using the spriteBlendMode specified in
+ // initializeConstants
+ game.SpriteBatch.Begin(spriteBlendMode);
+
+ foreach (Particle p in particles)
+ {
+ // skip inactive particles
+ if (!p.Active)
+ continue;
+
+ // normalized lifetime is a value from 0 to 1 and represents how far
+ // a particle is through its life. 0 means it just started, .5 is half
+ // way through, and 1.0 means it's just about to be finished.
+ // this value will be used to calculate alpha and scale, to avoid
+ // having particles suddenly appear or disappear.
+ float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
+
+ // we want particles to fade in and fade out, so we'll calculate alpha
+ // to be (normalizedLifetime) * (1-normalizedLifetime). this way, when
+ // normalizedLifetime is 0 or 1, alpha is 0. the maximum value is at
+ // normalizedLifetime = .5, and is
+ // (normalizedLifetime) * (1-normalizedLifetime)
+ // (.5) * (1-.5)
+ // .25
+ // since we want the maximum alpha to be 1, not .25, we'll scale the
+ // entire equation by 4.
+ float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
+ Color color = new Color(new Vector4(1, 1, 1, alpha));
+
+ // make particles grow as they age. they'll start at 75% of their size,
+ // and increase to 100% once they're finished.
+ float scale = p.Scale * (.75f + .25f * normalizedLifetime);
+
+ game.SpriteBatch.Draw(texture, p.Position, null, color,
+ p.Rotation, origin, scale, SpriteEffects.None, 0.0f);
+ }
+
+ game.SpriteBatch.End();
+
+ base.Draw(gameTime);
+ }
+ }
+}
View
120 Samples/ParticleSample1/SmokePlumeParticleSystem.cs
@@ -0,0 +1,120 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// SmokePlumeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+#if IPHONE
+using XnaTouch.Framework;
+using XnaTouch.Framework.Audio;
+using XnaTouch.Framework.GamerServices;
+using XnaTouch.Framework.Graphics;
+using XnaTouch.Framework.Input;
+using XnaTouch.Framework.Storage;
+using XnaTouch.Framework.Content;
+using XnaTouch.Framework.Media;
+#else
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Media;
+#endif
+#endregion
+
+namespace ParticleSample
+{
+ /// <summary>
+ /// SmokePlumeParticleSystem is a specialization of ParticleSystem which sends up a
+ /// plume of smoke. The smoke is blown to the right by the wind.
+ /// </summary>
+ public class SmokePlumeParticleSystem : ParticleSystem
+ {
+ public SmokePlumeParticleSystem(ParticleSampleGame game, int howManyEffects)
+ : base(game,howManyEffects)
+ {
+ }
+
+ /// <summary>
+ /// Set up the constants that will give this particle system its behavior and
+ /// properties.
+ /// </summary>
+ protected override void InitializeConstants()
+ {
+ textureFilename = "smoke";
+
+ minInitialSpeed = 20;
+ maxInitialSpeed = 100;
+
+ // we don't want the particles to accelerate at all, aside from what we
+ // do in our overriden InitializeParticle.
+ minAcceleration = 0;
+ maxAcceleration = 0;
+
+ // long lifetime, this can be changed to create thinner or thicker smoke.
+ // tweak minNumParticles and maxNumParticles to complement the effect.
+ minLifetime = 5.0f;
+ maxLifetime = 7.0f;
+
+ minScale = .5f;
+ maxScale = 1.0f;
+
+ minNumParticles = 7;
+ maxNumParticles = 15;
+
+ // rotate slowly, we want a fairly relaxed effect
+ minRotationSpeed = -MathHelper.PiOver4 / 2.0f;
+ maxRotationSpeed = MathHelper.PiOver4 / 2.0f;
+
+ spriteBlendMode = SpriteBlendMode.AlphaBlend;
+
+ DrawOrder = AlphaBlendDrawOrder;
+ }
+
+ /// <summary>
+ /// PickRandomDirection is overriden so that we can make the particles always
+ /// move have an initial velocity pointing up.
+ /// </summary>
+ /// <returns>a random direction which points basically up.</returns>
+ protected override Vector2 PickRandomDirection()
+ {
+ // Point the particles somewhere between 80 and 100 degrees.
+ // tweak this to make the smoke have more or less spread.
+ float radians = ParticleSampleGame.RandomBetween(
+ MathHelper.ToRadians(80), MathHelper.ToRadians(100));
+
+ Vector2 direction = Vector2.Zero;
+ // from the unit circle, cosine is the x coordinate and sine is the
+ // y coordinate. We're negating y because on the screen increasing y moves
+ // down the monitor.
+ direction.X = (float)Math.Cos(radians);
+ direction.Y = -(float)Math.Sin(radians);
+ return direction;
+ }
+
+ /// <summary>
+ /// InitializeParticle is overridden to add the appearance of wind.
+ /// </summary>
+ /// <param name="p">the particle to set up</param>
+ /// <param name="where">where the particle should be placed</param>
+ protected override void InitializeParticle(Particle p, Vector2 where)
+ {
+ base.InitializeParticle(p, where);
+
+ // the base is mostly good, but we want to simulate a little bit of wind
+ // heading to the right.
+ p.Acceleration.X += ParticleSampleGame.RandomBetween(10, 50);
+ }
+ }
+}
View
121 Samples/ParticleSample1/XnaTouch.Samples.ParticleSample.csproj
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{4D0C3C5D-B4A5-4C6F-8756-4A05C50ECC3A}</ProjectGuid>
+ <ProjectTypeGuids>{E613F3A2-FE9C-494F-B74E-F63BCB86FEA6};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>XnaTouch.Samples.ParticleSample</RootNamespace>
+ <MtouchSdkVersion>3.0</MtouchSdkVersion>
+ <MtouchMinimumOS>3.0</MtouchMinimumOS>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <BundleDisplayName>ParticleSample</BundleDisplayName>
+ <BundleIcon>GameThumbnail.png</BundleIcon>
+ <BundleIdentifier>project.MonoGame.ParticleSample</BundleIdentifier>
+ <AssemblyName>ParticleSample</AssemblyName>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
+ <DefineConstants>DEBUG;IPHONE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <MtouchDebug>True</MtouchDebug>
+ <CodesignKey>iPhone Developer</CodesignKey>
+ <MtouchLink>None</MtouchLink>
+ <MtouchI18n />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\iPhoneSimulator\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <MtouchDebug>False</MtouchDebug>
+ <CodesignKey>iPhone Developer</CodesignKey>
+ <MtouchLink>None</MtouchLink>
+ <DefineConstants>IPHONE</DefineConstants>
+ <MtouchI18n />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\iPhone\Debug</OutputPath>
+ <DefineConstants>DEBUG;IPHONE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <MtouchDebug>True</MtouchDebug>
+ <CodesignKey>iPhone Developer</CodesignKey>
+ <MtouchLink>None</MtouchLink>
+ <MtouchI18n />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\iPhone\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <MtouchDebug>False</MtouchDebug>
+ <CodesignKey>iPhone Developer</CodesignKey>
+ <DefineConstants>IPHONE</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Distribution|iPhone' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin</OutputPath>
+ <WarningLevel>4</WarningLevel>
+ <MtouchDebug>False</MtouchDebug>
+ <CodesignKey>iPhone Developer</CodesignKey>
+ <MtouchI18n />
+ <DefineConstants>IPHONE</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Core" />
+ <Reference Include="monotouch" />
+ <Reference Include="OpenTK" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Main.cs" />
+ <Compile Include="ExplosionParticleSystem.cs" />
+ <Compile Include="ExplosionSmokeParticleSystem.cs" />
+ <Compile Include="Particle.cs" />
+ <Compile Include="ParticleSampleGame.cs" />
+ <Compile Include="ParticleSystem.cs" />
+ <Compile Include="SmokePlumeParticleSystem.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Content\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ProjectExtensions>
+ <MonoDevelop>
+ <Properties InternalTargetFrameworkVersion="IPhone" />
+ </MonoDevelop>
+ </ProjectExtensions>
+ <ItemGroup>
+ <Content Include="Default.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="GameThumbnail.png" />
+ <None Include="Info.plist" />
+ <None Include="Content\Arial.xnb" />
+ <None Include="Content\explosion.png" />
+ <None Include="Content\smoke.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\XnaTouch.Framework\XnaTouch.Framework.csproj">
+ <Project>{DB8508BB-9849-4CC2-BC0F-8EB5DACB3C47}</Project>
+ <Name>XnaTouch.Framework</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
Please sign in to comment.
Something went wrong with that request. Please try again.