Animaciones de Sprites – XNA


Para los videojuegos, no solo basta con tener un fondo en movimiento, los personajes también deben simular un movimiento casi real.

En el cine, como en los videojuegos, para hacer una animación se tienen varios “frames” o fotogramas, que al pasarlos rápidamente dan la sensación de animación.

 

Vamos a crear una clase que nos permita ir adicionándole las imágenes para la animación, voy a usar el mismo proyecto creado anteriormente, luego cargo mis súper imágenes:

 

Ahora adicionamos una clase y la llamamos animación, antes de crear la clase, creamos una estructura para guardar la textura de la imagen:

struct ImagenAnimacion
{
public Texture2D imagen;
}
//Ahora nuestra clase:
class Animacion
{
      Int32 imagenActual = 0;
      Boolean repitiendo = false;
      Boolean detenido = false;
      Boolean moviendo = false;
      Int32 frames = 0;
      Int32 totalframes = 0;
      float tiempoCambio = 0.0f;
      float tiempoTotal = 0.0f;
      Int32 Inicio = 0;
      Int32 Fin = 0;
      List<ImagenAnimacion> listaImagenes = new List<ImagenAnimacion>();
      Vector2 posicion;
      float escala = 1.0f;
      SpriteEffects efectos = SpriteEffects.None;
      private SpriteBatch batch;
      public float Escala
      {
            set
            {
                  escala = value;
            }
      }
      Vector2 origen = Vector2.Zero;
      public Vector2 Origen
      {
            set
            {
                  origen = value;
            }
      }

      public Animacion(GraphicsDeviceManager graficos, Vector2 _posicion)
      {
            this.posicion = _posicion;
            this.batch = new SpriteBatch(graficos.GraphicsDevice);
      }

      public void adicionarImagen(Texture2D _imagen, Int32 _frames)
      {
            ImagenAnimacion Celda = new ImagenAnimacion();
            Celda.imagen = _imagen;
            totalframes = _frames;
            listaImagenes.Add(Celda);
      }

      public void ponerPosicion(float x, float y)
      {
            posicion.X = x;
            posicion.Y = y;
      }

      public void moverIzquierda(Int32 velocidad)
      {
            posicion.X -= velocidad;
      }
      public void moverDerecha(Int32 velocidad)
      {
            posicion.X += velocidad;
      }
      public void moverArriba(Int32 velocidad)
      {
            posicion.Y -= velocidad;
      }
      public void moverAbajo(Int32 velocidad)
      {
            posicion.Y += velocidad;
      }
      public void Detener()
      {
            if (moviendo)
            {
                  detenido = true;
                  repitiendo = false;
                  moviendo = false;
                  tiempoTotal = 0.0f;
                  tiempoCambio = 0.0f;
            }
      }
      public void irA(Int32 numero)
      {
            if (moviendo)
                  return;
            if (numero < 0 || numero >= listaImagenes.Count)
                  return;
            imagenActual = numero;
      }
      public void iniciarTodo(float segundos, Boolean repetir)
      {
            if (!moviendo)
            {
                  irA(0);
                  detenido = false;
                  moviendo = true;
                  repitiendo = repetir;
                  Inicio = 0;
                  Fin = listaImagenes.Count - 1;
                  tiempoCambio = segundos / (float)listaImagenes.Count;
            }
      }
      public void iniciar(Int32 _inicio, Int32 _fin, float segundos, Boolean repetir)
      {
            if (!moviendo)
            {
                  irA(_inicio);
                  detenido = false;
                  repitiendo = repetir;
                  moviendo = true;
                  this.Inicio = _inicio;
                  this.Fin = _fin;
                  float diferencia = (float)_fin - (float)_inicio;
                  tiempoCambio = segundos / diferencia;
            }
      }
      public void Dibujar()
      {
            if (listaImagenes.Count == 0 || imagenActual < 0 || listaImagenes.Count < imagenActual)
                  return;
            batch.Begin();
            if (totalframes > 0)
            {
                  Int32 anchoFrame = listaImagenes[imagenActual].imagen.Width / totalframes;
                  Rectangle rectImagen = new Rectangle(anchoFrame * frames, 0, anchoFrame, listaImagenes[imagenActual].imagen.Height);
                  batch.Draw(listaImagenes[imagenActual].imagen, posicion, rectImagen, Color.White, 0.0f, origen, new Vector2(escala, escala), efectos, 0.0f);
            }
            else
            {
                  batch.Draw(listaImagenes[imagenActual].imagen, posicion, null, Color.White, 0.0f, origen, new Vector2(escala, escala), efectos, 0.0f);
            }
                  batch.End();
      }
      public void Actualizar(GameTime gameTime)
      {
            if (!detenido)
            {
                  tiempoTotal += (float)gameTime.ElapsedGameTime.TotalSeconds;
                  if (tiempoTotal > tiempoCambio)
                  {
                        if (totalframes > 0)
                        {
                              frames++;
                              tiempoTotal = 0.0f;
                              if (repitiendo)
                              {
                                    if (frames > Fin)
                                          frames = Inicio;
                              }
                              if (frames > Fin)
                              {
                                    frames = Fin;
                                    moviendo = false;
                              }                      
                     }
                       else
                        {
                              tiempoTotal = 0.0f;
                              imagenActual++;
                              if (repitiendo)
                              {
                                    if (imagenActual > Fin)
                                          imagenActual = Inicio;
                              }
                              if (imagenActual > Fin)
                              {
                                    imagenActual = Fin;
                                    moviendo = false;
                              }
                        }
                  }
            }
      }
}

En la clase tenemos lo siguiente:

–          imagenActual: Nos guarda el número del frame o fotograma que estamos dibujando

–          moviendo, repitiendo, detenido: Estos flags nos son útiles para saber si la animación esta ejecutándose (moviendo), si se repite (repitiendo) o si se encuentra detenida (detenido)

–          frames, totalFrames: estas nos serán útiles para cuando queramos cargar una sola imagen con varios frames, más adelante se hará un ejemplo

–          tiempoCambio, tiempoToltal: son útiles para hacer el cambio de fotogramas, depende de la velocidad que queramos

–          Inicio, Fin: Sirven para saber desde que fotograma empieza la animación y hasta donde acaba

–          listaImagenes: es la lista que va a guardas las imágenes o imagen.

–          Escala: si queremos escalar la imagen, cambiarle el tamaño

–          posicion:  la posición de la imagen

–          efectos, batch: son útiles para el manejo de los efectos y el dibujado de las imágenes

–          El contructor de la clase, recibe como parámetros el dispositivo grafico, y la posición inicial de la imagen

–          La función adicionarImagen, recibe la textura y la cantidad de frames que tiene la imagen, cuando cargamos una sola imagen y en ella están todos los frames, debemos decirle cuántos frames hay, si no pasamos 0.

–          ponerPosicion: mueve la imagen a la posición especifica

–          moverIzquierda, moverDerecha, moverAbajo, moverArriba: sirven para mover la animación por la pantalla.

–          Detener: detiene la animación, haciendo que quede una imagen estática

–          irA: sirve para ir a un fotograma especifico

–          iniciarTodo: Inicia la animación desde la imagen 0 hasta la final, el cambio entre fotogramas depende del parámetro segundos, y el parámetro repetir sirve para saber si la animación se hace una sola vez o se repite indefinidamente.

–          iniciar: esta función recibe como parámetros la imagen inicial y final desde donde queremos la animación, los otros parámetros son iguales al de iniciarTodo.

–          Dibujar: Aquí dibujamos la imagen que se encuentre en la variable imagenActual, para el lote de imágenes (una sola imagen que tiene varios fotogramas) usamos el parámetro sourceRectangle de la función Draw, allí le decimos que parte de la imagen es la que queremos dibujar.

–          Actualizar: va cambiando el número de la imagen actual, solo si el tiempo total es igual al tiempo de cambio, el tiempo total es igual al tiempo que va transcurriendo en el juego.

Ahora para ver la clase en acción, en la clase game1.cs hacemos lo siguiente:

Para varias imágenes separadas:

Texture2D carro1;
Texture2D carro2;
Texture2D carro3;
Texture2D carro4;
Animacion carro;

En el Initialize:

carro = new Animacion(graphics, new Vector2(250, 250));
carro.Escala = 0.5f;

En el LoadContent:

carro1 = Content.Load<Texture2D>("carro1");
carro2 = Content.Load<Texture2D>("carro2");
carro3 = Content.Load<Texture2D>("carro3");
carro4 = Content.Load<Texture2D>("carro4");
carro.adicionarImagen(carro1, 0);
carro.adicionarImagen(carro2, 0);
carro.adicionarImagen(carro3, 0);
carro.adicionarImagen(carro4, 0);
carro.iniciarTodo(0.3f, true);

En el Update:

KeyboardState teclado;
teclado = Keyboard.GetState();
if (teclado.IsKeyDown(Keys.Left))
{
carro.moverIzquierda(3);
}
if (teclado.IsKeyDown(Keys.Right))
{
      carro.moverDerecha(3);
}
carro.Actualizar(gameTime);

En el Draw debemos ponerlo después del dibujo del fondo, si no nos quedaría sobre los fondos y no se veria:

carro.Dibujar();

Al ejecutar la aplicación, nos aparece el fondo moviéndose, y a su vez tenemos a un carro que mueve sus llantas, se ve como si el carro se fuera moviendo de verdad:

Si movemos las teclas de derecha o izquierda, se verá mucho mejor:

 

 

Ahora si queremos cargar un lote de imágenes:

Declaramos nuestra textura y animación:

mario = Content.Load<Texture2D>("Mario");
animacion_mario.adicionarImagen(mario, 5);
animacion_mario.iniciar(0, 1, 0.3f, true);

En el método adicionarImagen, enviamos la cantidad de fotogramas que tiene la imagen, luego en Iniciar, le decimos que solo queremos mostrar los fotogramas 0 y 1.

En el Initialize:

animacion_mario = new Animacion(graphics, new Vector2(450, 290));

Luego llamamos los métodos Actualizar y dibujar, al ejecutar la aplicación vemos a mario moviéndose  como si la volqueta lo fuera a atropellar jeje:

 

Código Fuente: Aquí.

Listo, eso es todo, ya ustedes podrán crear fondos y animaciones mucho mejores.

Anuncios

10 pensamientos en “Animaciones de Sprites – XNA

  1. Pingback: Juego de la Ranita -Frogger- XNA « Escarbando Código

  2. Muyas gracias por el código me fue de una gran ayuda para arreglar una parte de el mio que no no lo lograba completar (fondos dinámicos ). El código me saco la duda.

  3. Hola esta muy bueno los tutoriales, pero tengo una duda por que parece que estoy declarando mal en los metodos adicionar imagen y iniciar por que no me sale el fotograma 01 sale solo una mancha 😦 porfavor agradeceria tu ayuda

  4. 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

    • Hola, me alegra que te sirvan. Ahora para tener la animación, puedes hacerlas de varias formas, si quieres usar lo que muestro en este post, puedes tener un set de imágenes e indicar la cantidad de imágenes que se tienen, luego, digamos que las imágenes de la animación son del 0 al 3, entonces cuando se mueva el personaje llamas la animación:

      animacion_mario.iniciar(0, 3, 0.3f, true);
      Ahora si quieres que cambie cuando oprimas la tecla de saltar, llamas de nuevo la animación en cuánto se oprima la tecla, indicando cuales son las imágenes de la animación

      • Gracias, me funciono muy bien, ahora tengo una pregunta he visto en los juegos plataforma que mientras se recorre el mapa, el nivel en ciertas posiciones te aparecen enemigos u objetos animados pero he tratado de hacerlo pero al mover el personaje da la sensación que se mueve junto con el personaje y al cambiarle la posición del vector para que me aparezca en cierto punto del eje X tampoco me resulta y no se si podrás ayudarme, Gracias

Deseas comentar o sugerir algo?

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