Ejemplo XNA parte 4 – Animación del elemento Jugador

Hasta ahora tenemos una aeronave que se puede mover a lo largo de la pantalla, pero sin ninguna animación, no se ve interesante ni llamativo para el usuario. Eso lo vamos arreglar ahora. Utilizaremos la clase “Animacion” que importamos desde la parte 1 de este tutorial. En caso que no hiciste el tutorial de animación en este blog, es recomendable que lo leas para que entiendas el mecanismo utilizado para desplegar objetos animados en la pantalla. Accede dicho tutorial dando click en el siguiente enlace: Despliegue de sprites animados 2D.

En la clase jugador

En esta clase Jugador, lo primero que haremos es reemplazar la variable

public Textured2D PlayerTexture

Por esta variable:

public Animation PlayerAnimation;

Esto es necesario ya que no trabajaremos desplegando una Textura estatica de la nave. Trabajaremos con la clase animacion para trabajar con el mecanismo de animacion de sprites. Debido a este cambio de atributo, debemos modificar unas cuantas instrucciones en otros metodos. Ve al metodo Initialize, y reemplazalo por el siguiente:

public void Initialize(Animation animation, Vector2 position)
{
	PlayerAnimation = animation;
	Position = position;
	// Set the player to be active
	Active = true;
	// Set the player health
	Health = 100;
}

Aquí lo que hicimos fue asignar el atributo Animacion que nos fue pasado por parámetro, al igual que la posición. Hacemos true al atributo active para que sea desplegado por defecto, y le inicializamos la vida de la nave a 100. También debemos modificar los atributos Width y Height en la clase jugador. Reemplázalas con lo siguiente:

// Get the width of the player ship
public int Width
{
	get { return PlayerAnimation.FrameWidth; }
}
// Get the height of the player ship
public int Height
{
	get { return PlayerAnimation.FrameHeight; }
}

Ya que no nos interesa saber el alto ni ancho de la imagen Texture2d que teníamos antes, sino el ancho y alto de una imagen en nuestra clase Animacion. Con respecto a Updatedebemos actualizar la posición e invocar el método Update de PlayerAnimation para desplazar el sprite a lo largo de la pantalla con el campo Position, e invocamos Update para el efecto de animación. El método Update queda de la siguiente manera:

public void Update(GameTime gameTime)
{
	PlayerAnimation.Position = Position;
	PlayerAnimation.Update(gameTime);
}

Finalmente, reemplazamos el método Draw que nos desplegaba la Textura2D estática, llamando al método Draw de PlayerAnimation para que despliegue el sprite animado. El método Draw quedaría de la siguiente manera:

public void Draw(SpriteBatch spriteBatch)
{
	PlayerAnimation.Draw(spriteBatch);
}

En la clase Game1

Por último nos queda modificar la clase principal, que involucra la carga de las imágenes de animación, y mandarlas al objeto Jugador por el método Initialize. Dirígete a LoadContent, y reemplaza el método completo por este:

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

	Animation playerAnimation = new Animation();
	Texture2D playerTexture = Content.Load<Texture2D>("Imagenes/animacionNave");
	playerAnimation.Initialize(playerTexture, 
		Vector2.Zero, 115, 69, 8, 30, Color.White, 1f, true);

	Vector2 playerPosition = new Vector2(
		GraphicsDevice.Viewport.TitleSafeArea.X +
		GraphicsDevice.Viewport.TitleSafeArea.Width / 2,
		GraphicsDevice.Viewport.TitleSafeArea.Y +
		GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
	jugador.Initialize(playerAnimation, playerPosition);
}

Nada de otro mundo, simplemente instanciamos un objeto local de la clase Animacion, cargamos la tira de imágenes de la nave en playerTexture, e invocamos el método Initialize de playerAnimation, enviándole por parámetros:

  • playerTexture, el cual es la tira completa de imágenes de la nave
  • Vector2.Zero para la inicializar la posición
  • 115, que es el ancho de una sola imagen expresados en pixeles
  • 69, es el alto de una sola imagen expresados en pixeles
  • 8, representa el numero de imágenes que componen la animación
  • 30, tiempo de despliegue por imagen expresados en milisegundos
  • Color.White, para desplegar la imagen en sus colores reales
  • 1f, para el valor de escala del elemento
  • true, para especificar si queremos que se repita la animación múltiples veces

Se instancia un Vector2 para la posición, para desplegar la nave en el centro de la pantalla, y finalmente se invoca el Initialize de jugador, enviandole playerAnimation playerPosition. Ahora busca el método UpdatePlayer, y al comienzo del método, agrega la siguiente línea:

jugador.Update(gameTime);

Esto invoca el Update de jugador, que este a su vez hace Update a su atributo playerAnimation para refrescar las imágenes de despliegue para dar el efecto de animación de nuestra nave. Ahora no queda más sino armar la solución y ejecutar la aplicación. Ahora veras la nave animada en el centro de la pantalla, con un efecto de movimiento en las alas y el propulsor en la parte trasera. También observa que lo puedes controlar con las teclas de dirección del teclado.

Hasta este punto de nuestro juego de ejemplo, hemos logrado desplegar una nave animada, e interactiva con el usuario. De la misma manera como animamos el sprite del jugador, se harán con los demás elementos animados, como los enemigos. Puede descargar nuestro proyecto de ejemplo hasta este punto dando clic en el siguiente enlace:

Proyecto Shooter Parte 4

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

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