Volver al Inicio Microsoft Student Tech Club: Universidad Libre

Tutorial de XNA: Personaje corriendo y saltando
 

Necesitamos que nuestro personaje corra y cuando se presione determinada tecla, este salte. Son 2 animaciones. Estos son los pasos para lograr unir estas dos animaciones.

Paso 1: Adjuntar al proyecto las dos animaciones y tener dos juegos de variables en la clase protagonista que se encarguen de las dos animaciones.

    class Protagonista
    {
        //Clase principal de donde se obtiene el LoadContet
        Game1 ClasePrincipal = null;
        //Animaciones
        private Texture2D AnimacionCorriendo;
        private Texture2D AnimacionSaltando;
        //Total de dibujos que tiene el personaje animado corriendo
        int TotalDibujosCorriendo = 10;
        int DibujoCorriendoActual = 0;
        //Total de dibujos que tiene el personaje animado saltando
        int TotalDibujosSaltando = 11;
        int DibujoSaltandoActual = 0;

Paso 2: Crear el método de carga de las texturas

        //Carga texturas
        public void LoadContent()
        {
            AnimacionCorriendo = ClasePrincipal.Content.Load<Texture2D>("Sprites\\Protagonista\\Correr");
            AnimacionSaltando = ClasePrincipal.Content.Load<Texture2D>("Sprites\\Protagonista\\Saltar");
        }

Paso 3: Desde la clase principal se cargan las texturas de ambas animaciones.

        protected override void LoadContent()
        {
            //Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            //Carga texturas del protagonista
            objProtagonista = new Protagonista(this);
            objProtagonista.LoadContent();
        }

Paso 4: Captura la tecla de salto.. en Update()

            //En caso de que el jugador oprima la tecla de salto
            if (estadoTeclado.IsKeyDown(Keys.Up))
                Saltando = true;

Paso 5: La lógica del salto combinada con la lógica de correr, por supuesto en Update()

            //Acumula los milisegundos que lleva ejecutando el juego  
            CuentaTiempo += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
            //Si el acumulado pasa de determinado tiempo
            if (CuentaTiempo > CadaCuantoCambia)
            {
                //Pasa al siguiente "frame" dependiendo si está saltando o corriendo
                if (Saltando == true)
                {
                    DibujoSaltandoActual++;
                    //Para el efecto de subir y bajar cuando se salta
                    if (DibujoSaltandoActual <= TotalDibujosCorriendo / 2)
                        Altura = -2f;
                    else
                        Altura = 2f;
                }
                else
                    DibujoCorriendoActual++;
                //Se controla no pasarse de todos los "frames" del personaje corriendo
                if (DibujoCorriendoActual > TotalDibujosCorriendo - 1 && Saltando == false)
                    DibujoCorriendoActual = 0;
                //Se controla no pasarse de todos los "frames" del personaje saltando
                if (DibujoSaltandoActual > TotalDibujosSaltando - 1 && Saltando == true)
                {
                    Saltando = false; //el salto finaliza
                    DibujoSaltandoActual = 0; //reinicia la variable que cuenta los "frames" de salto
                    Altura = 0f; //Terminó el salto, la altura vuelve a cero.
                }
                //Acumulador de tiempo a cero  
                CuentaTiempo = 0f;
            }
            //Determina donde ubicar el rectángulo para el siguiente "frame"
            if (Saltando == true)
                rectOrigenPersonaje = new Rectangle(this.DibujoSaltandoActual * this.rectPersonajeAncho, 0, this.rectPersonajeAncho, this.rectPersonajeAlto);
            else
                rectOrigenPersonaje = new Rectangle(this.DibujoCorriendoActual * this.rectPersonajeAncho, 0, this.rectPersonajeAncho, this.rectPersonajeAlto);

Paso 6: Dibujando los frames

            if (Saltando == true)  //Animación del salto
            {
                Posicion.Y += Altura;
                TrabajaSprites.Draw(AnimacionSaltando, Posicion, rectOrigenPersonaje, Color.White, 0.0f, Origen, 1.0f, Direccion, 0.0f);
            }
            else //Animación de correr
                TrabajaSprites.Draw(AnimacionCorriendo, Posicion, rectOrigenPersonaje, Color.White, 0.0f, Origen, 1.0f, Direccion, 0.0f);

 

 

El código completo de la clase protagonista es:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Animacion
{
    class Protagonista
    {
        //Clase principal de donde se obtiene el LoadContet
        Game1 ClasePrincipal = null;
        //Animaciones
        private Texture2D AnimacionCorriendo;
        private Texture2D AnimacionSaltando;
        //Total de dibujos que tiene el personaje animado corriendo
        int TotalDibujosCorriendo = 10;
        int DibujoCorriendoActual = 0;
        //Total de dibujos que tiene el personaje animado saltando
        int TotalDibujosSaltando = 11;
        int DibujoSaltandoActual = 0;
        //Saber si el estado del personaje es saltando
        bool Saltando = false;
        //Controla la altura del salto mientras nuestro personaje está saltando
        float Altura = 0f;
        //Tamaño del rectángulo que muestra un "frame" del personaje animado
        int rectPersonajeAncho = 96;
        int rectPersonajeAlto = 96;
        //Controla el cambio de un "frame" a otro en el tiempo
        float CuentaTiempo = 0f;
        float CadaCuantoCambia = 100f;
        //Rectángulo que ubica que "frame" del archivo .PNG se dibuja
        Rectangle rectOrigenPersonaje;
        //En que dirección va corriendo el protagonista
        SpriteEffects Direccion = SpriteEffects.None;
        //Posición del personaje
        Vector2 Posicion = new Vector2(0f, 50f);
 
        //Constructor
        public Protagonista(Game1 ClasePrincipal)
        {
            this.ClasePrincipal = ClasePrincipal;
        }
        //Carga texturas
        public void LoadContent()
        {
            AnimacionCorriendo = ClasePrincipal.Content.Load<Texture2D>("Sprites\\Protagonista\\Correr");
            AnimacionSaltando = ClasePrincipal.Content.Load<Texture2D>("Sprites\\Protagonista\\Saltar");
        }
        public void Actualiza(GameTime gameTime)
        {
            //Esta es una compilación condicional. El teclado solo es leído si la aplicación ejecuta en un PC
#if !XBOX
            //Código para leer el teclado
            KeyboardState estadoTeclado = Keyboard.GetState();
            //Avanza a la izquierda
            if (estadoTeclado.IsKeyDown(Keys.Left))
            {
                Direccion = SpriteEffects.None;  //Protagonista avanza a la izquierda
                Posicion.X-=2; //Avanza a la izquierda, obsérvese que se resta en X
            }
            //Avanza a la derecha
            if (estadoTeclado.IsKeyDown(Keys.Right))
            {
                Direccion = SpriteEffects.FlipHorizontally; //Protagonista avanza a la derecha
                Posicion.X += 2; //Avanza a la derecha, obsérvese que se suma en X
            }
            //En caso de que el jugador oprima la tecla de salto
            if (estadoTeclado.IsKeyDown(Keys.Up))
                Saltando = true;
#endif
            //Acumula los milisegundos que lleva ejecutando el juego  
            CuentaTiempo += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
            //Si el acumulado pasa de determinado tiempo
            if (CuentaTiempo > CadaCuantoCambia)
            {
                //Pasa al siguiente "frame" dependiendo si está saltando o corriendo
                if (Saltando == true)
                {
                    DibujoSaltandoActual++;
                    //Para el efecto de subir y bajar cuando se salta
                    if (DibujoSaltandoActual <= TotalDibujosCorriendo / 2)
                        Altura = -2f;
                    else
                        Altura = 2f;
                }
                else
                    DibujoCorriendoActual++;
                //Se controla no pasarse de todos los "frames" del personaje corriendo
                if (DibujoCorriendoActual > TotalDibujosCorriendo - 1 && Saltando == false)
                    DibujoCorriendoActual = 0;
                //Se controla no pasarse de todos los "frames" del personaje saltando
                if (DibujoSaltandoActual > TotalDibujosSaltando - 1 && Saltando == true)
                {
                    Saltando = false; //el salto finaliza
                    DibujoSaltandoActual = 0; //reinicia la variable que cuenta los "frames" de salto
                    Altura = 0f; //Terminó el salto, la altura vuelve a cero.
                }
                //Acumulador de tiempo a cero  
                CuentaTiempo = 0f;
            }
            //Determina donde ubicar el rectángulo para el siguiente "frame"
            if (Saltando == true)
                rectOrigenPersonaje = new Rectangle(this.DibujoSaltandoActual * this.rectPersonajeAncho, 0, this.rectPersonajeAncho, this.rectPersonajeAlto);
            else
                rectOrigenPersonaje = new Rectangle(this.DibujoCorriendoActual * this.rectPersonajeAncho, 0, this.rectPersonajeAncho, this.rectPersonajeAlto);
        }
        public void Dibujar(SpriteBatch TrabajaSprites)
        {
            /* Los parámetros son:
             * 1. Textura a cargar (todo el archivo .png que tiene la animación).
             * 2. Posición donde colocar el sprite "animado"
             * 3. Rectángulo origen. Carga el "frame" a mostrar que luego es dibujado en el rectángulo destino
             * 4. Color de fondo
             * 5. Rotación
             * 6. Origen
             * 7. Escala
             * 8. Efecto del sprite. Reflejo horizontal.
             * 9. Profundidad en el que se dibuja */
            Vector2 Origen = new Vector2(0f, 0f);
            if (Saltando == true)  //Animación del salto
            {
                Posicion.Y += Altura;
                TrabajaSprites.Draw(AnimacionSaltando, Posicion, rectOrigenPersonaje, Color.White, 0.0f, Origen, 1.0f, Direccion, 0.0f);
            }
            else //Animación de correr
                TrabajaSprites.Draw(AnimacionCorriendo, Posicion, rectOrigenPersonaje, Color.White, 0.0f, Origen, 1.0f, Direccion, 0.0f);
        }
    }
}

Nota aclaratoria: Se ve redundante el uso de  if (Saltando == true) , porque sólo es necesario escribir if (Saltando), la razón de esto, es experiencia: ver la comparativa directamente hace más fácil y rápido depurarla en la mente. De todos modos el compilador optimiza igual ambas expresiones.

El código de la clase principal varió muy poco (sólo la carga de las dos texturas). Este es el código completo:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Animacion
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        //Protagonista
        Protagonista objProtagonista;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }
        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            //Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            //Carga texturas del protagonista
            objProtagonista = new Protagonista(this);
            objProtagonista.LoadContent();
        }
        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            //Actualiza el protagonista
            objProtagonista.Actualiza(gameTime);
            base.Update(gameTime);
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            //Dibuja el protagonista
            objProtagonista.Dibujar(spriteBatch);
           
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

No es muy realista el resultado cuando nuestro protagonista brinca, pero el objetivo fue mostrar la mezcla de dos acciones y dos animaciones.

 

Volver al Inicio Célula Microsoft. Universidad Libre