Siguiendo con los post donde explico como creé el Tetris voy a pasar a explicar como crear una base para tener unas buenas clases para manejar los sprites, tanto los controlados por el usuario como los que son manejados automáticamente.

Todo sprite tiene una serie de atributos que lo definen que forman parte de todos los sprites y son los que implementé en la clase SpriteBase de la cual luego derivaran las demás.

Lee el resto de esta entrada »

Si deseo crear un juego tarde o temprano tendré que verme las caras con la creación de sprites para el juego y sin duda aunque con talento salen mucho mejor estas cosas siempre me han gustado.

Es cierto, le tenía ganas ya al PixelArt y a la animación y gracias a los sprites en juegos he podido tener dos tazas de cada una. No ha sido demasiado problemático excepto al principio para encontrar unas proporciones que sean de mi gusto y no crear estrambóticos diseños.

La creación la he realizado con GIMP y ha sido bastante sencillo gracias al tratamiento de que dispone. Trabajo por capas, cada capa es un fotograma de la animación, una vez todo hecho un bonito fondo para que no se superponga al hacer el .gif y listo. Una vez comprobado el resultado solo restaría crear la malla para utilizar en XNA, en mi caso concreto ahora mismo.

El resultado es gracioso y le tengo bastante cariño, pese a no ser gran cosa… Pero me ha dado una experiencia mínima en esto de la animación que sin duda voy a explotar tanto como pueda.

Prueba de creación de sprite

En tener tiempo e ideas claras empezaré con la creación de personajes que ya serán utilizados con los fines en que empezó esto.

Una vez ya sabemos realizar animaciones con XNA y lo probamos nos damos cuenta de que algo falla porque la velocidad entre sprites es tan brutal que le va a dar una rampa en cualquier momento.

Por supuesto, esto se soluciona de un modo bastante sencillo que da mucho juego si se planea utilizarlo del mejor modo para nuestros propósitos.

Primero tendremos que crear los atributos necesarios para controlar la velocidad con la que se refresan los sprites en pantalla.

int tiempoUltimoFrame = 0;
int tiempoMSegundos = 100;

Con tiempoUltimoFrame guardaremos el tiempo transcurrido desde el último sprite dibujado mientras que en tiempoMSegundo tenemos asignado el tiempo que debe pasar entre sprites.

Ahora solo queda modificar el método Update para que dibuje cuando queremos cada sprite.

tiempoUltimoFrame += gameTime.ElapsedGameTime.Milliseconds;
if (tiempoUltimoFrame > tiempoMSegundos)
{
tiempoUltimoFrame = 0;
if (++currentFrame.X >= sheetSize.X)
{
currentFrame.X = 0;
}
}

He reutilizado el código sobre animación para ahorrarme trabajo dando supuesto que ya sabéis de que va. Simplemente con gameTime.ElapsedGameTime.Milliseconds controlamos el tiempo transcurrido y cuando es mayor al deseado se pasa al siguiente sprites restaurando el valor de tiempoUltimoFrame.

XNA: Animación

enero 27, 2009

Normalmente a la hora de hacer una animación con sprites se sueles utilizar lo que se denomina un sheet sprites que viene a ser una hoja de sprites.

Esta hoja de sprites es una cuadrícula donde se ponen de forma sucesiva cada uno de los sprites que queremos que formen la animación de modo que tan solo tengamos que movernos por el sprite y no ir cambiando de sprite. De este modo se gana velocidad y sencillez a la hora de animar.

Para poder dar un ejemplo de animación he optado por utilizar un sheet sprite ya hecho por Wishthechao y serán los sprites del personaje andando de cara.

wish

Como con cualquier textura la cargamos en el Content y la asignamos a una Textura2D. Una vez esto ya podemos pasar al tema de la animación.

Lo primero es saber como moverse dentro de la textura por ello utilizaremos tipo de dato Point como atributo de clase por su utilidad al disponer de X e Y.

Point frameSize = new Point(27, 40);
Point currentFrame = new Point(0, 0);
Point sheetSize = new Point(4, 0);

frameSize es el tamaño del sprite en ese frame, currentFrame cuenta los sprite ya dibujados hasta el frame actual y sheetSize como indica su nombre es el tamaño de la hoja de sprites.

Para dibujar los sprites utilizaremos el metodo de spriteBatch.Draw que venimos utilizando solo que en rectangle? Source Rectangle ya no pondremos null sino el tamaño del sprite que compone la hoja de sprites.

spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.BackToFront,
SaveStateMode.None);

spriteBatch.Draw(texture, Vector2.Zero,
new Rectangle(currentFrame.X * frameSize.X,
currentFrame.Y * frameSize.Y,
frameSize.X, frameSize.Y),
Color.White, 0f, Vector2.Zero,1f, SpriteEffects.None, 0f);

spriteBatch.End();

Como podéis observar el rectángulo creado para delimitar la parte visible del sheet sprite se forma a partir de las dos variables creadas anteriormente, currentFrame y frameSize. De este modo nos vamos moviendo a traves del sheet sprite sin tener que modificar el método Draw.

Aun falta la parte más importante que es la que generará el movimiento ya que de momento solo dibujará el primer sprite de la hoja de sprites.

Para ello debemos ir al método Update, sistema nervioso del juego, y hacer que con cada iteración vaya cambiando la posición del rectangulo que delimita el sprite en la hoja de sprites y que una vez recorrido los sprites deseados vuelva al inicio creando esa continuidad de movimiento deseada en este ejemplo.

if (++currentFrame.X >= sheetSize.X)
{
currentFrame.X= 0;
}

Dado que en este sheet sprite no nos movemos verticalmente para hacer la animación no es necesario modificar la componente Y en ningún caso pero de ser así sería igual de sencillo.

if (++currentFrame.X >= sheetSize.X)
{
currentFrame.X= 0;
if (++currentFrame.Y >= sheetSize.Y)
{
currentFrame.Y= 0;
}
}

Eso si, debemos indicarlo correctamente en sheetSize para que haga el recorrido deseado.

El movimiento es algo tan básico para un juego que no existe ningún juego sin movimiento. Por ello, considero que de aquí en adelante empieza el conocimiento hacía el objetivo que tengo marcado. Un juego.

Lo primero a entender en el movimiento en XNA es que todo lo relacionado con el movimiento se desarrolla en el método Update. Y no solo el movimiento sino la detección de colisiones, las posiciones, la inteligencia artificial… En definitiva, todo sucede ahí. Es el cerebro del juego, su sistema nervioso.

Para mover un sprite debemos primero crear y dar valor a su posición. Así pues crearemos el atributo pos del tipo Vector2 y también necesitaremos definir a que velocidad se moverá el sprite. Para ello utilizaremos un float.

float speed = 2f;
Vector2 pos = new Vector2(0,0);
//también puede inicializarse así: Vector2 pos = Vector2.Zero;

Doy por supuesta la carga del sprite y me voy a centrar en hacer un simple ejemplo de movimiento de un sprite por la ventana. En este ejemplo haremos que el sprite de vueltas por el borde de la venta de forma indefinida de forma que hará la forma de un cuadrilátero su recorrido.

Todo lo que ponga a partir de este momento va dentro del método Update.

if(pos1.X < Window.ClientBounds.Width -texture.Width && pos1.Y == 0){
pos1.X += speed1;
}else if(pos1.X >= Window.ClientBounds.Width - texture.Width && pos1.Y < Window.ClientBounds.Height - texture.Height){
pos1.Y += speed1;
}else if(pos1.Y >= Window.ClientBounds.Height - texture.Height && pos1.X > 0){
pos1.X -= speed1;
}else if(pos1.Y <= Window.ClientBounds.Height + texture.Height && pos1.X == 0){
pos1.Y -= speed1;
}

Es bien sencillo el código sin duda. El sprite puede hacer 4 movimientos solamente: Desplazarse a la derecha, hacía abajo, a la izquierda o hacía arriba y siempre bajo una condiciones bien concretas. He elegido que haga un recorrido en el sentido de las agujas del reloj.

Se debe siempre mantener la perspectiva y contar con las X y las Y en todo momento aunque solo queramos mover una de las dos. Nos movemos en un plano y por tanto, ambas son importantes.

Esto por supuesto también es valido para los textos o cualquier objeto que deseemos mover.

Al dibujar multiples sprites en pantalla podemos debemos dibujarlos en un solo bloque SpriteBatch Begin-End a ser posible para mejorar cuestiones de velocidad de dibujado.

Por tanto, a ser posible, la mejor opción es dibujar todo el juego utilizando solo un objeto SpriteBatch.

XNA: Cargar un sprite

noviembre 28, 2008

Lo primero que uno se da cuenta nada más empezar un proyecto nuevo con XNA es que ya funciona. Si exacto, ya sale una ventana perfectamente. Esto me recuerda las paranoias que había que montarse con C++ y GLUT para poder lograr sacar una simple ventana. Ya había leído esto antes de empezar y lo comprobé. Realmente muy bueno aunque claro esta, se trata de código autogenerado, no le demos mayor importancia.

Nada más empezar observamos distintas cosas. La primera es que los métodos principales ya están medio implementados automáticamente, lo cual, facilita enormemente la orientación a la hora de empezar y ayuda a entender que y como estamos haciendo las cosas.

Una vez orientados y mínimamente familiarizados con el lenguaje C# y el IDE lo primero que hay que hacer es, pulsar F1. Por primera vez en todo el tiempo que llevo utilizando Microsoft Windows, la ayuda sirve de algo y en este caso concreto, es mi guía personal de aprendizaje de XNA.

La ayuda de Visual Studio C# 2008 Express es muy buena gracias al estar conectado directamente si disponemos de internet a MSDN cosa que ayuda pues no hay que liarse con la web y podemos ir directamente a lo que nos interesa.

De momento, voy a ir siguiendo paso por paso, uniendo o separando, a mi modo de ver cada uno de los mini-tutoriales disponibles que son, de gran ayuda, aunque de mayor ayuda es aun los mini-ejemplos ya implementados. Sin lugar a dudar, un 9,5/10 para Microsoft, sería un 10 si estuviese en distintos idiomas.

Empezaré con cargar un sprite que viene a resumir los dos primeros puntos. Omitiré ciertas cosas en las explicaciones (XBox360, zune, etc) pero siempre será 100% funcional lo descrito aquí.

Lo primero (dejando de lado crear un proyecto la plataforma Windows) es pode añadir el contenido que utilizaremos en nuestro proyecto. Nada más fácil. Existen 2 formas de añadir contenido, como recurso o como link a recurso.

Para añadir un contenido como recurso pulsamos botón derecho en el panel de soluciones y pulsamos en agregar objeto existente. Una vez seleccionado el fichero le damos a agregar. Simple. Si por contra deseamos añadirlo como link a recurso seguimos exactamente los mismos pasos pero en el botón de agregar, si nos fijamos, vemos que hay una flecha. Al pulsarla saldrá un menú contextual y podremos elegir entre agregar o agregar como link. Seleccionamos como link y ya esta. Más que simple.

Se recomienda la segunda forma a no ser que sea un proyecto en el que solo intervengas tu según dice la ayuda. Desde el principio a mi me pareció la mejor opción sin duda.

Para empezar la primera prueba con un sprite, añadiremos un sprite en mi caso un JPEG. En propiedades del recurso podremos renombrarlo y ver que tipo de paquetería se utiliza del framework entre otras cosas. Interesante saberlo.

Pasemos a cargar un sprite en pantalla siguiendo el tutorial de F1 (xD).

Lo primero es crear los atributos, privados, tanto para la textura 2D como para la capa de dibujo de la textura como el recuadro de seguridad para no salirse de las dimensiones del dispositivo, en este caso pantalla. Los atributos son atributos de clase.

private Texture2D SpriteTexture;
private SpriteBatch ForegroundBatch;
private Rectangle TitleSafe;

Una vez tenemos definidos los atributos que utilizaremos para cargar y manejar el sprite vamos al método overrided LoadContent para crear la nueva capa y se cargará el sprite que deseemos. Luego calcularemos el área visible del dispositivo (pantalla).

ForegroundBatch = new SpriteBatch(graphics.GraphicsDevice);
SpriteTexture = Content.Load("sprite");
TitleSafe = GetTitleSafeArea(.8f);


Content.Load
especifica el modelo que utilizaremos, en este caso Texture2D entre mayor/menor y entre paréntesis el nombre del recursos que se encuentra en Content. Para saber el nombre debemos ir a propiedades del recurso aunque de normal es el nombre del recurso. Respecto a TitleSafe, en él se guarda en que posiciones se puede dibujar con seguridad llamando al metodo GetTitleSafeArea que escribiremos a continuación.

protected Rectangle GetTitleSafeArea(float percent){
Rectangle RetVal = new Rectangle(graphics.GraphicsDevice.Viewport.X,
graphics.GraphicsDevice.Viewport.Y,
graphics.GraphicsDevice.Viewport.Width,
graphics.GraphicsDevice.Viewport.Heigth);
return RetVal;
}

Al método se le pasa como argumento el porcentaje del dispositivo que se verá aunque en este caso, al tratarse de una pantalla de monitor, el porcentaje es siempre 100% por ello, le pasamos directamente los valores que debe tener gracias al framework que lo facilita.

Ahora solo queda dibujar el sprite en la capa que hemos creado y especificar la posición que deseamos que sea dibujado utilizando el método Draw.

GraphicsDevice.Clear(Color.CornFlameBlue);
ForegroundBatch.Begin();
Vector2 pos = new Vector2(TitleSafe.Left, TitleSafe.Top);
ForegroundBatch.Draw(SpriteTexture, pos, Color.White);
ForegroundBatch.End();
base.Draw(gameTime);

Primero hemos limpiado la pantalla decidiendo el color de fondo. Luego inicializamos la capa y creamos un vector pos del tipo Vector2 en el que indicamos la posicion (X,Y) en la que se dibujará el sprite y lo dibujamos.

ACTUALIZACIÓN(03.12.08): para concretar la posición (X,Y) que deseamos se utiliza el TilteSafe.X = int x y TitleSafe.Y = int y siendo el lateral izquierdo-derecha el (0,0) tal que: Vector2 pos = new Vector2(TitleSafe.X = 300, TitleSafe.Y = 400);

ACTUALIZACIÓN(19.01.09): el paramentro Color.White es para teñir la imagen de modo que si ponemos Color.Green la imagen se ve verde. Con White no se modifica.