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á:
Código fuente
Descargar AnimarSprites.zip 191Kb (XNA 3.0)
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
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:
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!
Video

David gracias por el tuto
pero, mi pregunta es: con que programa puedo hacer las hojas de sprite. cual me recomiendas
Yo utilicé el photoshop para unirlas todas, pero es algo manual, seguro que existen algunos que te facilitan la vida en esa tarea pero nunca he utilizado ninguno jeje asi que te tocará buscar por google
jaja, no te entendi nada
jajajajaj alguna duda un poco más concreta?
no es mas facil buscar un programa como graphics gale para animar u sprite sheet
Pero si lo animas con un programa externo entonces te guardará un .gif no? XNA no trae soporte por defecto para cargar archivos .GIF asi que tendrías que hacerlo tu y creo que eso es algo más complicado que animar el sprite tu mismo
(L) hahah gracias
Esta forma es más eficiente ya que todos los frames se encuentran en una única textura, si quieres ver otro ejemplo similar visita la web de xna creators donde hay uno muy parecido a este
gracias por la respuesta, ahora lo entendi bien, pero acabo de ver otro modo de hacer esto, con los sprites de un mismo mono pero en archivos individuales y los manejas mediante un FOR (mono01.png, mono02.png… mono0n.png, etc )cual sera la mejor manera de mover sprites entre estas 2, amigo David? podrias hacer un mini tuto para esta otra forma?
… chaop
Si cambias el sprite sheet tienes que hacer modificaciones, verás, en este ejemplo se divide el ancho del sprite sheet entre el numero de frames que has puesto, asi que si la animación tiene 6 frames, y cada frame digamos que tiene un ancho de 40píxeles, tendrás que crear una imagen de 240píxeles de ancho y poner cada frame entre series de 40-40 píxeles para que todo funcione
bueno, aprendi a usar la clase ke hiciste, pero sigo sin entenderla bien hahaha, eso si, no me resulta al cambiar el sprite sheet
, trate con uno de akuma pero no todos tienen el mismo ancho (akuma parado mide menos ke akuma lanzando un combo)… podrias ayudarme
, gracias
aca esta el akuma
http://planetrenders.net/renders/displayimage.php?pos=-10408
Cualquier duda que tengas puedes usar el foro para ello
la verdad es que me ha costado entender esta tuto U_U