
Bienvenidos a este nuevo Post. La vez anterior pudimos desplegar sprites en pantalla y hacer una animación con ellos. Lo que vamos a hacer ahora es utilizar las teclas de dirección del teclado para poder controlar a nuestro personaje. Antes de empezar puedes consultar el post “Edición básico de sprites” si quieres saber el proceso que seguí para generar los sprites.
Los puntos que vamos a ver en este post son los siguientes:
* Mejora del código anterior.
* Carga de imagen adicional.
* Modificación del método Draw()
* Entrada de datos desde el teclado.
* Creación de constructores
* Carga de una imagen de Fondo.
Modificación del código anterior
Lo primero que vamos a hacer , como comenté al final del Post anterior es darle una arregladita al código para hacerlo más compacto. Lo primero será abrir nuestro proyecto del Post anterior y abrir el archivo AnimSprite.cs. Aquí lo que vamos a hacer es cambiar el nivel de acceso de nuestro arreglo SpriteMove a private para que solo la misma clase tenga acceso a él. Además, ya no va a ser un arreglo, si no una lista por dos razones, la primera es por que reduce bastante el código y se vuelve muy sencillo de entender y la segunda es para empezar a utilizar una forma de agrupar objetos diferente a los arreglos. Para conocer más sobre las listas agregué un post que puedes consultat llamado “Listas” debajo de “Consideraciones del C#” por si la sintaxis no es muy clara y quieres conocer mejor su uso. Con estos cambios la declaración quedaría así:
privateList<Texture2D> SpriteMov = newList< Texture2D>();
Ya que modificamos nuestro objeto lo siguiente es crear un nuevo método que se llamará CargaImagen() el cual tendrá el siguiente código:
public voidCargaImagen(Texture2D Sprite)
{
SpriteMov.Add(Sprite);
}
Es un código muy simple que lo único que hace es recibir un sprite de tipo Texture2D y cargalo a nuestra lista que acabamos de crear; Como comenté antes, utilizando listas el código se vuelve muy simple pues lo único que hace falta es utilizar el Método Add() de la lista para agregar un nuevo elemento. Por otro lado lo que vamos a hacer es abrir nuestra clase Engine.cs para modificar el código que ocupábamos para cargar las imágenes dentro de método LoadContent(), el cual va a quedar así:
for (int i = 1; i<=6; i++)
{
Megaman.CargaImagen(Content.Load<Texture2D>(”Sprites/MM”+ i));
}
Lo que hace este código es iterar seis veces (que es el número de imágenes que forman la animación) y aprovechando que las imágenes comparten el nombre común “MM” más un número. Podemos incrementar ese número en un ciclo y concatenarlo al final del nombre genérico para obtener el nombre de cada imagen a cargar. El resultado del Load lo pasamos a la función CargaImagen() que acabamos de hacer y listo ya tenemos un código mas eficiente para la carga de imágenes.
Carga de Imagen adicional
Antes de seguir adelante voy a explicar lo que vamos a hacer. Actualmente tenemos a nuestro Megaman corriendo sin parar en un fondo blanco y lo que queremos, obviamente, es tener el control del personaje y sus movimientos, por lo pronto y para este post nos limitaremos a desplazar al personaje hacia la izquierda y hacia la derecha utilizando las flechas de dirección del teclado. Para lograr esto hay algunas consideraciones a tomar.
Primero, los sprites que tenemos ahora son de una animación continua y es por eso que es necesario carga una imagen más para nuestro personaje de pie y sin avanzar.
Segundo, actualmente tenemos seis sprites para el movimiento de Megaman hacia la derecha y agregaríamos un nuevo sprite con lo que ya tendríamos siete, pero si queremos que el personaje se desplace también hacia la zquieda harán falta otros siete sprites de Megaman corriendo hacia la izquierda más uno de pie mirando también hacia la izquierda. Si abrimos nuestras imagines en un editor de imágenes, las volteamos y guardamos con un numero consecutivo tendríamos todas nuestros sprites, sin embargo para hacerlo de una manera más elegante vamos a ocupar los mismos sprites que tenemos y por código vamos a invertirlos hacia la izquierda si la animación es en esa dirección.
Para cargar la imagen seguiremos el proceso visto en el post anterior “Carga y animación sprites” y la imagen a cargar es la siguiente:.

Modificación del método Draw()
Ya que tenemos siente sprites cargadas en nuestro programa necesitamos modificar el ciclo for que acabamos de escribir para agregar una imagen más
for (int i = 1; i<=7; i++)
{
Megaman.CargaImagen(Content.Load<Texture2D>(”Sprites/MM”+ i));
}
Por otro lado para lograr voltear la imagen necesitamos hacer varias modificaciones. Primero vamos a cambiar el método Draw() de nuestra clase AnimSprite.cs ya que anteriormente solo llamábamos al metodo Draw() del spriteBatch, con el vector de posición y un color de mascara pero para voltear la imagen tenemos que llamar a otra versión sobrecargada del mismo método Draw() que utiliza otros parámetros. Lo primero que necesitamos es declarar una nueva variable dentro de nuestra clase AnimSprite.cs de tipo entero que llamaremos UltimaDir.
public int UltimaDir;
Esta variable la vamos a utilizar de la siguiente manera: si se presionó la tecla de dirección derecha le asignaremos el valor de 0 y si fue presionada la tecla de dirección izquierda la asignaremos el valor de 1. (abajo viene la explicación de como cachar estos eventos). Utilizando esta variable nuestro método Draw() quedaría así:
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
if(UltimaDir== 0) //Derecha
spriteBatch.Draw(SpriteMov[FrameActual], new Rectangle(XPos, YPos,
SpriteMov[FrameActual].Width,SpriteMov[FrameActual].Height), null,Color.White,
Angulo,Origen, SpriteEffects.None,0f);
else if(UltimaDir== 1) //Izquiera
spriteBatch.Draw(SpriteMov[FrameActual], newRectangle(XPos, YPos,
SpriteMov[FrameActual].Width, SpriteMov[FrameActual].Height), null, Color.White,
Angulo, Origen, SpriteEffects.FlipHorizontally,0f);
spriteBatch.End();
}
Lo que hacemos es dividir el código en dos opciones, la primera es cuando la última tecla de dirección presionada fue la derecha (UltimaDir ==0) y la segunda es cuando la última tecla de dirección fue la izquierda (UltimaDir ==1). Para el primer caso vamos a mostrar los sprites en su forma normal, sin embargo para mostrar congruencia en los dos casos he llamado al mismo método sobrecargado de Draw() al que le vamos a pasar los siguiente parámetros:
Primero: new Rectangle(XPos, YPos, SpriteMov[FrameActual].Width,
SpriteMov[FrameActual].Height), En lugar de ocupar el vector de posición que ocupábamos en el post pasado esta sobrecarga utiliza un objeto Rectangle que lo que va hacer es colocar nuestro sprite dentro de un rectángulo definido por cuatro puntos que corresponden a la esquina superior izquierda, el largo y el ancho del mismo, de tal forma que si definimos un rectángulo de diferentes dimensiones que nuestro sprite este se acoplará a las dimensiones del mismo pero como para nuestro caso queremos mantener las dimensiones originales, vamos a hacer lo siguiente: En lugar de nuestro vector de posición vamos a declarar un par de variables enteras que nos servirán para definir la posición de nuestro personaje
public int XPos, YPos;
A continuación para definir las dimensiones del rectángulo vamos a utilizar las dimensiones del sprite para garantizar que mantendrá su forma al momento de dibujarlo en pantalla. Para eso es que se utilizan los parámetros Width y Height de nuestro sprite a dibujar
Segundo: null. El segundo parámetro es un objeto también del tipo Rectangle que se utiliza cuando se quiere dibujar solo una porción del sprite. Esta porción será la que contenga el rectángulo que definamos, pero para nuestro caso, enviamos un valor nulo para indicar que dibujaremos el sprite completo.
Tercero: Color.White El color de mascara que es el mismo que en la sobrecarga anterior y que seguirá siendo blanca osea sin mascara.
Cuarto: Angulo Este parámetro es el ángulo de rotación de nuestro sprite, en caso de que quisiéramos rotarlo ; Para este parámetro vamos a tener que declarar una nueva variable donde almacenaremos un ángulo de rotación para cuando haga falta.
public float Angulo;
Quinto: Origen , Al definir la rotación de un objeto es necesario definir el pivote u origen con respecto al cual se va a efectuar esta rotación, es por eso que vamos a declarar un vector que almacene ese origen de rotación y se lo vamos a pasar como parámetro.
public Vector2 Origen;
Sexto: SpriteEffects.None Y finalmenta la razón por la cual utilizamos esta sobrecarga: el efecto, que para el caso del avance hacia la derecha es None (porque permanecerá sin cambios) pero para cuando avanza a la izquierda será FlipHorizontally. Y que es esto? Pues este parámetro nos indica si el sprite será dibujado sin alteración (SpriteEffects.None) o volteado sobre el eje horizontal (SpriteEffects.FlipHorizontally).
Septimo : 0f . Este parámetro es un número flotante que indica la profundidad del sprite dentro de la pantalla, esto se utiliza cuando tenemos varios sprites en pantalla para definir cual va arriba de cual. Para nuestro caso mandamos 0 para poner el personaje más enfrente en caso de que hubiera otros sprites.
Entrada de datos desde el teclado
Para cachar la entrada de datos desde el teclado vamos a escribir la siguiente línea dentro nuestro método Update() y dentro de nuestra if que controla el tiempo
KeyboardStatekState= Keyboard.GetState();
Lo que estamos haciendo aquí es declarar un objeto que llamaremos kState de la clase KeyboarState.
Esta clase ya nos hace toda la chamba para la verificación del estado de las teclas ya que al llamar al método GetSatate() podremos preguntar si una tecla fue presionada o no. Lo que necesitamos ahora es checar el estado de las teclas que nos interesan, que son las teclas de dirección izquierda y derecha pero que hacemos después? Por ejemplo, si presionamos la tecla de dirección derecha lo que queremos es que Megaman se desplace hacia la derecha pero también que la animación empiece a correr y nos muestre un frame tras otro y que al terminar los seis, vuelva con el primero como hicimos en el ejemplo anterior, además queremos que al dejar de presionar la tecla izquierda se detenga el desplazamiento, la animación y que se despliegue el sprite de Megaman parado y viendo hacia la derecha. Para hacer esto necesitamos saber si la tecla de dirección izquierda esta presionada en el momento de la verificación pero también necesitamos saber si fue presionada antes o mejor dicho si no ha dejado de ser presionada. Para verificar esto necesitamos almacenar en una variable el estado anterior de la tecla al momento de hacer una nueva verificación y para hacer eso vamos a utilizar la variable UltimaDir que declaramos antes. Utilizando esta variable podemos cambiar el código que utilizábamos para modificar la animación de Megman por el siguiente código dentro del método Update() de la clase Engine.cs
if (kState.IsKeyDown(Keys.Right) && kState.IsKeyUp(Keys.Left))
{
Megaman.XPos +=10;
if (Megaman.UltimaDir == 0)
Megaman.UpdateMov();
Megaman.UltimaDir = 0;
}
else if (kState.IsKeyDown(Keys.Left) && kState.IsKeyUp(Keys.Right))
{
Megaman.XPos -= 10;
if (Megaman.UltimaDir == 1)
Megaman.UpdateMov();
Megaman.UltimaDir = 1;
}
else
{
Megaman.FrameActual = 6;
}
Lo que hacemos aquí es utilizar nuestro objeto kState que acabamos de instanciar y vamos a utilizar el método isKeyDown() que recibe como parámetro la tecla a evaluar a través de un objeto Keys. Lo que preguntamos en el primer if es si la tecla de dirección derecha fue presionada (IsKeyDown) pero la tecla de dirección Izquierdo no fue presionada (IsKeyUp) Si se cumple esta condición entonces desplazaremos a nuestro personaje diez pixeles a la derecha. Además si no se a soltado la tecla de dirección derecha (UltimaDir == 0) pasaremos al siguiente frame de nuestra animación.
Por último guardamos en nuestra variable UtimaDir que la tecla de dirección derecha fue presionada.
Para la siguiente condición se utiliza la misma lógica pero aplicada a la condición de que se haya presionado la tecla de dirección Izquierda y no la Derecha.
Finalmente si no se presionó ninguna de las dos teclas, seteamos nuestro frame actual a 6 que es donde se encuentra nuestro personaje parado y según la última dirección se dibujará viendo a la derecha o a la izquierda.
Creación de constructores
Ya que agregamos nuevos parámetros a nuestra clase AnimSprites.cs lo que quiero hacer es crear un nuevo constructor de la clase, primer porque quiero dejar algunos fijos en un constructor por default y segundo para tener la posibilidad de definir parámetros al crear un nuevo objeto. El primer constructor quedaría de la siguiente manera:
publicAnimSprite()
{
XPos= 200;
YPos= 200;
Angulo=0;
Origen= new Vector2(0,0);
}
Este constructor será nuestro constructor por default y será llamada cuando no definamos ningún parámetro. Lo que hace es copiar en
nuestros parámetros del objeto los valores que quiero que tenga al crear cualquier objeto.
Ahora vamos a crear el segundo constructor que tendrá el siguiente
código:
publicAnimSprite(int PosX, int PosY,float Angle, Vector2 Origim)
{
XPos = PosX;
YPos = PosY;
Angulo = Angle;
Origen = Origim;
}
A diferencia del anterior, este constructor recibe cuatro parámetros,
los cuales definen el estado iniciar del objeto a crear. Este
constructor no lo ocuparemos para este ejemplo pero puedes empezar a
hacer pruebas con él.
Carga de una imagen de Fondo.
Si ejecutas el programa ya podrás controlar a Megaman en ambas direcciones, sin ningún límite, es decir, puedes salirte de la pantalla en ambos lados. Lo que vamos a hacer ahora es cargar una imagen de fondo para olvidarnos del aburrido fondo blanco. Para eso he modificado una imagen que descargue del sitio Sprite-Resource para que ocupe toda la pantalla. La imagen en cuestión es la siguiente:

Para cargar esta imagen vamos a hacer lo siguiente: Primero hay que cargar la imagen con el método que ya conocemos. Enseguida declaramos un objeto Texture2D dentro de nuestra clase Engine.cs para guardar ahí la imagen.
private Texture2D Fondo1;
A continuación agregamos el código para copiar la imagen en el objeto; esto lo haremos dentro del método LoadContent().
Fondo1 = Content.Load<Texture2D>(”Sprites/Fondo1“);
Y por último vamos a agregar el código para dibujar el fondo en pantalla; Este código irá dentro del método Draw() y deberá copiarse antes del código que dibuja a nuestro personaje.
spriteBatch.Begin();
spriteBatch.Draw(Fondo1, new Vector2(0,0),Color.White);
spriteBatch.End();
Lo que hacemos aquí es colocar la imagen de fondo en la coordenada (0,0) para que ocupe toda la pantalla.
Por último y para que haya congruencia del escenario con el personaje voy a modificar las coordenadas de inicio dentro del constructor por default de nuestro personaje para que queden así:
XPos = 100;
YPos = 395;
Y listo, ya puedes controlar a Megaman con un escenario de fondo. Claro que ahorita no hay control de colisiones así que puede atravesar paredes pero eso lo resolveremos en otro Post.
Si todo salió bien deberías de ver una imagen como la siguiente en pantalla completa:

Puedes bajar todo el proyecto y las imagenes de aqui (Proyecto Completo) o puedes bajar solo las imagenes para armar tú mismo el proyecto desde esta liga (Sprites2 de Megaman)
Espero que haya sido de su agrado este ejemplo y que haya quedado claro, como siempre si hay alguna duda no olvides postearla, tampoco olvides tus comentarios antes de salir. Gracias y nos vemos en el siguiente post.
Piroshi.
Tags: Sprites,
xna