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