Ejemplo XNA parte 9 – Despliegue de explosiones

Ya tenemos nuestra nave con un arma para detonar las minas y defenderse, pero estas desaparecen cuando son destruidas. Aburrido, verdad? En esta parte de nuestro juego ejemplo de XNA implementaremos el despliegue de explosiones para los enemigos destruidos por la aeronave.

En la clase Game1

Comienza agregando lo siguientes atributos:

Texture2D explosionTexture;
List<Animation> explosions;

Estamos declarando la textura que tendrá el efecto de animación de la explosión, y una lista de Animacion para las explosiones, ya que puede que más de un enemigo sea destruido por el Jugador dentro de la escena, ocasionando múltiples explosiones. En el método Initialize, agrega esta línea al final:

explosions = new List<Animation>();

Simplemente estamos instanciando la lista de explosiones. Ahora en el LoadContent al final, cargaremos la imagen de explosión en nuestra textura con la siguiente instrucción:

explosionTexture = Content.Load<Texture2D>("explosion");

Es necesario contar con un método para agregar explosiones a la lista, para eso creamos el método AddExplosion, como se ve a continuación:

private void AddExplosion(Vector2 position)
{
	Animation explosion = new Animation();
	explosion.Initialize(explosionTexture,position, 
		134, 134, 12, 45, Color.White, 1f,false);
	explosions.Add(explosion);
}

Análogo cuando implementamos los enemigos, este método instancia una animación, la inicializa y finalmente la agrega en la lista de explosiones. Ahora el detalle es, Cuando llamamos este método? Ese es el truco, debemos llamarlo cuando la vida de los enemigos sea menor o igual a cero. Ve al método UpdateEnemies, y dentro de la instrucción if(enemies[i].Active == false), agrega las siguientes instrucciones debajo de ‘{‘:

// Si la vida del enemigo i es <= 0
if (enemies[i].Health <= 0)
{
	// Agregamos una explosion
	AddExplosion(enemies[i].Position);
}

Nota que cuando la condición se cumple, le estamos pasando como parámetro cuál enemigo esta siendo destruido, y la posición donde se mostrara la explosión. Ahora agregaremos el método UpdateExplosions para actualizar los elementos de explosión en la escena:

private void UpdateExplosions(GameTime gameTime)
{
	for (int i = explosions.Count - 1; i >= 0; i--)
	{
		explosions[i].Update(gameTime);
		if (explosions[i].Active == false)
		{
			explosions.RemoveAt(i);
		}
	}
}

Ahora debemos invocar este método en Update, después de UpdateProjectiles():

// Actualizando las explosiones
UpdateExplosions(gameTime);

En cuanto al método Draw, debemos dibujar todos los elementos de la lista de explosiones. Agrega las siguientes líneas de instrucciones al final del método:

// Dibujamos las explosiones
for (int i = 0; i < explosions.Count; i++)
{
	explosions[i].Draw(spriteBatch);
}

Ya terminamos de implementar las explosiones. Ahora arma la solucion y ejecuta el juego. Si todo esta bien codificado, se debera desplegar nuestro juego, y la aeronave destruyendo las minas, causando multiples explosiones. Aqui te muestro el snapshot del juego ejemplo en ejecucion con explosiones.

Ya ves el juego mas llamativo, verdad? Sin embargo, le faltan efectos de sonido a nuestro Shooter para hacerlo mas interesante. Ese será el siguiente paso. Puedes descargarte el proyecto en este punto dando clic en el siguiente enlace:

Proyecto Shooter Parte 9

Despliegue de sprites animados 2D

En el capitulo anterior se explicó la manera más directa de desplegar objetos en la escena de nuestro juego. Sin embargo, estos elementos estáticos resultan aburridos y monótonos para cualquier juego que estemos trabajando. En este capitulo te voy a explicar como hacer sprites animados. Abre Visual Studio 2010, crea un nuevo proyecto de tipo Windows Game (4.0). En el Content, descarga la siguiente imagen y agrégala al Content de tu proyecto:

Ahora en el proyecto, agrega una nueva clase y nómbrala «Animation». En esta clase implementaremos la mecánica necesaria para desplegar sprites animados.

Dinámica de animación

En principio contamos de una tira de imágenes que representan una animación. Lo que queremos implementaremos es una función capaz de desplegar una sola imagen, y trasladando la imagen, desplegamos la siguiente imagen consecutiva, y así sucesivamente, dándonos la ilusión de una imagen animada, como el efecto StopMotion.

En la clase Animation

Para esta clase, primero debemos agregar las siguientes librerías fundamentales de XNA.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

Ahora agregaremos los siguientes atributos:

// La imagen animada representada por un grupo de imagenes
Texture2D spriteStrip;

// Valor para escalar el sprite
float scale;

// Tiempo desde la ultima vez que se actualizo la imagen
int elapsedTime;

// Tiempo de despliegue por imagen
int frameTime;

// Numero de imagenes que conforman la animacion
int frameCount;

// Indice de la imagen actual
int currentFrame;

// Color de la imagen a desplegar
Color color;

// El area de la imagen que vamos a desplegar
Rectangle sourceRect = new Rectangle();

// El area donde queremos desplegar la imagen
Rectangle destinationRect = new Rectangle();

// Ancho de la una imagen
public int FrameWidth;

// Alto de una imagen
public int FrameHeight;

// Estado de la animacion
public bool Active;

// Repetir animacion
public bool Looping;

// Posicion del sprite
public Vector2 Position;

Para esta clase, utilizara tres métodos muy importantes: InitializeUpdate y Draw. Para el método Initialize, evidentemente inicializaremos todos los atributos para luego desplegar la imagen. Observa que se le pasan la mayoría de los valores por parámetros, la idea es para mantener esta clase lo mas genérica posible, a modo de re-utilizarla para cargar múltiples sprites con diferentes imágenes y propiedades.

public void Initialize(Texture2D texture, Vector2 position,
	int frameWidth, int frameHeight, int frameCount,
	int frametime, Color color, float scale, bool looping)
{
	// Mantener copias locales de los valores pasados
	this.color = color;
	this.FrameWidth = frameWidth;
	this.FrameHeight = frameHeight;
	this.frameCount = frameCount;
	this.frameTime = frametime;
	this.scale = scale;

	Looping = looping;
	Position = position;
	spriteStrip = texture;

	// Hacer reset a los tiempos
	elapsedTime = 0;
	currentFrame = 0;

	// Activar la animacion por defecto
	Active = true;
}

En cuanto al método Update, como ya se explicó la mecánica para animar sprites, aquí se implementa la lógica para desplegar la tira de imágenes, mostrándola una a una para dar la sensación de movimiento de los sprites. La lógica del método Update es el siguiente:

public void Update(GameTime gameTime)
{
	// No actualizar si la imagen esta desactivada
	if (Active == false) 
		return;

	// Actualizar tiempo transcurrido
	elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;

	// Si elapsedTime es mayor que frame time
	// debemos cambiar de imagen
	if (elapsedTime > frameTime)
	{
		// Movemos a la siguiente imagen
		currentFrame++;
		// Si currentFrame es igual al frameCount 
		// hacemos reset currentFrame a cero
		if (currentFrame == frameCount)
		{
			currentFrame = 0;
			// Si no queremos repetir la animacion
			// asignamos Active a falso
			if (Looping == false)
				Active = false;
		}
		// Reiniciamos elapsedTime a cero
		elapsedTime = 0;
	}
	// Tomamos la imagen correcta multiplicando el currentFrame 
	// por el ancho de la imagen
	sourceRect = new Rectangle(
		currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);

	// Actualizamos la posicion de la imagen en caso que esta
	// se desplace por la pantalla
	destinationRect = new Rectangle(
		(int)Position.X - (int)(FrameWidth * scale) / 2,
		(int)Position.Y - (int)(FrameHeight * scale) / 2,
		(int)(FrameWidth * scale),
		(int)(FrameHeight * scale));
}

Finalmente llegamos en el método Draw, donde simplemente invocamos el método spriteBatch.Draw SIEMPRE y cuando active este en verdadero. Aquí esta el método Draw:

public void Draw(SpriteBatch spriteBatch)
{
    if (Active)
    {
        spriteBatch.Draw(spriteStrip, destinationRect, sourceRect, color);
    }
}

Este es todo el código necesario para nuestra clase Animacion. Ahora solo debemos instanciarla, inicializarla y llamarla para desplegarla en escena y e invocar su método Update para que refresque las imágenes para lograr la animación del sprite.

En la clase Game1

Para nuestra clase principal, comenzaremos agregando los siguientes atributos:

Animation playerAnimation;
Vector2 spritePos;

Proseguimos a dirigirnos al método LoadContent, donde cargaremos la imagen del Content a la variable playerTexture e instanciar la posición inicial del sprite, y se la enviamos por parámetros al método Initialize a nuestro objeto playerAnimation:

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);

	playerAnimation = new Animation();
	Texture2D playerTexture = Content.Load<Texture2D>("Ken");

	spritePos = new Vector2(
		GraphicsDevice.Viewport.TitleSafeArea.X +
		GraphicsDevice.Viewport.TitleSafeArea.Width / 2,
		GraphicsDevice.Viewport.TitleSafeArea.Y +
		GraphicsDevice.Viewport.TitleSafeArea.Height / 2);

	playerAnimation.Initialize(playerTexture,  
		spritePos, 106, 110, 6, 80, Color.White, 1f, true);
}

Ahora en el método Update, basta con solo invocar el método Update de nuestro objeto playerAnimation:

playerAnimation.Update(gameTime);

Y Finalmente, en el método Draw, invocamos el método Draw de nuestro objeto playerAnimation. Recuerda que debes encapsular esta instrucción con los metodos spriteBatch.Begin(); y spriteBatch.End(); de lo contrario, no veras ningún elemento desplegado en escena.

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	playerAnimation.Draw(spriteBatch);
	spriteBatch.End();
	base.Draw(gameTime);
}

Ya en este punto, queda armar la solucion y esperar que se ejecute tu aplicacion con una animacion de Ken corriendo. Te anexo una captura de la aplicacion:

Experimenta las propiedades de la clase Animacion, y analiza los resultados. Qué ocurre si le aumentas el currentTime? Qué ocurre si le aumentas el scale? Es importante que estudies el comportamiento de esta clase para que tengas un mayor control y entendimiento sobre sprites animados. Este es un camino para animar sprites, pero puede haber muchas otras formas para hacerlo.

Si presentas problemas armando tu solución, puedes descargar el proyecto final en el siguiente enlace:

Proyecto AnimacionXNA

Interacción en XNA para Windows Phone: Usando el Acelerómetro

El acelerómetro es un sensor que se encuentran en la mayoría de dispositivos móviles de hoy en día. Básicamente, este sensor constantemente mide el cambio de las aceleraciones asociadas con el fenómeno de peso experimentada por una masa de prueba que se encuentra en el marco de referencia del dispositivo. En otras palabras, es un sensor capaz de detectar el mínimo movimiento de posición del dispositivo en cualquiera de sus tres ejes. Para el caso particular de Windows Phones, es importante que tengas presente la convención de los ejes para este tipo de dispositivos:

Es importante tener en cuenta la convención de ejes de coordenadas del dispositivos con el que queremos desarrollar una aplicación que involucre utilizar el acelerómetro. Otra facilidad que nos ofrece el emulador de Windows Phone es la funcionalidad de simular el cambio de orientación del teléfono, ideal cuando queremos hacer pruebas de nuestra aplicación utilizando el acelerómetro para aquellos desarrolladores que no cuentan con el teléfono físico.

Puedes establecer la posición inicial del teléfono (modo retrato levantado, modo horizontal levantado, modo retrato acostado y modo horizontal acostado), y en base a esa posición deberás implementar tu aplicación. Ahora prosigamos a implementar un ejemplo sencillo donde una pequeña imagen que se desplazara de acuerdo a la orientación a la que se encuentre el dispositivo.

Ejemplo de una aplicación utilizando el acelerómetro

Abre Visual Studio 2010 y crea un proyecto tipo Windows Phone Game (4.0), y nómbralo «AcelerometroXNA». Ahora debemos agregar una referencia para utilizar el Acelerómetro. Dale clic-derecho a la carpeta Referencias y dale clic a «agregar Referencia», y busca la librería .NET «Microsoft.Devices.Sensors».

Una vez agregada esa referencia, importa la librería en la clase Game1.cs:

using Microsoft.Devices.Sensors;

Ahora, nuevamente agreguemos una imagen al Content de nuestro proyecto. En este ejemplo usaremos la siguiente imagen:

Dale clic-derecho a AcelerometroXNAContent, y selecciona «agregar->ítem existente», y selecciona la imagen que descargaste previamente.

Regresando a nuestra Game1.cs, en el constructor Game1, reemplázalo por este:

Accelerometer accelerometer;
Texture2D circuloTex;
Vector2 circuloPos;
Vector2 circuloVel;

En el constructor de Game1.cs, debemos establecer explícitamente las dimensiones de la pantalla, a modo que este evite de rotar automáticamente independientemente a como manipulemos el dispositivo.

public Game1()
{
	graphics = new GraphicsDeviceManager(this);
	graphics.PreferredBackBufferHeight = 800;
	graphics.PreferredBackBufferWidth = 480;
	Content.RootDirectory = "Content";
	TargetElapsedTime = TimeSpan.FromTicks(333333);
	InactiveSleepTime = TimeSpan.FromSeconds(1);
}

En el método Initialize, inicializamos la posición de nuestra imagen en el centro de la pantalla, instanciamos nuestra clase acelerómetro, y le agregamos un manejador de evento (AccelReadingChanged) que se disparara cada vez que el sensor detecte un cambio de valor de aceleración en cualquiera de sus tres ejes; y por último, hacemos Start al acelerómetro para que comience a recibir datos del sensor.

protected override void Initialize()
{
	circuloPos = new Vector2(
		graphics.PreferredBackBufferWidth / 2, 
		graphics.PreferredBackBufferHeight / 2);
	accelerometer = new Accelerometer();
	accelerometer.ReadingChanged += new EventHandler
		<AccelerometerReadingEventArgs>(AccelReadingChanged);
	accelerometer.Start();
	base.Initialize();
}

Ahora proseguimos a cargar nuestra imagen de marte a nuestra variable de textura circuloTex. Esto se realiza en el método LoadContent.

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);
	circuloTex = Content.Load<Texture2D>("planet-mars");
}

Algo muy importante que debemos hacer en el método UnloadContent particularmente en este ejemplo. Debemos liberar el recurso del acelerómetro cuando finalice la ejecución de la aplicación. Esto se hace invocando al método Stop de nuestro objeto accelerometer. Luego hacemos Unload a nuestro contenido para liberar el contenido grafico cargado a memoria.

protected override void UnloadContent()
{
	accelerometer.Stop();
	Content.Unload();
}

En el método Initialize habíamos declarado un método manejador de evento del acelerómetro llamado AccelReadingChanged. He aquí dicho método. Puedes observar que al circuloVel se le suman los valores detectados por el sensor, y estos a su vez son sumados a la posición de nuestra elemento grafico. Solo estamos trabajando con el eje X e Y, lo que significa que la posición inicial del dispositivo es horizontal, con la orientación de pantalla modo Landscape.

void AccelReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
	circuloVel.X += (float)e.X;
	circuloVel.Y -= (float)e.Y;
	circuloPos += circuloVel;
}

En cuanto al método Update, que debemos hacer? Obviamente asegurarnos que nuestro planeta marte no se salga de los bordes de la pantalla. Esto es cuidando que la posición de nuestra imagen no sobrepase las dimensiones de nuestro viewport. Esto se hace con las siguientes instrucciones:

Viewport viewport = graphics.GraphicsDevice.Viewport;
if (circuloPos.X < 0) 
{ 
	circuloPos.X = 0; 
	circuloVel.X = 0; 
} 
else if (circuloPos.X > viewport.Width - circuloTex.Width)
{
	circuloPos.X = viewport.Width - circuloTex.Width;
	circuloVel.X = 0;
}
if (circuloPos.Y < 0) 
{ 
	circuloPos.Y = 0; 
	circuloVel.Y = 0; 
} 
else if (circuloPos.Y > viewport.Height - circuloTex.Height)
{
	circuloPos.Y = viewport.Height - circuloTex.Height;
	circuloVel.Y = 0;
}

Finalmente, llegamos al método Draw, donde simplemente desplegamos nuestra imagen en la posición vinculada al cambio de valores que se encuentre recibiendo el acelerómetro.

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	spriteBatch.Draw(circuloTex, circuloPos, Color.White);
	spriteBatch.End();
	base.Draw(gameTime);
}

Ya el proyecto listo, proseguimos a armar nuestra solución y ejecutamos. Una vez que se ejecute el emulador, despliega el panel de herramientas adicionales, en la sección del acelerómetro. Ajusta la orientación inicial a «Landscape Flat», y dale clic a la esfera roja del emulador y trasládala hacia diversas direcciones para que observes como la imagen de marte se desplaza por la pantalla de acuerdo a la dirección de orientación del dispositivo.

De esta forma se trabaja con el Acelerómetro para Windows Phone utilizando el Framework XNA. Si tu proyecto presenta problemas de armar la solución, puedes descargarte el ejemplo:

Proyecto Acelerometro XNA

Ejemplo XNA parte 2 – Creación de la Clase Jugador

Haz un clic derecho sobre el proyecto y agrega una nueva clase llamada «Jugador», y agrega los métodos Initialize, Update y Draw. Deberás importar las librerías de XNA.

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

namespace Shooter
{
	class Jugador
	{
		public void Initialize()
		{
		}

		public void Update()
		{
		}

		public void Draw()
		{
		}
	}
}

Esta es la estructura básica de nuestra clase Jugador, que funcionará para desplegar el elemento que controlará el usuario, que en nuestro caso es la aeronave. Ahora agrega los siguientes atributos (observa la importancia de cada uno que se encuentran comentados)

// Animacion representando al jugador
public Texture2D PlayerTexture;

// Posicion del jugador
public Vector2 Position;

// Estado del jugador
public bool Active;

// Cantidad de vida del jugador
public int Health;

// Obtener el ancho del jugador
public int Width
{
	get { return PlayerTexture.Width; }
}

// Obtener el alto del jugador
public int Height
{
	get { return PlayerTexture.Height; }
}

Estos son algunos atributos fundamentales para nuestra clase jugador, pero obviamente se pueden agregar mas dependiendo del control que queramos tener sobre este elemento. Ahora reemplaza el método Initialize (de la clase Jugador) con lo siguiente:

public void Initialize(Texture2D texture, Vector2 position)
{
	PlayerTexture = texture; 
	// Asignar la posicion del jugador
	Position = position;
	// Activar el jugador
	Active = true;
	// Inicializar la vida del jugador
	Health = 100;
}

Como se puede ver claramente, lo que hace el metodo Initialize es establecer las propiedades iniciales de nuestra clase Jugador. Ahora reemplaza el metodo Draw con el siguiente:

public void Draw(SpriteBatch spriteBatch)
{ 
	spriteBatch.Draw(PlayerTexture, Position, null, 
		Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}

Ya con la clase Jugador con sus atributos y funciones listas, pasaremos a la clase principal para instanciar un objeto Jugador.

En la clase Game1.cs

Ya que tenemos todo preparada la clase Jugador, debemos instanciarla, inicializarla y por último, invocar su método Draw desde esta clase principal para desplegar la nave en la pantalla. Ve al inicio de esta clase, y debajo de la declaración del atributo SpriteBatch spriteBatch; agrega nuestra variable tipo Jugador.

Jugador jugador;

Y ahora en el método Initialize, instanciamos nuestra clase:

jugador = new Jugador();

Siguiente, debemos cargar la textura de nuestra nave que se encuentra en el Content de nuestro proyecto. En el método LoadContent, agrega las siguientes líneas:

// Load the player resources 
Vector2 playerPosition = new Vector2(
	GraphicsDevice.Viewport.TitleSafeArea.X,
	GraphicsDevice.Viewport.TitleSafeArea.Y + 
	GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
jugador.Initialize(Content.Load<Texture2D>("Imagenes/nave"), playerPosition);

Ya que tenemos nuestro objeto Jugador instacianciado, y cargado, ve al metodo Draw y agrega las siguientes instrucciones:

spriteBatch.Begin();
jugador.Draw(spriteBatch);
spriteBatch.End();

Ahora finalmente compila el proyecto, y ejecútalo. Si todo funciona correctamente, verás algo como esto:

Si tu proyecto presenta errores y no encuentra como solucionarlos, no entres en pánico, puedes descargar el proyecto de acá en su estado final de este tutorial, simplemente da clic al siguiente enlace:

Proyecto Shooter Parte 2

Interacción en XNA para Windows Phone: Reconocimiento de Gestos

XNA posee funcionalidades innatas para reconocer los gestos más comunes con los que interactuamos normalmente con cualquier dispositivo móvil en la actualidad. Simplemente se debe habilitar el reconocimiento de gestos, y así el juego tendrá la capacidad de interpretar cualquier tipo de gesto realizado por parte del usuario. Los gestos más comunes se muestran en el siguiente diagrama.

Un Tap es el gesto cuando le damos un toque rápido en algún punto de la pantalla. Double Tap son dos toques consecutivos en un mismo punto, como hacer un doble clic. El Pinch y Stretch son el típico gesto que conocemos para hacer zoom-in y zoom-out en una foto o una imagen. El gesto Pan ocurre cuando deseamos trasladarnos por un scrollbar, o navegar por un mapa o imagen. Flick es un toque rápido con alguna dirección, lo que causa una especie de inercia a algún elemento que estemos trasladando.

A continuación implementaremos un ejemplo sencillo de reconocimiento de gestos utilizando el Framework XNA para Windows Phone.

Ejemplo de Reconocimiento de Gestos

Crea un proyecto Windows Phone Game (4.0), y nómbralo «GestosXNA», selecciona versión del sistema operativo 7.1. Haz un clic-derecho sobre el Content del proyecto, y agrega un nuevo Ítem de tipo «Sprite Font», y nómbralo «SpriteFont1».

En la clase Game1.cs, después de la declaración del atributo SpriteBatch spriteBatch; agrega los siguientes atributos:

SpriteFont spriteFont;
String message = "Realiza un gesto";
Vector2 messagePos = Vector2.Zero;
Color color = Color.Black;

Ahora, en el método Initialize, y agrega la siguiente instrucción antes de la línea base.Initialize(); y agrega lo siguiente:

TouchPanel.EnabledGestures = GestureType.Tap | GestureType.DoubleTap | 
                        GestureType.Hold | GestureType.HorizontalDrag | 
                        GestureType.VerticalDrag | GestureType.FreeDrag | 
                        GestureType.DragComplete | GestureType.Pinch | 
                        GestureType.PinchComplete | GestureType.Flick;

De esta forma, estamos habilitando cuales gestos deseamos que nuestra aplicación sea capaz de reconocer en tiempo de ejecución. En el método LoadContent, cargaremos la fuente agregada del Content, de esta manera:

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);
	spriteFont = Content.Load<Texture2D>("SpriteFont1");
}

Ahora, en el método Update es donde en cada ciclo del Game Loop, el juego realizara una captura del estado de la pantalla táctil, registrando gestos realizados por el usuario, y los irá reconociendo en tiempo real. El método completo está implementado de la siguiente forma:

if (TouchPanel.IsGestureAvailable)
{
	GestureSample gesture = TouchPanel.ReadGesture();
	switch (gesture.GestureType)
	{
		case GestureType.Tap:
			message = "Tap";
			color = Color.Red;
			break;
		case GestureType.DoubleTap:
			message = "Doble Tap";
			color = Color.Orange;
			break;
		case GestureType.Hold:
			message = "Hold";
			color = Color.Yellow;
			break;
		case GestureType.HorizontalDrag:
			message = "Arrastre horizontal";
			color = Color.Blue;
			break;
		case GestureType.VerticalDrag:
			message = "Arrastre vertical";
			color = Color.Indigo;
			break;
		case GestureType.FreeDrag:
			message = "Arrastre libre";
			color = Color.Green;
			break;
		case GestureType.DragComplete:
			message = "Gesto arrastre completado";
			color = Color.Gold;
			break;
		case GestureType.Flick:
			message = "Flick";
			color = Color.Violet;
			break;
		case GestureType.Pinch:
			message = "Pinch";
			color = Color.Violet;
			break;
		case GestureType.PinchComplete:
			message = "Gesto pinch completado";
			color = Color.Silver;
			break;
	}
	messagePos = gesture.Position;
}

Primero se verifica si el TouchPanel tiene el reconocimiento de gestos habilitados. Luego TouchPanel.ReadGesture() devuelve un GestureSample en cada ciclo del GameLoop, que es analizado en el switch(gesture), y se detendrá en el caso correspondiente al gesto reconocido, cambiando el valor del string message, informando el gesto detectado, y finalmente, se le asigna la posición del mensaje donde ocurrió el gesto analizado.

En el método Draw, proseguimos a dibujar nuestro string:

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	spriteBatch.DrawString(spriteFont, message, messagePos, color);
	spriteBatch.End();
	base.Draw(gameTime);
}

Listo! Ahora compila la solución y si todo esta correctamente codificado, veras la siguiente pantalla:

Ya con la aplicación ejecutándose en el emulador Windows Phone, realiza gestos utilizando tu mouse y verás que son reconocidos por nuestra aplicación, mostrando un mensaje sobre el gesto detectado, y en la posición donde ocurrió.

Desafortunadamente no podrás realizar gestos multitouch como el Pinch; para eso necesitarías hacer deploy del proyecto a un teléfono físico. Sin embargo, puedes realizar cualquier otro gesto habilitado. Experimenta y realiza todos los gestos disponibles.

Si tu proyecto presenta errores de compilación, puedes descargarte el ejemplo:

Proyecto GestosXNA

Ejemplo XNA parte 1 – Creacion del Proyecto XNA

Vamos a crear un juego completo sencillo desde cero.

Dinámica del juego

Haremos un juego 2D, donde el usuario controlará una aeronave que dispara rayos laser para detonar minas que se le presentan en su camino, evitando que colisione contra ellas. El diagrama a seguir será el siguiente:

Se puede ver un tanto complicado, pero verás que armándolo paso a paso será sencillo.  Es siempre una buena practica diagramar el juego que estés diseñando, a modo que tengas una vista general de lo que quieres crear de manera modular y organizada. Puedes ver los diversos estados en el que se encuentra tu aplicación, como Menú Principal, Fase «Juego» y toda la lógica que conlleva esa fase, como el chequeo de input, actualización de posición de los elementos del juego como los enemigos, proyectiles, etc.; hasta la fase de fin de juego, q te llevara de regreso al menú principal.

En este capitulo de construcción del juego, nos enfocaremos en la fase de «Juego», donde manejaremos despliegue de elementos animados (el jugador, enemigos, proyectiles, explosiones), una técnica para el fondo, dando sensación de profundidad, efectos de sonido, puntaje y colisiones. A continuación te muestro un snapshot de como quedara el juego al final.

Sin más preámbulo, prepárate para la implementación de nuestro juego.

Comencemos a implementar nuestro juego

Abre Visual Studio 2010, y crea un proyecto XNA para Windows PC, y nómbralo «Shooter».

Material Requerido

Vamos a necesitar el siguiente contenido gráfico para nuestro content.

Material Shooter

Crea una carpeta «Imágenes», «Fuente» y «Audio» en la sección Content, y agrega el contenido descargado en las carpetas agregadas correspondientes. También descarga la clase Animación (link) para animar los elementos descargados, y agrégalo a tu proyecto.

Agregar el Material al Proyecto

En la sección Content del proyecto, agrega tres carpetas nombradas «Imagenes», «Fuente» y «Sonidos». Ahora prosigue a agregar el material descargado previamente en las carpetas correspondientes. En la sección de código del proyecto, agrega la Clase “Animation”, que la usaremos después. Asegúrate que el Namespace de esa clase sea la misma a como nombraste el proyecto («Shooter»).

Una vez haya agregado todo el contenido necesario para nuestro juego, la estructura del proyecto debe quedar de la siguiente forma:

Ahora prosigue a compilar tu solución y ejecútalo. Si todo funciona bien, te debería desplegar una ventana con un gran panel azul.

Hasta este punto hemos preparado nuestro proyecto con todo el material audiovisual necesario para implementar nuestro juego. Si deseas descargarte la fase final de este tutorial, simplemente da click al proyecto:

Proyecto Shooter Parte 1

Despliegues de Sprites en 2D

Bien, ya visto en articulos anteriores todo el basamento teórico de como funciona el Framework XNA, las versiones que han salido desde su lanzamiento en el 2007, el soporte para las diversas plataformas de Microsoft (Windows PC, Xbox 360 y Windows Phone), el funcionamiento de XNA y por último, la estructura de un proyecto de un Windows Game XNA en Visual Studio, es hora que empecemos a codificar y dibujar elementos.

Paso 1. Crear el proyecto

Debes abrir Visual Studio 2010 con XNA Game Studio 4 previamente instalado, y crear un proyecto Windows Game (4) como se mostró en el artículo anterior.

Paso 2. Agregar una imagen a nuestro Content

Ahora debemos agregar una carpeta en el Content donde colocaremos nuestras imágenes. No es requerido crear un directorio, pero lo recomiendo para mantener nuestro contenido gráfico organizado. Simplemente da un clic-derecho en tu proyecto Content, le das clic a agregar->nueva carpeta, y lo nombraremos «Imagenes».

Para agregar una imagen (que la pueden encontrar aqui para descargar), dale clic-derecho a la carpeta «Imagenes» ya creada, luego clic en agregar->item existente. Te saldrá un explorador de archivos, para que busques el archivo de imagen que deseas agregar a tu proyecto. Selecciona la imagen que descargaste previamente (smiley.png), y le das clic a «Agregar»).

Paso 3. Dibujar el sprite en la Escena

Ya que tenemos nuestra imagen en el Content de nuestro proyecto, debemos cargarla y desplegarla escribiendo código. Lógicamente para desplegar esta imagen, dependemos lógicamente de dos cosas: Posición y Textura. Para eso, agrega las siguientes dos variables dentro de tu clase principal (Game1), debajo de spriteBatch. El código se vería de la siguiente manera:

Vector2 es un tipo de datos que posee dos campos float x,y; a modo de trabajar con coordenadas de dos dimensiones. Ininializa posicion en el método Initialize:

Ahora procedemos a cargar la imagen a nuestro juego. Esto se hace en el método LoadContent. El código quedaría así:

Puedes observar que se instancia la variable spriteBatch (el cual puedes verlo por los momentos como el pincel encargado de pintar todos los elementos en la escena de nuestro juego). También observa como se carga una textura2D con el método Load. Toma en cuenta el string que pasamos como parámetro; es la ruta del elemento que queremos cargar. Por esto, es importante colocar en qué directorio se encuentra si esta organizado de esta manera.

Paso 4. Desplegar nuestro sprite en la escena

Recapitulando, hemos agregado nuestra imagen archivo al contenido de nuestro proyecto; hemos agregado una variable para la posición donde desplegaremos nuestro sprite, y declaramos una variable Texture2D, donde realizamos la carga de nuestra imagen del contenido a la aplicación. Ahora, sin más preámbulos, dibujemos nuestro sprite. Esto se implementa en el método Draw.

Para pintar nuestro sprite, debemos utilizar nuestra variables spriteBatch, ya que este es el encargado de dibujar los elementos en la escena como ya mencione a priori. Todos los elementos que vayamos a dibujar se hacen mediante su método Draw(), y estos deben estar encerrados dentro de los métodos spriteBatch.Begin() y spriteBatch.End(). Muy importante estos dos métodos, porque estamos avisando a nuestra aplicación que vamos a pintar elementos, y cuando terminemos, invocamos el método End(). En nuestro caso, nuestro código se vera así:

Fíjate el método spriteBatch.Draw(). Solo estamos pasando la textura que deseamos desplegar, la posición, y el color que queremos que sea desplegado. Si colocamos Blanco, se mostraran los colores originales de la imagen. Cualquier otro color modificara los colores de nuestro sprite.

Finalmente, al ejecutar nuestra aplicación, tendrás un resultado como este:

Sencillo, no crees? Esta es la manera mas elemental para desplegar un sprite en nuestro juego. El método Draw que utilizamos es el mas básico disponible, ya que este método cuenta con 7 formas distintas de invocar con diversos parámetros, incluyendo efectos, manejo de rectángulos para dibujar ciertas áreas, etc. Para proyectos más complejos, se trabajan con clases donde encapsulan estas propiedades (textura, posición), y se le agregan mas propiedades para ser mas flexibles ante transformaciones y efectos.

Estructura de un proyecto XNA

Para crear un proyecto XNA, debemos abrir Visual Studio 2010 con el XNA Game Studio 4.0 previamente instalado. Da un clic en Archivo, Nuevo Proyecto, y ve a la categoría de plantillas «XNA Game Studio», y selecciona la plantilla Windows Game (4.0). Escribe el nombre de proyecto deseado y le das click en  aceptar.

Una vez creado el proyecto, verás la clase principal del proyecto que por defecto se llama Game1. En el Solution Explorer, veras la siguiente estructura de carpetas.

Proyecto WindowsGame1

En esta sección de la solución es donde se desarrolla toda la lógica del juego. La clase principal es la llamada Class1, donde se encuentra bien definidos los métodos principales a implementar acorde al funcionamiento del framework XNA. Existe otra clase estática llamada Program.cs, donde aquí en realidad es el que invoca la Clase1 para ejecutarla; sin embargo, tu labor es implementar tu juego es en la clase Class1.

También veras un archivo llamado «Game.ico», donde este es un pequeño icono de 32×32 pixeles que se coloca en la esquina superior izquierda de la ventana de nuestro juego al momento de ejecutar nuestra aplicación. Este icono también se mostrara si lo anclas a la barra de inicio.

Entre otros elementos dentro de este proyecto, se encuentra el directorio “Properties”, donde se encuentra el archivo “AssemblyInfo.cs”, que es el archivo configuración de ensamblado de nuestro proyecto. Al abrirlo, veras que contiene toda la información acerca de la versión de la aplicación, el Titulo del juego, producto, descripción, año, etc. También se encuentra el directorio «References», donde en esta se encuentra referencias a las librerías fundamentales requeridas para el funcionamiento de este proyecto. Si deseas trabajar con librerías externas encontradas en la web (Como trabajar con el Kinect, o librerias de físicas, etc.), con descargarlas y hacerles referencia, ya se encuentran disponibles para utilizarlas en tu juego.

En este punto, te sugiero que trabajes con directorios de clases, a modo de trabajar un proyecto de manera organizada por grandes módulos esenciales, como física, sonido, gráficos, interacción entrada/salida, etc.

Proyecto WindowsGame1Content

Para este proyecto de tipo Windows Game Library (4.0) que se genera automáticamente en la solución al momento que se crea un Windows Game (4.0), es donde se coloca todo el contenido gráfico audiovisual del juego. Se recomienda que organices el contenido en directorios, para imágenes, archivos de configuración, sonidos, videos, música de fondo, etc. Esta sección ya se encuentra referenciada por el proyecto principal (que se puede ver dentro del directorio «Content References»).

Mecanismo del Framework XNA

A continuación se muestra una figura de la forma como funciona el framework XNA a nivel de implementación. Es de suma importancia saber el orden de los métodos que son invocados cómo desarrollamos nuestros juegos. Sin mas que agregar, se muestra el diagrama, y la explicación de cada método en el orden de ejecución para el momento que se inicia un juego hasta el momento que finaliza.

Método Initialize

El método Initialize es usado para inicializar variables y otros objetos asociados con el objeto Game1. Los objetos del dispositivo grafico será inicializados en este punto y pueden ser usados en el métodos Initialize para ayudarte a inicializar otros objetos que dependen en la configuración. Utilizarás este método para inicializar valores de puntuación y otros valores afines.

Método LoadContent

El método LoadContent es invocado después del método Initialize, de la misma forma para el caso que el contenido grafico debe ser recargado (Por ejemplo cuando el dispositivo grafico debe ser reiniciado debido a que el jugador ha cambiado la configuración grafica del juego, o algo por el estilo). El método LoadContent es donde se carga todos los gráficos y otro contenido requerido por el juego, incluyendo imágenes, modelos, sonidos, etc.

Game Loop (Método Update y Metodo Draw)

Despues que culmina el método LoadContent, el juego entra en un estado conocido como Game Loop, o bucle de juego.  Casi todos los juegos entran en algún tipo de bucle de juego, independientemente si están escritos en XNA o no. Esta área de código difiere del típico entorno de programación de juegos, y para algunos desarrolladores les puede tomar algo de tiempo acostumbrarse.

En esencia, un Game Loop consiste en una serie de métodos que son invocados una y otra vez hasta que el juego finaliza. En XNA, el Game Loop consiste en dos métodos: Update y Draw. Por ahora, puedes pensar en el Game Loop en estos términos: toda la lógica que afecta el juego actual se encontrará en el método Update o el método Draw. El método Draw es típicamente usado, obviamente, para dibujar elementos. Procura implementar lo mínimo que puedas en esta área excepto para desplegar elementos de tu escena. Todo lo demás que sea necesario para la ejecución adecuada de tu juego (que eventualmente involucra el mover objetos, chequear colisiones, actualizar puntuaciones, chequear la condición fin de juego, etc.) se encontrará en el método Update.

Método UnloadContent

Al punto dentro del Game Loop donde se llegue a una condición de finalizar el Juego, el bucle se termina, y se invoca automáticamente el método UnloadContent. Este método es usado para liberar cualquier contenido cargado previamente en el método LoadContent que requiere un manejo especial de liberación. Tipicamente, XNA (como .NET) manejara la recolección de basura automáticamente, pero si has modificado la memoria en algún objeto que requiere un manejo especial, el método UnloadContent es donde implementaras la lógica de liberación de contenido de la memoria.

Diseña un sitio como este con WordPress.com
Comenzar