Animar Sprites usando una hoja de sprites(Sprite Sheet)

Saludos a todos, hoy vengo con un nuevo tutorial muy sencillo y que espero que os resulte útil en vuestros juegos 2D.

A continuación os mostraré como animar sencillas texturas 2D para dotarlas de movimiento y poder crear personajes que caminen, guerreros que luchen o cualquier cosa que se os ocurra, vamos allá:

Antes de nada tenemos que conseguir una hoja de sprites, o en inglés, una sprite sheet, que no es más que una serie de imágenes todas ellas en el mismo archivo. Para este ejemplo he usado una del famoso pirata de la saga Monkey Island, Guybrush Threepwood🙂 , aquí os dejo una miniatura para que os hagáis una idea de como es. (El archivo a tamaño completo está en la carpeta content del ejemplo dentro del .zip)

Sprite Sheet

Sprite Sheet

Ahora vamos con la idea básica para realizar la animación. Como podéis ver arriba en la imagen, tenemos 12 imágenes de Guybrush andando, cada una es el siguiente “paso” de la anterior y así hasta llegar a la última donde el ciclo vuelve a empezar por la primera imagen de nuevo. Así que básicamente lo que tendremos que hacer será indicarle a XNA que imagen mostrar, cada cuanto tiempo pasamos a la siguiente imagen, y cual es la siguiente imagen. En la siguiente imagen podéis ver como he numerado los frames o imágenes del archivo:

Nomenclatura de frames

Nomenclatura de frames

Pues con todo esto en mente vamos a empezar, lo primero que haré será crearme una clase auxiliar llamada TexturaAnimada, que será la encargada de ver que frame nos toca pintar y cuales son sus coordenadas dentro del archivo.

Aquí tenéis el código completo:

public class TexturaAnimada
{
private int framecount;
private Texture2D myTexture;
private float timePerFrame;
private int frame;
private float totalElapsed;

public Vector2 position;

public TexturaAnimada(Vector2 pos, int frameCount, int framesPerSec)
{
position = pos;
framecount = frameCount;
timePerFrame = 1.0f/ framesPerSec;
frame = 0;
totalElapsed = 0;

}

public void Load(ContentManager content, string textName)
{
myTexture = content.Load(textName);

}

public void UpdateFrame(float elapsed)
{
totalElapsed += elapsed;
if (totalElapsed > timePerFrame)
{
frame++;
//Los frames son circulares, al llegar al último volvemos al primero.
frame = frame % framecount;
totalElapsed -= timePerFrame;
}
}

public void DrawFrame(SpriteBatch batch)
{
DrawFrame(batch, frame);
}

public void DrawFrame(SpriteBatch batch, int frame)
{
int FrameWidth = myTexture.Width / framecount;

Rectangle sourcerect = new Rectangle(FrameWidth * frame, 0,
FrameWidth, myTexture.Height);

batch.Draw(myTexture, position, sourcerect, Color.White,
0, new Vector2(0,0), 1, SpriteEffects.None, 1);
}

}

Empecemos mirando el constructor:

  • Primero establecemos la posición donde queremos pintar el sprite
  • A continuación establecemos el número de frames.
  • Calculamos el tiempo que será mostrado cada frame (Divido 1 segundo entre el numero de frames por segundo que desee).
  • Y finalmente decimos que el frame actual es el 0 y que el tiempo transcurrido también es 0.

El método Load es bastante sencillo, simplemente le pasamos el content desde la clase Game1 y el nombre de la imagen para que la cargue.

En el método UpdateFrame recibe el tiempo que ha pasado desde la última vez que fue llamado y comprueba si ya hemos sobrepasado el tiempo de un frame para cambiarlo al siguiente.

El método DrawFrame nos servirá para localizar el frame a dibujar dentro del sprite sheet, para ello dividimos el ancho del sprite sheet entre el numero de frames para saber cuanto es el ancho de cada uno. Y el rectángulo sourcerect se encarga de seleccionar el frame a pintar. Una vez tenemos todo, lo pintamos por pantalla con el método Draw.

En el archivo Game1.cs tengo lo siguiente:

public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private TexturaAnimada SpriteTexture;
        private Vector2 spritePos;
        private const int FRAMES = 12;
        private const int FRAMESPERSEC = 10;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            spritePos = new Vector2(graphics.PreferredBackBufferWidth/2,50);
            SpriteTexture = new TexturaAnimada(spritePos, FRAMES, FRAMESPERSEC);

        }

        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);

            SpriteTexture.Load(Content, "guy");

            // TODO: use this.Content to load your game content here
        }
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {

            // TODO: Add your update logic here
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            SpriteTexture.UpdateFrame(elapsed);
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            SpriteTexture.DrawFrame(spriteBatch);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }

Aquí poco nuevo hacemos, básicamente definimos las constantes de número de frames y de frames por segundo, creamos el objeto de la clase TexturaAnimada y la iniciamos. El resto es cargar la textura en el Load(), actualizar el frame pasándole el tiempo transcurrido y pintar el frame actual, todo eso manejado por la clase auxiliar que nos creamos al principio.

Como puede verse no es muy complicado y el resultado es más que suficiente, próximamente os contaré como animar al personaje mediante el teclado, para que ande de izquierda a derecha y cosas así, os dejo un video de como queda, un saludo y hasta la próxima!

Código fuente

Descargar AnimarSprites.zip 191Kb (XNA 3.0)

Descargar AnimarSprites.zip 191Kb (XNA 4.0)

Video

34 pensamientos en “Animar Sprites usando una hoja de sprites(Sprite Sheet)

  1. Buenas, una duda no seria mas sencillo utilizar 1 timer de milisegundos, y 1 Switch? Ejemplo: cuando el timer este en 0.5 algun valor = 1 en 1.0 valor = 2 y en el Switch
    Case 1: Usar frame 1
    Case 2: Usar frame 2
    y asi y si el personaje muere o se presiona alguna tecla este timer se detiene y se ejecuta otro timer (o el mismo) reproduciendo otra secunecia

    Desde ya muchas gracias

  2. Hola primero que nada agradecerte me parecen excelentes tus tutoriales quería hacerte una pregunta he creado la animación de un personaje tanto a la izquierda como a la derecha pero me gustaría añadir una animación a la hora de brincar y no se si puedes guiarme un poco o explicarme de que forma hacer que el personaje ejecute diferentes
    animaciones al presionar diferentes teclas
    Gracias

Responder

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