Sistema de Partículas 2D – XNA


En este Post voy a mostrar como crear un Sistema de Partículas que puede ser usado y modificado en cualquier juego. Con esto podemos simular efectos de Fuego, Humo, Fuegos pirotécnicos, Nieve, entre otras partículas que se nos puedan ocurrir.

Como Funciona:

Un Sistema de Partículas tiene 2 componentes, la partícula en sí y el Emisor que va a ser el origen de las partículas, la partícula es un pequeño punto en el juego, y al juntar varias partículas con diferentes posiciones, velocidades y rotaciones, se crea un gran efecto, también tienen un tiempo de Vida que es el tiempo en que durará la partícula en el juego.

Clase Partícula:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Particulas
{
 /// <summary>
 /// Clase que respresenta las particulas
 /// </summary>
 public class Particula
 {
 public Texture2D Textura { get; set; }
 public Vector2 Posicion { get; set; }
 public Vector2 Velocidad { get; set; }
 public float Angulo { get; set; }
 public float VelocidadAngular { get; set; }
 public Color Color { get; set; }
 public float Tamano { get; set; }
 public Int16 TiempodeVida { get; set; }

 /// <summary>
 /// Constructor de la clase
 /// </summary>
 /// <param name="textura"></param>
 /// <param name="posicion"></param>
 /// <param name="velocidad"></param>
 /// <param name="angulo"></param>
 /// <param name="velocidadAngular"></param>
 /// <param name="color"></param>
 /// <param name="tamano"></param>
 /// <param name="tiempodeVida"></param>
 public Particula(Texture2D textura, Vector2 posicion, Vector2 velocidad, float angulo, float velocidadAngular,
 Color color, float tamano, Int16 tiempodeVida)
 {
 Textura = textura;
 Posicion = posicion;
 Velocidad = velocidad;
 VelocidadAngular = velocidadAngular;
 Angulo = angulo;
 Color = color;
 Tamano = tamano;
 TiempodeVida = tiempodeVida;
 }

 /// <summary>
 /// Disminuye el tiempo de Vida, y aumenta la Posición y la Velocidad Angular para la rotación
 /// </summary>
 public void Update()
 {
 TiempodeVida--;
 Posicion += Velocidad;
 Angulo += VelocidadAngular;
 }

 /// <summary>
 /// Dibuja cada partícula
 /// </summary>
 /// <param name="spriteBatch"></param>
 public void Draw(SpriteBatch spriteBatch)
 {
 Rectangle rectanguloFuente = new Rectangle(0,0,Textura.Width, Textura.Height);
 Vector2 origen = new Vector2(Textura.Width / 2, Textura.Height / 2);
 spriteBatch.Draw(Textura, Posicion, rectanguloFuente, Color, Angulo, origen, Tamano, SpriteEffects.None, 0f);
 }
 }
}

La clase Partícula tiene unas propiedades que servirán para el tamaño, velocidad, posición, rotación, tiempo de Vida, Color, Ángulo.

El método Update va a disminuir el tiempo de Vida, y a aumentar la velocidad y Posición.

El método Draw será el encargado de dibujar cada partícula

Clase Motor de Partículas:


using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Particulas
{
 public class MotorParticulas
 {
 private Random random;
 public Vector2 PosicionEmisor { get; set; }
 public List<Particula> particulas;
 private List<Texture2D> texturas;
 public Boolean aleatorias = false;

 /// <summary>
 /// Constructor de la clase
 /// </summary>
 /// <param name="texturas"></param>
 /// <param name="posicion"></param>
 public MotorParticulas(List<Texture2D> texturas, Vector2 posicion)
 {
 PosicionEmisor = posicion;
 this.texturas = texturas;
 this.particulas = new List<Particula>();
 random = new Random();
 }

 /// <summary>
 /// Genera partículas con propiedades aleatorias
 /// </summary>
 /// <returns></returns>
private Particula GenerarParticulasAleatorias()
{
Texture2D textura = texturas[random.Next(texturas.Count)];
Vector2 posicion = PosicionEmisor;
Vector2 velocidad = new Vector2(1f * (float)(random.NextDouble() * 2 - 1),
1f * (float)(random.NextDouble() * 2 - 1));
float angulo = 0;
float velocidadangular = 0.1f * (float)(random.NextDouble() * 2 - 1);
Color color;
if (aleatorias)
{
color = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
}
else
{
color = Color.White;
}
float tamano = (float)random.NextDouble();
Int16 tiempodeVida = Convert.ToInt16(200 + random.Next(40));
return new Particula(textura, posicion, velocidad, angulo, velocidadangular, color, tamano, tiempodeVida);
}

/// <summary>
/// Añade y elimina partículas dependiendo del tiempo de Vida
/// </summary>
public void Update()
{
Int32 total = 10;

for (int i = 0; i < total; i++)
{
particulas.Add(GenerarParticulasAleatorias());
}

for (Int32 particula = 0; particula < particulas.Count; particula++)
{
particulas[particula].Update();
if (particulas[particula].TiempodeVida <= 0)
{
particulas.RemoveAt(particula);
particula--;
}
}
}

/// <summary>
/// Dibuja cada partícula
/// </summary>
/// <param name="spriteBatch"></param>
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
for (Int32 indice = 0; indice < particulas.Count; indice++)
{
particulas[indice].Draw(spriteBatch);
}
spriteBatch.End();
}
}
}

La clase MotorParticulas, tiene una lista de Texturas que serán usadas para pintar las partículas, también una variable Random que servirá para crear partículas con propiedades aleatorias, una lista de Partículas y un Vector que tendrá la posición del Emisor.

El método GenerarParticulasAleatorias hace lo que su nombre dice.

El método Update, cada vez que sea llamado creará una 10 partículas aleatorias y eliminara las que ya hayan pasado su tiempo de Vida.

El método Draw dibuja cada partícula  de la lista.

Usando el Sistema de Partículas

Lo último que nos queda es usar el Motor de Partículas, primero hay que adicionar las texturas que vamos a usar, como ejemplo he adicionado varias texturas, y al oprimir la tecla Espacio se cambiará la textura, mostrando diferentes estilos de Partículas:


using System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Particulas
{
 /// <summary>
 /// This is the main type for your game
 /// </summary>
 public class Game1 : Microsoft.Xna.Framework.Game
 {
 GraphicsDeviceManager graphics;
 SpriteBatch spriteBatch;
 MotorParticulas motorParticulas;
 List<Texture2D> texturas = new List<Texture2D>();
 SpriteFont nombres;
 Texture2D fuego;
 Texture2D humo;
 Texture2D stars;
 Texture2D circles;
 Texture2D fuegoBlanco;
 Texture2D nieve1;
 Texture2D nieve2;
 Int32 contTipos = 7;
 tiposParticulas tipos = tiposParticulas.Fuego;

 KeyboardState oldteclado;
 KeyboardState actualteclado;

 public Game1()
 {
 graphics = new GraphicsDeviceManager(this);
 Content.RootDirectory = "Content";
 }

 protected override void Initialize()
 {
 base.Initialize();
 }

 protected override void LoadContent()
 {
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);

nombres = Content.Load<SpriteFont>("SpriteFont1");
fuegoBlanco = Content.Load<Texture2D>("fuegoBlanco");

circles = Content.Load<Texture2D>("circle");
nieve1 = Content.Load<Texture2D>("snow1");
nieve2 = Content.Load<Texture2D>("snow2");
stars = Content.Load<Texture2D>("star");
fuego = Content.Load<Texture2D>("fire");
humo = Content.Load<Texture2D>("smoke");
texturas.Add(fuego);
motorParticulas = new MotorParticulas(texturas, new Vector2(400, 300));
 }

/// <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)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
actualteclado = Keyboard.GetState();
if (actualteclado.IsKeyUp(Keys.Space) &&
oldteclado.IsKeyDown(Keys.Space))
{
cambiarTipoParticula();
}

motorParticulas.PosicionEmisor = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
motorParticulas.Update();
// TODO: Add your update logic here
oldteclado = actualteclado;
base.Update(gameTime);
}

 protected void cambiarTipoParticula()
 {
 texturas.Clear();
 tipos = (tiposParticulas)((int)(tipos + 1) % contTipos);
switch (tipos)
{
case tiposParticulas.Fuego:
motorParticulas.aleatorias = false;
texturas.Add(fuego);
break;
case tiposParticulas.Humo:
motorParticulas.aleatorias = false;
texturas.Add(humo);
break;
case tiposParticulas.Circulos:
motorParticulas.aleatorias = true;
texturas.Add(circles);
break;
case tiposParticulas.Estrellas:
motorParticulas.aleatorias = true;
texturas.Add(stars);
break;
case tiposParticulas.FuegoBlanco:
motorParticulas.aleatorias = false;
texturas.Add(fuegoBlanco);
break;
case tiposParticulas.HumoyFuego:
motorParticulas.aleatorias = false;
texturas.Add(fuego);
texturas.Add(humo);
break;
case tiposParticulas.Nieve:
motorParticulas.aleatorias = false;
texturas.Add(nieve1);
texturas.Add(nieve2);
break;
}
}

/// <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)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
spriteBatch.DrawString(nombres, tipos.ToString(), new Vector2(10, 10), Color.Red);
spriteBatch.End();
// TODO: Add your drawing code here
motorParticulas.Draw(spriteBatch);
base.Draw(gameTime);
}
 }

 /// <summary>
 /// Enumeración de los tipos de Partículas
 /// </summary>
 public enum tiposParticulas
 {
Fuego,
Humo,
Circulos,
Estrellas,
FuegoBlanco,
HumoyFuego,
Nieve
}
}

El motor puede ser modificado para cambiar el tamaño, tiempo de Vida, y demás propiedades de las partículas.

En el siguiente Video se muestra en vivo como funciona el Motor:

El código Fuente Aquí.

6 pensamientos en “Sistema de Partículas 2D – XNA

  1. Pingback: Cómo hacer un Puzzle Bobble o Bust a Move en XNA – Parte 2 « Escarbando Código

  2. Hola.
    Estoy empezando con XNA. Intenté bajar el código, pero no funciona.
    ¿Podrías revisarlo o enviarmelo por correo?
    Gracias!!

Deseas comentar o sugerir algo?

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s