kalabaza.com.mx
Sitio de animación y desarrollo de video juegos

 

Bienvenidos a esta nuevo post en el que continuaremos con la programación del scroll en el  Background de los juegos 2D.

En esta ocasión hice bastantes cambios a la versión anterior por ser un método diferente de programación, sin embargo el programa lo empecé utilizando el código del post anterior, como lo he estado haciendo hasta ahora. Espero que no haya confusión con los cambios de código pero trataré de explicarlo lo mejor posible.

 

En este ocasión vamos a ver los siguientes puntos:

 

  • Explicación previa
  • Agregar propiedades a AnimSprite
  • Carga de imágenes para los backgrounds
  • Desplazamiento de los backgrounds
  • Modificación de método Draw()
     

Explicación previa

 

Como siempre voy a explicar primero lo que vamos a hacer. Anteriormente teníamos tres backgrounds que se desplazaban en sentido opuesto al desplazamiento del personaje cuando este alcanzaba uno de los límites predefinidos en los extremos de la pantalla. Estos background se iban repitiendo de tal forma que el avance se volvía infinito. En esta ocasión vamos limitar el avance en ambos extremos de nuestro escenario y vamos a ocupar también tres background pero no consecutivos, sino sobrepuestos y que se desplazan a diferentes velocidades para crear mayor profundidad en el escenario. Además el fondo que se encuentra más al fondo mantendrá un desplazamiento constante independiente del movimiento del personaje para imitar el desplazamiento de las nubes.

 

 

Agregar propiedades a AnimSprite

 

Vamos a empezar por hacer unas modificaciones a nuestra clase AnimSprite; estas modificaciones consisten en agregar un par de propiedades que son las dimensiones de los sprites, a estas dos propiedades las llamaremos Largo y Altura y las declararemos en la parte de arriba de nuestra clase.

 

  public int Largo;
  public int Altura;

 

Estos dos valores no los vamos a asignar nosotros sino que vamos a obtener su valor directamente de las imágenes al momento de cargarlas, escribiendo el siguiente código en el método CargaImagen()

 

  Altura = SpriteMov[0].Height;
  Largo = SpriteMov[0].Width;

 

De esta forma, cada vez que se cargue una imagen tendremos a nuestra disposición estos dos valores. Sin embargo cabe señalar que para el caso en el que ocupamos la clase para cargar sprites animados como es el caso de Megaman, lo que nos devolverían estas variables sería las dimensiones de la última imagen cargada la cual puede o no tener las mismas dimensiones de las otras.

 

 

Carga de imágenes para los backgrounds

 

Lo que haremos a continuación es cargar las imágenes que vamos a ocupar dentro de nuestro proyecto en la carpeta Sprites, como lo hemos hecho antes. Puedes consultar la explicación que viene en el Post "Carga y animación de un sprites" y puedes descargar las imágenes aqui.

 

Como comenté vamos a ocupar tres fondos de los cuales los primeros dos se repetirán varias veces en la pantalla y el tercero solo se recorrerá conforme el personaje vaya avanzando. Para los dos primeros vamos a crear listas de nuestra clase AnimSprite y para el tercero solo crearemos un objeto con el siguiente código

 

        List<AnimSprite> FondosCielo = new List<AnimSprite>();

        List<AnimSprite> FondosPicos = new List<AnimSprite>();

        AnimSprite Fondo = new AnimSprite()

 

Este código va dentro de la declaración de variables de nuestra clase Engine y sustituira la declaración del arreglo Fondos que teníamos anteriormente en esa ubicación; Si tienes duda sobre la sintaxis de las listas puedes consultar el post “Listas”

 

Ya con nuestros objetos declarados lo siguiente es escribir el código para cargar las imágenes. Para hacer esto vamos a sustituir el código que ocupábamos para este propósito dentro del LoadContent() de tal forma que el método completo quedará de la siguiente forma:

 

 protected override void LoadContent()
        {

            spriteBatch = new SpriteBatch(this.graphics.GraphicsDevice);

           // Sección 1 Cielo azul

            int PosTmp = 0;

              for (int i = 1;i < 5; i++)
              {
                AnimSprite FondoTmp = new AnimSprite(PosTmp, 0, 0, new Vector2(0, 0));
                FondoTmp.CargaImagen(Content.Load<Texture2D>(“Sprites/Fondo3″ ));
                FondosCielo.Add(FondoTmp);
                PosTmp = FondoTmp.Largo * i;
              }

           // Sección 2 Picos

            PosTmp = 0;

              for (int i = 1; i < 8; i++)
            {
               AnimSprite FondoTmp = new AnimSprite(PosTmp, 325, 0, new Vector2(0, 0));

                FondoTmp.CargaImagen(Content.Load<Texture2D>(“Sprites/Fondo2″));
                FondosPicos.Add(FondoTmp);
                PosTmp = FondoTmp.Largo * i;
            }

 

            //Sección 3 Piso

             Fondo = new AnimSprite(0, 353, 0, new Vector2(0, 0));
             Fondo.CargaImagen(Content.Load<Texture2D>(“Sprites/Fondo1″));

            //Sección 4 Personaje

              for (int i = 1; i<=7; i++)
                Megaman.CargaImagen(Content.Load<Texture2D>(“Sprites/MM”+ i));

        }

Puse comentarios para poder explicar el código por partes.
En la primer sección vamos a cargar la imagen de más al fondo, la cual he denominado Cielo azul.

 

 

Iniciamos por declarar la variable PosTmp que va a guardar la posición inicial de nuestras imágenes que carguemos en nuestra lista. Enseguida lo que vamos a hacer es utilizar el ciclo for para cargar cuatro veces la imagen en la lista  FondosCielo  (desde i= 1 hasta i=5 ) esto es porque esta imagen tiene de dimensiones 256 x 482 y aunque se necesitan tres imágenes para cubrir toda la pantalla, necesitamos una imagen adicional para cubrir el área que queda al desplazarse por completo una de las imágenes; Puedes cambiar el límite del  for  a 4 para entender mejor lo que pasa.

 

Dentro del ciclo for tenemos lo siguiente:

 

 AnimSprite FondoTmp = new AnimSprite(PosTmp, 0, 0, new Vector2(0, 0));

 

En esta línea lo que hacemos es instanciar un objeto temporal  AnimSprite de nombre FondoTmp; Este objeto tendrá como posición inicial en X el valor de PosTmp, el cual originalmente es cero y conforme va avanzando el loop se va incrementando de acuerdo al largo de la imagen. Por ejemplo, para nuestro ejemplo la imagen que  ocupamos tiene de dimensiones en Largo y Alto de 256 x 482 respectivamente. Por lo tanto el valor de PosTmp será el siguiente para cada valor de i dentro del for:

 

 

i PosTmp
1 0
2 256
3 512
4 768

 

La posición en Y será constante e igual a cero, el ángulo de rotación será también cero así como las coordenadas del centro de rotación que esta definido por el vector que se instancia como parámetro final. Enseguida tenemos la siguiente línea:

 

FondoTmp.CargaImagen(Content.Load<Texture2D>(“Sprites/Fondo3″ ));

 

que como ya sabemos por post anteriores nos sirve para cargar nuestra imagen Fondo3 en el objeto que acabamos de crear.

La siguiente línea

 

FondosPicos.Add(FondoTmp);

 

se utiliza para agregar el objeto que acabamos de instanciar dentro de nuestra lista y finalmente en la última línea

 

PosTmp = FondoTmp.Largo * i;

 

incrementamos el valor de PosTmp de acuerdo a la tabla anterior.

 

En la sección 2 denominada ‘Picos’ tenemos la misma estructura pero reiniciamos el valor de PostTmp a cero y en lugar de Fondo1 vamos a cargar la imagen Fondo2.

 

 

Además dado que las dimensiones de esta nueva imagen son 128 x 108 será necesario copiar siete veces la imagen en la lista FondosPicos  (desde i= 1 hasta i = 8).

 

Nota: La mejor solución para definir el número de veces que la imagen se guarda en la lista es determinarlo en base al ancho de la imagen a carga de tal forma que rellene por completo toda la pantalla y agregamos una imagen más para cubrir el espacio faltante al desplazarse. En ese ejemplo ocupo valores constantes para que sea más fácil de entender.

 

En la sección 3 denominada ‘Piso’ la estructura es diferente, dado que solo vamos a ocupar una imagen que va a ocupar todo nuestro escenario.

 

 

Por lo tanto solo instanciamos en objeto Fondo como hemos estado haciendo y definimos su posición en la coordenada (0,0). Finalmente cargamos la imagen Fondo1 con ángulo de rotación 0 y origen en (0,0).

 

En la última sección denominada ‘Personaje’ seguimos cargando los sprites de megaman como en los post anteriores.

 

 

Desplazamiento de los backgrounds

 

Ya con las imágenes cargadas lo siguiente es escribir el código para desplazarlas conforme avanza nuestro personaje. Para hacer esto vamos a modificar el método Update() de nuestra clase Engine. Como expliqué al principio, el background del cielo va a desplazarse independientemente del movimiento del personaje. Este desplazamiento será de un pixel por cada ciclo del juego y será de derecha a izquierda. Para lograr esto vamos a escribir el siguiente código dentro de nuestro if principal del método Update()

 

          foreach (AnimSprite Tmp in FondosCielo)
              Tmp.XPos -= 1;

 

Lo que hacemos aquí es desplazar un píxel a la izquierda cada uno de los background dentro de nuestra lista FondosCielo; como comenté antes, este código esta fuera de las condiciones de dirección de los personajes así que siempre se ejecuta.

 

Dentro de nuestro código original  tenemos enseguida los dos if’s que controlan  los cambios en el juego de acuerdo a la tecla presionada, izquierda o derecha. Dentro del primer if (correspondiente a la condición de que se ha presionado la tecla a la DERECHA) vamos a sustituir el código anterior por el siguiente:

 

//Avance
   if (Megaman.XPos < 540 || (Megaman.XPos <graphics.PreferredBackBufferWidth -- 40 &&
     Fondo.XPos < graphics.PreferredBackBufferWidth-Fondo.Largo+10 ))
    Megaman.XPos += 10;
   else if (Fondo.XPos>= graphics.PreferredBackBufferWidth-Fondo.Largo+10)
  {
 //Cielo
      for(int i= 0; i < FondosCielo.Count;i++)
        FondosCielo[i].XPos -= 3;

//Picos
     for (int i = 0; i < FondosPicos.Count; i++)
     {
        FondosPicos[i].XPos -= 6;

          if (FondosPicos[i].XPos + FondosPicos[i].Largo < 0)
          {
             if (i == 0)
                FondosPicos[i].XPos = FondosPicos[FondosPicos.Count - 1].XPos +
FondosPicos[FondosPicos.Count - 1].Largo-6;

             else
                FondosPicos[i].XPos = FondosPicos[i - 1].XPos + FondosPicos[i - 1].Largo-6;
          }
      }


// Piso
 Fondo.XPos -= 10;
 }


//Megaman

  if (Megaman.UltimaDir == 0 && Megaman.XPos < graphics.PreferredBackBufferWidth -- 40)
   Megaman.UpdateMov();
  else
   Megaman.FrameActual = 6;
 

Megaman.UltimaDir = 0;

 

Nuevamente el código en este bloque está dividido en cinco partes definidas por las etiquetas: Avance, Cielo, Picos, Piso y Megaman. En la sección Avance tendremos, como su nombre lo indica, la condición que hacer que el personaje avance o no cuando se presiona la tecla de dirección a la derecha. Lo que queremos es limitar el movimiento para que solo se pueda mover dentro del área definida por la imagen de piso que ocupamos. Al igual que en el post anterior vamos de permitir que nuestro personaje avance mientras no alcance el límite derecho definido por nosotros (Megaman.XPos < 540) sin embargo tenemos que agregar una nueva condición que permitirá avanzar a nuestro personaje hasta el final de la pantalla cuando se haya alcanzado el final de la imagen definida por nuestro objeto Piso. Esta condición tendrá que verificar que la posición de Megaman sea menor al límite derecho de la pantalla. Este límite esta definido por la constante graphics.PreferredBackBufferWidth que para nuestro ejemplo tiene un valor de 640, sin embargo como recordarán el origen de los sprites se encuentra en el extremo superior izquierdo, entonces se permitimos que Megaman alcance este límite habrá salido de la pantalla. Es por eso que necesitamos restar el ancho de los sprites de Megaman para que siempre se mantenga dentro de la pantalla. Este ancho es aproximadamente 40 pixeles y digo aproximadamente porque el ancho real del sprite es 44 pixeles pero a la derecha tienen un espacio en blanco variable para cada imagen por consiguiente nuestra condición queda como:

 

Megaman.XPos <graphics.PreferredBackBufferWidth -- 40

 

pero si dejamos esta condición así, permitira a Megaman que alcance el límite de la pantalla y no le importará la condición anterior. Es por eso que tenemos que agregar una condición adicional a esta parte del if. Esta parte debe permitir a Megaman que alcance el límite de la pantalla pero cuando la posición de Fondo este a una posición n donde n es el negativo del Largo de la imagen más el ancho de la pantalla osea:

 

Fondo.XPos < graphics.PreferredBackBufferWidth-Fondo.Largo+10

 

El 10 adicional es una corrección que hay que hacer porque el avance de Fondo es de 10 en 10 unidades. Con esto nos quedará completo el if de la Sección ‘Avance’.

 

Si la condición anterior no se cumple entonces debemos verificar primero que Fondo no este completamente a la izquierda para permitir el desplazamiento de los demás fondos. Esto lo hacemos verificando que la posición de Fondo sea mayor al negativo del largo de la imagen más el ancho de la pantalla o lo que es lo mismo:

 

Fondo.XPos>= graphics.PreferredBackBufferWidth-Fondo.Largo+10

 

Nuevamente el 10 es un valor de corrección.

 

En la sección denominada ‘Cielo’ vamos a desplazar tres pixeles a la izquierda todos nuestras imágenes que están dentro de la lista FondosCielo con ayuda del for que recorrerá todas las imágenes en la lista. Esto será lo único que haremos por ahora pero más adelante explicaré porque.

 

En la sección ‘Picos’ tenemos también un for que recorrerá todas las imágenes de nuestra lista FondosPicos pero en lugar de recorrer las imágenes tres pixeles las recorreremos seis. Esto, como comenté al principio nos creará un efecto de profundidad en nuestro escenario.
Lo que hacemos a continuación es verificar que cada imagen que vamos recorriendo se encuentre dentro de los límites de la pantalla de la siguiente manera:

 

 

Como se muestra en la imagen, si la posición actual de la imagen (FondosPicos[i].XPos) esta en una posición negativa ‘n’ tal que ‘n’ = -- FondosPicos[i]. Largo  esto significaría que la imagen ha salido por completo de la pantalla en cuyo caso lo que hacemos es colocar esta imagen a la derecha al final de la última imagen de la lista o mejor dicho a la derecha de la imagen que este más a la derecha de la pantalla. La pregunta ahora sería cual es el índice de dicha imagen. Primero si la imagen que se salió de la pantalla es la primer imagen o sea aquella con índice i ==0 entonces sabemos que la imagen más a la derecha es la última en la lista y para encontrar el último índice utilizamos la propiedad Count que nos regresa el número de elementos contenidos en la lista y le restamos 1 para obtener el último índice.

 

 

Una vez que conocemos el último indicie solo nos resta sumar la posición en X del mismo al ancho o Largo de las imágenes que componen la lista y restarle 6 pixeles como se muestra en la siguiente línea:

 

  FondosPicos[i].XPos = FondosPicos[FondosPicos.Count - 1].XPos + FondosPicos[FondosPicos.Count - 1].Largo-6;

 

Para un caso diferente sabemos que si siempre realizamos el movimiento de las imágenes a la derecha del último elemento de la lista esta debería tener un índice siguiente menor al que vamos a desplazar, así que  solo tendríamos que restar 1 al índice que vamos a desplazar.

 

 

y al igual que en el caso anterior solo sumamos la posición de este elemento al largo de las imágenes y le restamos 6 pixeles.

 

FondosPicos[i].XPos = FondosPicos[i - 1].XPos + FondosPicos[i - 1].Largo-6;

 

Para nuestra sección ‘Piso’ solo nos queda restar 10 pixeles a nuestra imagen. Como puedes observar esta imagen es la que más se desplaza y este desplazamiento es equivalente al que realiza el personaje porque se encuentran en el mismo plano, además no se necesita ninguna validación porque esta imagen no se repite.

 

En nuestra última sección vamos a seguir actualizando el sprite que se va a mostrar en la animación de Megaman desplazándose, sin embargo como ahora tenemos límites en cada extremo de la pantalla tenemos que verificar que no los haya alcanzado antes de seguir con el siguiente sprite. Lo único que hacemos es verificar que la posición de megaman este dentro de los límites de la pantalla para permitirle que se desplace de lo contrario se detendrá la animación y se mostrará el sprite de megaman de pie. Finalmente establecemos como última dirección 0 que es a la derecha

 

Dentro de la condición de que se haya presionado la tecla de dirección IZQUIERDA, tendremos que escribir el siguiente código.

 

//Avance
  if (Megaman.XPos > 100 || (Megaman.XPos > 0 && Fondo.XPos >= 0))
    Megaman.XPos -= 10;

  else if (Fondo.XPos<0)
  {

//Cielo
    for (int i = FondosCielo.Count-1; i >= 0; i--)
    {
      FondosCielo[i].XPos += 3;

        if (FondosCielo[i].XPos > graphics.PreferredBackBufferWidth)
        {
           if (i == FondosCielo.Count -- 1)
              FondosCielo[i].XPos = FondosCielo[0].XPos -- FondosCielo[i].Largo + 3;
           else
              FondosCielo[i].XPos = FondosCielo[i + 1].XPos -- FondosCielo[i].Largo + 3;
        }
    }

//Picos
    for (int i = FondosPicos.Count -- 1; i >= 0; i--)
    {
      FondosPicos[i].XPos += 6;

        if (FondosPicos[i].XPos > graphics.PreferredBackBufferWidth)
        {
           if (i == FondosPicos.Count -- 1)
              FondosPicos[i].XPos = FondosPicos[0].XPos -- FondosPicos[i].Largo + 6;
           else
              FondosPicos[i].XPos = FondosPicos[i + 1].XPos -- FondosPicos[i].Largo + 6;
        }
    }

//Piso
  Fondo.XPos += 10;
}
 

//Megaman

  if (Megaman.UltimaDir == 1 && Megaman.XPos > 0)
    Megaman.UpdateMov();
  else
    Megaman.FrameActual = 6;

Megaman.UltimaDir = 1;

 

Dado que la estructura es la misma que el bloque anterior solo explicaré algunas diferencias importantes. Primero, como es obvio todas las condiciones y los límites se basan en el lado izquierdo de la pantalla, así por ejemplo en la sección Avance permitimos el desplazamiento de Megaman siempre y cuando no rebase el límite permitido que es hasta X = 100 o hasta el final de la pantalla si Fondo se encuentra recorrida completamente a la derecha.

 

En la sección cielo la diferencia principal con el bloque anterior es que sí cuenta con una validación después de incrementar 3 pixeles a la posición de FondoCielo ya que si alguna de las imágenes se sale de la pantalla se pasará al principio de la lista de manera inversa a lo que teníamos en el bloque anterior. Esta misma lógica se aplica ala sección ‘Picos’ en el que al salir una imagen del lado de la pantalla se recorre enseguida al principio de la lista.

 

 

Finalmente en la sección ‘Megaman’ al igual que en bloque anterior, actualizamos los sprites de Megaman siempre y cuando no se salga de la pantalla.Fuera de estos dos bloques de condición vamos a escribir el siguiente código

 

  for (int i = 0; i < FondosCielo.Count; i++)
  {
      if (FondosCielo[i].XPos + FondosCielo[i].Largo < 0)
      {
          if (i == 0)
            FondosCielo[i].XPos = FondosCielo[FondosCielo.Count - 1].XPos + FondosCielo[FondosCielo.Count - 1].Largo;
          else
            FondosCielo[i].XPos = FondosCielo[i - 1].XPos + FondosCielo[i - 1].Largo;
      }
  }

 

Este código lo que hace es verificar cada una de las imágenes de la lista FondoCielo y la razón por la que está afuera de las condiciones es porque como recordarán, ese fondo se mueve independientemente y por lo tanto necesitamos verificar que en ese movimiento no se salga de la pantalla. Es por esta razón que en el primer bloque el avance FondoCielo no tenía ningún límite para su avance pues se necesitaba validar al presionar la tecla de dirección derecha y también cuando no se presionaba ninguna tecla. De esta forma nos ahorramos una de las validaciones.

 

Lo que hacemos, como en casos anteriores es verificar que la posición de las imágenes FondoCielo estén siempre dentro de la pantalla o de lo contrario se moverá al final de la lista la que este fuera.

 

 

Modificación del método Draw()

 

Lo último que nos resta es dibujar en pantalla tanto los fondos como a nuestro personaje para esto vamos a escribir en dentro del método Draw() de nuestra clase AnimSprite el siguiente código:

 

    foreach (AnimSprite Tmp in FondosCielo)
      Tmp.Draw(spriteBatch);

    foreach (AnimSprite Tmp in FondosPicos)
      Tmp.Draw(spriteBatch);

  Fondo.Draw(spriteBatch);

  Megaman.Draw(spriteBatch);
  base.Draw(gameTime);

 

Lo que hacemos es dibujar cada una de las imágenes de cada una de las listas y finalmente dibujamos a Megaman en su posición actual. Es importante que se escriba el código en este orden porque se Irán dibujando y apilado las imágenes en el orden en que se va leyendo el código por lo tanto si dibujamos una lista antes de la otra se quedarán atrás y no se verán.

 

Finalmente mandamos llamar al método Draw() de nuestra clase base y listo, ya tenemos nuestros tres fondos a velocidades diferentes que aunque no tiene aún un sistema de colisión se verá así

 

 

Las ligas a los archivos de este proyecto están aqui

 

 

Espero que no haya sido muy confuso este largo post y que haya sido útil para ustedes. No olvides tus comentario antes de salir.

Gracias y nos vemos en el siguiente post.

 

Piroshi.

 

 


Tags: , , , ,

XNA

 

 

Bienvenidos a este nuevo Post. La vez anterior pudimos desplegar sprites en pantalla y hacer una animación con ellos. Lo que vamos a hacer ahora es utilizar las teclas de dirección del teclado para poder controlar a nuestro personaje. Antes de empezar puedes consultar el post “Edición básico de sprites” si quieres saber el proceso que seguí para generar los sprites.

 

Los puntos que vamos a ver en este post son los siguientes:

 

 

    * Mejora del código anterior.
    * Carga de imagen adicional.
    * Modificación del método Draw()
    * Entrada de datos desde el teclado.
    * Creación de constructores
    * Carga de una imagen de Fondo.

 

 

Modificación del código anterior

 

Lo primero que vamos a hacer , como comenté al final del Post anterior es darle una arregladita al código para hacerlo más compacto. Lo primero será abrir nuestro proyecto del Post anterior y abrir el archivo AnimSprite.cs. Aquí lo que vamos a hacer es cambiar el nivel de acceso de nuestro arreglo SpriteMove a private para que solo la misma clase tenga acceso a él. Además, ya no va a ser un arreglo, si no una lista por dos razones, la primera es por que reduce bastante el código y se vuelve muy sencillo de entender y la segunda es para empezar a utilizar una forma de agrupar objetos diferente a los arreglos.  Para conocer más sobre las listas agregué un post que puedes consultat llamado “Listas” debajo de “Consideraciones del C#” por si la sintaxis no es muy clara y quieres conocer mejor su uso. Con estos cambios la declaración quedaría así:

 

     privateList<Texture2D> SpriteMov = newList< Texture2D>(); 

 

Ya que modificamos nuestro objeto lo siguiente es crear un nuevo método que se llamará  CargaImagen() el cual tendrá el siguiente código:

 

        public voidCargaImagen(Texture2D Sprite)
        {
           SpriteMov.Add(Sprite);
        }

 

Es un código muy simple que lo único que hace es recibir un sprite de tipo Texture2D y cargalo a nuestra lista que acabamos de crear; Como comenté antes, utilizando listas el código se vuelve muy simple pues lo único que hace falta es utilizar el Método Add() de la lista para agregar un nuevo elemento. Por otro lado lo que vamos a hacer es abrir nuestra clase Engine.cs para modificar el código que ocupábamos para cargar las imágenes dentro de método LoadContent(), el cual va a quedar así:

 

             for (int i = 1; i<=6; i++)
             {
               Megaman.CargaImagen(Content.Load<Texture2D>(”Sprites/MM”+ i));
             }

 

Lo que hace este código es iterar seis veces (que es el número de imágenes que forman la animación) y aprovechando que las imágenes comparten el nombre común “MM” más un número. Podemos incrementar ese número en un ciclo y concatenarlo al final del nombre genérico para obtener el nombre de cada imagen a cargar. El resultado del Load lo pasamos a la función CargaImagen() que acabamos de hacer y listo ya tenemos un código mas eficiente para la carga de imágenes.

 

 

Carga de Imagen adicional

 

Antes de seguir adelante voy a explicar lo que vamos a hacer. Actualmente tenemos a nuestro Megaman corriendo sin parar en un fondo blanco y lo que queremos, obviamente, es tener el control del personaje y sus movimientos, por lo pronto y para este post nos limitaremos a desplazar al personaje hacia la izquierda y hacia la derecha utilizando las flechas de dirección del teclado. Para lograr esto hay algunas consideraciones a tomar.

 

Primero, los sprites que tenemos ahora son de una animación continua y es por eso que es necesario carga una imagen más para nuestro personaje de pie y sin avanzar.

 

Segundo, actualmente tenemos seis sprites para el movimiento de Megaman hacia la derecha y agregaríamos un nuevo sprite con lo que ya tendríamos siete, pero si queremos que el personaje se desplace también hacia la zquieda harán falta otros siete sprites de Megaman corriendo hacia la izquierda más uno de pie mirando también hacia la izquierda. Si abrimos nuestras imagines en un editor de imágenes, las volteamos y guardamos con un numero consecutivo tendríamos todas nuestros sprites, sin embargo para hacerlo de una manera más elegante vamos a ocupar los mismos sprites que tenemos y por código vamos a invertirlos hacia la izquierda si la animación es en esa dirección.

 

Para cargar la imagen seguiremos el proceso visto en el post anterior “Carga y animación sprites” y la imagen a cargar es la siguiente:.

 

MM7

 

 

Modificación del método Draw()

 

Ya que tenemos siente sprites cargadas en nuestro programa necesitamos modificar el ciclo for que acabamos de escribir para agregar una imagen más

 

 

             for (int i = 1; i<=7; i++)
             {
               Megaman.CargaImagen(Content.Load<Texture2D>(”Sprites/MM”+ i));
             }

 

Por otro lado para lograr voltear la imagen necesitamos hacer varias modificaciones. Primero vamos a cambiar el método Draw() de nuestra clase AnimSprite.cs ya que anteriormente solo llamábamos al metodo Draw() del spriteBatch,  con el vector de posición y un color de mascara pero para voltear la imagen tenemos que llamar a otra versión sobrecargada del mismo método Draw() que utiliza otros parámetros. Lo primero que necesitamos es declarar una nueva variable dentro de nuestra clase AnimSprite.cs de tipo entero que llamaremos UltimaDir.

 

      public int UltimaDir;

 

Esta variable la vamos a utilizar de la siguiente manera: si se presionó la tecla de dirección derecha le asignaremos el valor de 0 y si fue presionada la tecla de dirección izquierda la asignaremos el valor de 1. (abajo viene la explicación de como cachar estos eventos). Utilizando esta variable nuestro método Draw() quedaría así:

 

       public void Draw(SpriteBatch spriteBatch)
        {
           spriteBatch.Begin();

             if(UltimaDir== 0)  //Derecha
                 spriteBatch.Draw(SpriteMov[FrameActual], new Rectangle(XPos, YPos,
                  SpriteMov[FrameActual].Width,SpriteMov[FrameActual].Height), null,Color.White,
                  Angulo,Origen, SpriteEffects.None,0f);

             else if(UltimaDir== 1)  //Izquiera

                 spriteBatch.Draw(SpriteMov[FrameActual], newRectangle(XPos, YPos,

                 SpriteMov[FrameActual].Width, SpriteMov[FrameActual].Height), null, Color.White,
                 Angulo, Origen, SpriteEffects.FlipHorizontally,0f);

           spriteBatch.End();
       }

 

 

Lo que hacemos es dividir el código en dos opciones, la primera es cuando la última tecla de dirección presionada fue la derecha  (UltimaDir ==0) y la segunda es cuando la última tecla de dirección fue la izquierda (UltimaDir ==1). Para el primer caso vamos a mostrar los sprites en su forma normal, sin embargo para mostrar congruencia en los dos casos he llamado al mismo método sobrecargado de Draw() al que le vamos a pasar los siguiente parámetros:

 

Primero: new Rectangle(XPos, YPos, SpriteMov[FrameActual].Width,
SpriteMov[FrameActual].Height)
, En lugar de ocupar el vector de posición que ocupábamos en el post pasado esta sobrecarga utiliza un objeto Rectangle que lo que va hacer es colocar nuestro sprite dentro de un rectángulo definido por cuatro puntos que corresponden a la esquina superior izquierda, el largo y el ancho del mismo, de tal forma que si definimos un rectángulo de diferentes dimensiones que nuestro sprite este se acoplará a las dimensiones del mismo pero como para nuestro caso queremos mantener las dimensiones originales, vamos a hacer lo siguiente: En lugar de nuestro vector de posición vamos a declarar un par de variables enteras que nos servirán para definir la posición de nuestro personaje 

 

       public int XPos, YPos;  

 

A continuación para definir las dimensiones del rectángulo vamos a utilizar las dimensiones del sprite para garantizar que mantendrá su forma al momento de dibujarlo en pantalla. Para eso es que se utilizan los parámetros Width y Height de nuestro sprite a dibujar

 

Segundo: null. El segundo parámetro es un objeto también del tipo Rectangle que se utiliza cuando se quiere dibujar solo una porción del sprite. Esta porción será la que contenga el rectángulo que definamos, pero para nuestro caso, enviamos un valor nulo para indicar que dibujaremos el sprite completo.

 

Tercero: Color.White  El color de mascara que es el mismo que en la sobrecarga anterior y que seguirá siendo blanca osea sin mascara.

 

Cuarto: Angulo  Este parámetro es el ángulo de rotación de nuestro sprite, en caso de que quisiéramos rotarlo ; Para este parámetro vamos a tener que declarar una nueva variable donde almacenaremos  un ángulo de rotación para cuando haga falta.

 

        public float Angulo;

 

Quinto Origen ,  Al definir la rotación de un objeto es necesario definir el pivote u origen con respecto al cual se va a efectuar esta rotación, es por eso que vamos a declarar un vector que almacene ese origen de rotación y se lo vamos a pasar como parámetro.

 

        public Vector2 Origen;

 

Sexto: SpriteEffects.None Y finalmenta la razón por la cual utilizamos esta sobrecarga: el efecto, que para el caso del avance hacia la derecha es None (porque permanecerá sin cambios) pero para cuando avanza a la izquierda será FlipHorizontally. Y que es esto? Pues este parámetro nos indica si el sprite será dibujado sin alteración (SpriteEffects.None) o volteado sobre el eje  horizontal (SpriteEffects.FlipHorizontally).

 

Septimo : 0f . Este parámetro es un número flotante que indica la profundidad del sprite dentro de la pantalla, esto se utiliza cuando tenemos varios sprites en pantalla para definir cual va arriba de cual. Para nuestro caso mandamos 0 para poner el personaje más enfrente en caso de que hubiera otros sprites.

 

 

Entrada de datos desde el teclado

 

Para cachar la entrada de datos desde el teclado vamos a escribir la siguiente línea dentro nuestro método Update() y dentro de nuestra if que controla el tiempo

 

      KeyboardStatekState= Keyboard.GetState();

 

Lo que estamos haciendo aquí  es declarar un objeto que llamaremos  kState de la clase KeyboarState.

 

Esta clase ya nos hace toda la chamba para la verificación del estado de las teclas ya que al llamar al método GetSatate() podremos preguntar si una tecla fue presionada o no. Lo que necesitamos ahora es checar el estado de las teclas que nos interesan, que son las teclas de dirección izquierda y derecha pero que hacemos después? Por ejemplo, si presionamos la tecla de dirección derecha lo que queremos es que Megaman se desplace hacia la derecha pero también que la animación empiece a correr y nos muestre un frame tras otro y que al terminar los seis, vuelva con el primero como hicimos en el ejemplo anterior, además queremos que al dejar de presionar la tecla izquierda se detenga el desplazamiento, la animación y que se despliegue el sprite de Megaman parado y viendo hacia la derecha. Para hacer esto necesitamos saber si la tecla de dirección izquierda esta presionada en el momento de la verificación pero también necesitamos saber si fue presionada antes o mejor dicho si no ha dejado de ser presionada. Para verificar esto necesitamos almacenar en una variable el estado anterior de la tecla al momento de hacer una nueva verificación y para hacer eso vamos a utilizar la variable UltimaDir que declaramos antes. Utilizando esta variable podemos cambiar el código que utilizábamos para modificar la animación de Megman por el siguiente código dentro del método Update() de la clase Engine.cs

 


       if (kState.IsKeyDown(Keys.Right) && kState.IsKeyUp(Keys.Left))
          {
               Megaman.XPos +=10;
                  if (Megaman.UltimaDir == 0)
                      Megaman.UpdateMov();    
              Megaman.UltimaDir = 0;
          }
       else if (kState.IsKeyDown(Keys.Left) && kState.IsKeyUp(Keys.Right))
          {
               Megaman.XPos -= 10;
                  if (Megaman.UltimaDir == 1)
                      Megaman.UpdateMov();
               Megaman.UltimaDir = 1;
          }
        else
          {
                 Megaman.FrameActual = 6;
          }

 

Lo que hacemos aquí es utilizar nuestro objeto kState que acabamos de instanciar y vamos a utilizar el método isKeyDown() que recibe como parámetro la tecla a evaluar a través de un objeto Keys. Lo que preguntamos en el primer if es si la tecla de dirección derecha fue presionada (IsKeyDown) pero la tecla de dirección Izquierdo no fue presionada (IsKeyUp) Si se cumple esta condición entonces desplazaremos a nuestro personaje diez pixeles a la derecha. Además si no se a soltado la tecla de dirección derecha (UltimaDir == 0) pasaremos al siguiente frame de nuestra animación.

 

Por último guardamos en nuestra variable UtimaDir que la tecla de dirección derecha fue presionada.

 

Para la siguiente condición se utiliza la misma lógica pero aplicada a la condición de que se haya presionado la tecla de dirección Izquierda y no la Derecha.

 

Finalmente si no se presionó  ninguna de las dos teclas, seteamos nuestro frame actual a 6 que es donde se encuentra nuestro personaje parado y según la última dirección  se dibujará viendo a la derecha o a la izquierda.

 

 

Creación de constructores 

 

Ya que agregamos nuevos parámetros a nuestra clase AnimSprites.cs lo que quiero hacer es crear un nuevo constructor de la clase, primer porque quiero dejar algunos fijos en un constructor por default y segundo para tener la posibilidad de definir parámetros al crear un nuevo objeto. El primer constructor quedaría de la siguiente manera:

 

 

       publicAnimSprite()
        {
          XPos= 200;
          YPos= 200;
          Angulo=0;
          Origen= new Vector2(0,0);
      } 

 

Este constructor será  nuestro constructor por default y será llamada cuando no definamos ningún parámetro. Lo que hace es copiar en
nuestros parámetros del objeto los valores que quiero que tenga al crear cualquier objeto. 

 

Ahora vamos a crear el segundo constructor que tendrá el siguiente
código:

 

       publicAnimSprite(int PosX, int PosY,float Angle, Vector2 Origim)
        {
          XPos = PosX;
          YPos = PosY;
          Angulo = Angle;
          Origen = Origim;
       } 

 

A diferencia del anterior, este constructor recibe cuatro parámetros,
los cuales definen el estado iniciar del objeto a crear. Este
constructor no lo ocuparemos para este ejemplo pero puedes empezar a
hacer pruebas con él.

 

 

Carga de una imagen de Fondo.

 

Si ejecutas el programa ya podrás controlar a Megaman en ambas direcciones, sin ningún límite, es decir, puedes salirte de la pantalla en ambos lados. Lo que vamos a hacer ahora es cargar una imagen de fondo para olvidarnos del aburrido fondo blanco. Para eso he modificado una imagen que descargue del sitio Sprite-Resource para que ocupe toda la pantalla. La imagen en cuestión es la siguiente:

 

Fondo1 de megaman

 

Para cargar esta imagen vamos a hacer lo siguiente: Primero hay que cargar la imagen con el método que ya conocemos. Enseguida declaramos un objeto Texture2D dentro de nuestra clase Engine.cs para guardar ahí la imagen.

 

        private Texture2D Fondo1; 

 

A continuación agregamos el código para copiar la imagen en el objeto; esto lo haremos dentro del método LoadContent().

 

      Fondo1 = Content.Load<Texture2D>(”Sprites/Fondo1“);

 

Y por último vamos a agregar el código para dibujar el fondo en pantalla; Este código irá dentro del método Draw() y deberá copiarse antes del código que dibuja a nuestro personaje.

 

          spriteBatch.Begin();
              spriteBatch.Draw(Fondo1, new Vector2(0,0),Color.White);
           spriteBatch.End();

 

Lo que hacemos aquí es colocar la imagen de fondo en la coordenada (0,0) para que ocupe toda la pantalla.
Por último y para que haya congruencia del escenario con el personaje voy a modificar las coordenadas de inicio dentro del constructor por default  de nuestro personaje para que queden así:

 

           XPos = 100;
           YPos = 395;

 

Y listo, ya puedes controlar a Megaman con un escenario de fondo. Claro que ahorita no hay control de colisiones así que  puede atravesar paredes pero eso lo resolveremos en otro Post.

 

Si todo salió bien deberías de ver una imagen como la siguiente en pantalla completa:

 

Megaman con Fonfo1

 

Puedes bajar todo el proyecto y las imagenes de aqui (Proyecto Completo) o puedes bajar solo las imagenes para armar tú mismo el proyecto desde esta liga (Sprites2 de Megaman)

 

Espero que haya sido de su agrado este ejemplo y que haya quedado claro, como siempre si hay alguna duda no  olvides postearla, tampoco olvides tus comentarios antes de salir. Gracias y nos vemos en el siguiente post.

 

Piroshi.

 


Tags: ,

XNA

 

En esta ocasión vamos a ver como cargar imágenes en un proyecto de XNA además de crear una animación muy sencilla con las mismas. Puedes consultar la teoría previa en el post “Sistema coordenado 2D” donde encontrarás un par de conceptos útiles para entender mejor este post.

 

Lo primero que necesitamos es conseguir las imágenes que vamos a cargar, para lo cual si eres muy hábil para el dibujo puedes dibujar cada uno de los sprites tanto de los personajes, enemigos, fondos y demás para hacer tu juego completamente original. En realidad eso es lo que hay que hacer cuando ya se desarrolla un proyecto serio pero para nuestro caso y a modo de estudio vamos a ocupar sprites de juegos que ya existen y que fueron copy pasteados para nosotros. Existen varios sitios en Internet y varios juegos de donde podemos sacar las imágenes que nos hagan falta y ya depende de cada quien lo que quiera hacer. Para el ejemplo yo voy a ocupar sprites de Megaman por varias razones, primero, obvio, me gusta el juego y el personaje y segundo porque hay muchos sitios en Internet donde podemos descargar recursos de este juego. La imagen que utilicé para los sprites es la siguiente:

 

 

megaman sprites

 

 

La descargue del sitio sprites-resource que les recomiendo porque tiene una amplia variedad de sprites que podremos ocupar en nuestros proyectos.

 

Puedes hacer clic sobre ella para bajarla en tamaño completo; sin embargo existe un problema ya que no es solo bajar la imagen y cargarla en el proyecto. Para nuestro caso vamos a ocupar los sprites de megaman corriendo, así que habría que copiar cada uno de ellos y guardarlo en un archivo independiente en formato png, pero no te preocupes ya lo he
hecho para el ejemplo y puedes bajar todas las imágenes aquí.

 

Ya que tenemos las imágenes vamos a empezar con la programación. Lo primero será crear un proyecto nuevo (si no recuerdas como, puedes checar nuevamente la sección “Creacion un proyecto” . Para el ejemplo yo lo llamaré Ejemplo2D
pero puedes ocupar el nombre que quieras. También utilicé el nombre de Engine para la clase principal como hice el ejemplo de la sección “Análisis del código Inicial”.



Carga de Imágenes

 

Lo siguiente será cargar en nuestro proyecto las seis imágenes que vamos a
ocupar, para este necesitamos hacer clic derecho sobre la carpeta contets de nuestro explorador de soluciones y seleccionamos Agregar/Nueva carpeta.

 

 

Imagen1

 

 

Una vez que se ha creado damos clic derecho sobre la mism y seleccionanos Cambiar nombre ; el nombre que yo escribí pare el ejemplo es Sprites.

 

Ya que tenemos nuestra carpeta Sprite hacemos clic derecho sobre la misma y seleccionamos Agregar/Elemento existente.

 

 

Imagen2

 

 

En la ventana que aparece seleccionamos las imágenes y presionamos el botón Agregar.


Creación de una clase para nuestro personaje.

 

Podríamos hacer nuestra carga y animación de nuestro personaje en el mismo archivo Engine.cs pero para no romper con la estructura de la POO (Programación orientada a objetos) vamos a crear una clase que tendrá los métodos y atributos de nuestro personaje.

 

Para crear una clase nueva vamos a hacer lo siguiente: En nuestro explorador de soluciones hacemos clic derecho sobre el nombre de nuestro proyecto (En mi caso Ejemplo2D) y seleccionamos Agregar/Nuevo
elemento.

 

 

Imagen3

 

 

De la ventana que aparece seleccionamos una clase y escribimos el nombre AnimSprite.cs.

 

 

Imagen4

 

 

Yo escogí este nombre más genérico pensando en reutilizar esta clase en un futuro para otro fines pero
puedes llamarla personaje.cs o como quieras.

 

Código de la clase AnimSprite

 

Una vez que ya tenemos las imágenes en la carpeta lo siguiente es escribir el código para subir estas imágenes a un arreglo y así poderlas manipular posteriormente.

 

Lo primero que necesitamos es declarar un arreglo en donde se van a almacenar todos los sprites, asi que vamos a abrir la clase
AnimSprites.cs que acabamos de crear y dentro de la declaración de la clase escribimos la siguiente línea de código:

 

 
    public
Texture2D[] SpriteMov = new Texture2D[6];

 

Como pueden ver la clase que necesitamos para cargar las imagenes es Texture2D, si quieres ver la referencia de Microsoft de esta clase puedes consultarla aquí
.  

 

Vamos a utilzar un arreglo que en este caso tendrá una dimensión fija pero podemos hacer variable en modificaciones futuras. Como vamos a utilizar 6 sprites la dimensión de nuestro arreglo será también de 6.

 

Lo siguiente es definir un Vector que nos servirá para establecer la posición de nuestro personaje en pantalla, estoy utilizando el valor casi aleatorio de 200 en la coordenada X y 200 en Y pero puede ser cualquier valor dentro de nuestra resolución que ocupemos para el juego  que en nuestro caso es de 640×480. Para ello escribimos la siguiente línea de código.

 

      public Vector2 posicion = newVector2(200, 200);

 

Dado que vamos a hacer una animación por cuadros en necesario tener un contado que nos diga que frame se esta deplegando en este momento, para eso vamos a declarar una variable del tipo int que nos almecene esa información. La inicializamos a cero que es el indice de nuestro primer frame.

 

     public int  FrameActual = 0;

 

Ya que tenemos nuestros parametros los siguiente será los métodos. Primero necesitamos un método que nos dibuje en pantalla nuetro personaje; a este método lo llamaremos Draw() y el cuerpo del mismo es el
siguiente:

 

 
   public
 void Draw(SpriteBatch spriteBatch)
     {
       spriteBatch.Begin();
       spriteBatch.Draw(SpriteMov[FrameActual], posicion, Color.White);
       spriteBatch.End();
     }

 

El objetivo del método es simple, dibujar en pantalla el sprite que corresponde a nuestro frame actual y colocarlo en la posición actual, que para nuestro ejemplo será fija. Para lograr esto necesitamos el objeto spriteBatch que viene declarado por defecto al crear un proyecto nuevo y que como comenté en el post anterior, es el encargado de desplegar sprites en pantalla. Este objeto spriteBatch lo vamos a pasar como parámetro por el método Draw() de nuestra clase y lo que vamos a definir un bloque de Begin() End() en el que llamaremos al método Draw() del objeto spriteBatch. El bloque que comienza con el llamado al método Begin() lo que hace es básicamente prepar al dispositivo gráfico para desplegar sprites, este método tiene varias sobrecargas en las que podemos definir enrtre otras cosas la forma de renderes los sprites pero para nuestro caso no ocuparémos ningúna configuración adicional.

 

El método End() lo que hace es liberar el espacio ocupado por los sprites que se cargaron en spriteBatch y regresa al dispositivo gráfico al estado anterior que tenía antes de ser llamado por el método Begin().
El método Draw() recibe como parametros un objeto del tipo Texture2D, una objeto Vector2 para definir la posición y un objeto Color que se utiliza como máscara sobre el sprite que se esta dibujando.

 

Por último necesitamos declarar un método que controle el frame que se va a mostrar. Este método tendrá la siguiente estructura:

 

        public voidUpdateMov()
        {
            if (FrameActual < 5)
           {
                FrameActual = FrameActual + 1;
            }
            else
           {
                FrameActual = 0;
           }
        }

Lo que hace este método es básicamente incrementar la variable FrameActual  hasta que llega a su tope que en nuetro caso es 5. Una vez que llega a este valor se reinicia el contador a cero, lo
que nos regresa al primer frame de nuestra animación.

 

Programación de Engine.cs

 

Lo primero que vamos a hacer es declarar e instanciar un objeto de la clase que acabamos de crear, a este objeto lo llamaré Megaman

 

        AnimSprite Megaman = new AnimSprite();

 

Enseguida declaramos una variable con la que llevarémos el control del tiempo que pasa en la ejecución del programa.

 

        int  TiempoFlow;

 

Ahora, dentro del constructor de la clase (osea dentro Engine()) difinimos primeramente la resolucion de la pantalla, que como comenté antes será de 640×480. Para ello utilizaremos nuestro manejador gráfico y las variables  PreferredBackBufferWidth y PreferredBackBufferHeight.

 

            graphics.PreferredBackBufferWidth = 640;  
            graphics.PreferredBackBufferHeight = 480;

 

Opcionalmente pudes hacer que el juego se despliegue en pantalla completa en lugar de una ventada de 640×480. Para hacer esto tenemos que incluir la siguiente línea de código.

 

            graphics.IsFullScreen = true;

 

A continuación vamos a cargar cada una de las imagenes dentro de nuestro método LoadContent() escribiendo el siguiente código

 

          Megaman.SpriteMov[0] = Content.Load<Texture2D>(“Sprites/MM1″);

         Megaman.SpriteMov[1] = Content.Load<Texture2D>(“Sprites/MM2″);

         Megaman.SpriteMov[2] = Content.Load<Texture2D>(“Sprites/MM3″);

         Megaman.SpriteMov[3] = Content.Load<Texture2D>(“Sprites/MM4″);

         Megaman.SpriteMov[4] = Content.Load<Texture2D>(“Sprites/MM5″);

         Megaman.SpriteMov[5] = Content.Load<Texture2D>(“Sprites/MM6″);

 

Aprevechando que nuestro arreglo SpriteMov fue declarado public (por ahora) lo utilizaremos directamente para cargar cada imagen y esto lo haremos con el método Load del objeto Content. Lo que hace es cargar en cada elemento de nuestro arreglo las imagenes que tenemos en nuestra carpeta Content. El parámetro Texturu2D es para definir el tipo de dato que estamos cargando. La función soporta cualquiera de los siguientes tipos Model, Effect, SpriteFont, Texture, Texture2D, Texture3D y TextureCube aunque no creo que los ocupemos todos en este tutorial o por lo menos no por un buen rato.

 

Dentro de la función Update() básicamente vamos a hacer dos cosas una es llamar a la funcion UpdateMov() de nuestro objeto megaman y la otra es modificar la condición de salida del juego.

Para hacer lo primero bastaría con escribir Megaman.UpdateMov(); sin embargo la animación sería muy rápida y no la podríamos apreciar, por esta razón fue que declaramos la variable TiempoFlow y la vamos a ocupar de la siguiente forma.

 

       TiempoFlow = TiempoFlow + gameTime.ElapsedGameTime.Milliseconds;

 

         if (TiempoFlow > 50)
         {
             TiempoFlow = 0;
             Megaman.UpdateMov();
         }

 

ElapsedGameTime es una propiedad de la clase gameTime que nos regresa el tiempo que ha pasado desde la última vez que se invocó al método Update(); seleccionamos Miliseconds para que el valor devuelto sea en milisegundos (obvio).

 

Cada que entramos al método Update() la variable TiempoFlow se incrementa; este incremento será los n milisegundos que transcurrieron desde la última vez (alrededor de 16 milisegundos) y al alcanzar un valor mayor a 50 actualizará nuestra animación y nos mostrará el siguiente frame. Por otro lado regresaramos nuestro contador a cero para empezar de nuevo.

Lo siguiente que vamos a hacer es modificar la condición de salida del juego ya que originalmete se cumple presonar el botón Back de nuestro control pero nosotros estamos probando el juego en el teclado asi modificamos el código para que quede de la siguiente forma :

 

            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
                this.Exit();

 

De esta forma cada que entre al método Update() verificará si se ha presionado la tecla Esc y si es así terminará el programa. Para el siguiente post hablaré mas sobre la función GetState() y sus usos.

 

Por último vamos a modficar el método Draw(). Para esto lo único que vamos a hacer es llamar al método Draw() de nuestro objeto megaman y pasarle como parámetro el spriteBatch como ya expliqué antes, pero antes de llamarlo tenemos que limpiar la pantalla y dejar un color blanco de fondo porque nuestros sprites tiene fondo blanco.

 

             GraphicsDevice.Clear(Color.White);
             Megaman.Draw(spriteBatch);

 

Listo, si todo salió bien y ejecutas el programa deberías ver algo así, claro que en movimiento:

 

Imagen5

 

Si lo deseas puedes bajar el código fuente completo de aqui.

 

Así de simple es cargar sprites a un programa en XNA. Cabe señalar que parte del código es repetitivo y rompe con algunas reglas de la POO pero me pareció que de esta forma es más fácil de entender. En el siguiente post mejoraré el código ya que espero que haya quedado claro en este post. Si hay alguna duda no dejes de postearla en la parte de abajo y no olvides tampoco
tus comentarios. Nos vemos en el siguiente post.

 

Piroshi


Tags: ,

XNA

 

Antes de empezar a leer este post puedes consultar el post Estructura básica de un VJ para entender mejor los bloques de código que constituyen un video juego y la diferencia con un programa clásico.

 

En el post anterior creamos un nuevo proyecto y pudimos probar que al ejecutarlo nos generaba una ventana en modo gráfico que para nosotros fue el hola mundo gráfico, a continuación vamos a analizar los archivos que nos ha generado pero antes que nada me gustaría hacer unas modificaciones en el código.

 

Como pudimos ver anteriormente al generarse el proyecto se crearon varias carpetas y archivos, primero que nada demos doble clic en el archivo Game1.cs para abrir el código en la ventana principal.  El nombre Game1 es el nombre por defecto asignado a nuestra clase principal y al archivo que la contiene. Si queremos cambiar el nombre de esta clase no basta con solo cambiar el nombre en la clase ya que existen varias partes en el proyecto en las que se hace referencia a este nombre. Para poder cambiar todas las referencias sin tener que buscarlas una por una tenemos que seleccionar el nombre de la clase y presionar F2 para que aparezca una ventana, ahí podemos cambiar el nombre en todas las referencias del código, sin embargo, el nombre de archivo lo tenemos que cambiar directamente del lado derecho.
Para el ejemplo yo cambiaré el nombre a Engine.
A continuación vamos a analizar las clases Program.cs y Engine.cs

 

Program.cs

 

La clase Program será con la que se inicie la ejecución del programa, básicamente la vamos a utilizar para invocar a nuestra clase principal que contendrá todo el código de nuestro juego.
El código base que se generó al crear el proyecto es el siguiente.

 

 

1  using System;
2
3   namespace WindowsGame1
4   {
5      static class Program
6       {
7        /// <summary>
8        /// The main entry point for the application.
9        /// </summary>
10
11          static void Main(string[] args)
12         {
13            using (Engine game = new Engine())
14             {
15                game.Run();
16              }
17         }
18     }
19  }

 

 

En la línea 1 le decimos al compilador que vamos a ocupar el namespace System que contiene librerías básicas para la creación de proyectos. Para conocer las clases que incluye puedes consultar la ayuda de Microsoft aquí.

 

En la línea 3 se declara nuestro namespace el cual tiene el mismo nombre que nuestro proyecto.

 

En la línea 5 se declara la clase Program que tiene el mismo nombre del archivo

 

En la línea 11 se encuentra el método Main() que será nuestro punto de partida del juego, el código (string[] args) que se encuentra como parámetros de entrada se utiliza para mandar comandos por línea de comandos al ejecutar el juego, por ejemplo si al terminar nuestro proyecto tenemos un archivo ejecutable llamo Mijuego.exe podríamos escribir

 

MiJuego Parm1,Parm2

 

Los dos parámetros que escribamos aquí se guardaran en el arreglo args y podremos ocuparlos como queramos.

 

Dentro del método Main() se encuentra una instancia a la clase Engine y una llamada al método Run() de la misma. El análisis de la clase Engine lo veremos más adelante.

 

Otra cosa interesante en este código es que nosotros sabemos que al declarar una clase es necesario instanciar un objeto para poder acceder a sus métodos y parámetros; si aplicamos esta regla al código que estamos analizando ahora necesitaríamos instanciar un objeto de la clase Program y llamar al método Main() a través del mismo. Para evitar esto se declaran tanto la clase Program como el método Main() como static para poder ser invocados sin necesidad de instanciar un objeto.

 

Engine.cs

 

Como comenté antes, en esta clase se lleva a cabo el control principal del proyecto porque contiene las funciones de rendereo en pantalla, carga de recursos, etc.

 

Para analizar el código que se generó automáticamente voy a quitar los comentarios que incluye por default para concentrarnos en las funciones.

 

 

1    using System;
2    using System.Collections.Generic;
3    using System.Linq;
4    using Microsoft.Xna.Framework;
5    using Microsoft.Xna.Framework.Audio;
6    using Microsoft.Xna.Framework.Content;
7    using Microsoft.Xna.Framework.GamerServices;
8    using Microsoft.Xna.Framework.Graphics;
9    using Microsoft.Xna.Framework.Input;
10  using Microsoft.Xna.Framework.Media;
11  using Microsoft.Xna.Framework.Net;
12  using Microsoft.Xna.Framework.Storage;
13
14  namespace WindowsGame1
15  {
16      public class Engine : Microsoft.Xna.Framework.Game
17     {
18       GraphicsDeviceManager graphics;
19        SpriteBatch spriteBatch;
20
21        public Engine()
22          {
23           graphics = new GraphicsDeviceManager(this);
24           Content.RootDirectory = “Content”;
25    }
26
27        protected override void Initialize()
28        {
29            base.Initialize();
30        }
31
32        protected override void LoadContent()
33       {
34            spriteBatch = new SpriteBatch(GraphicsDevice);
35        }
36
37        protected override void UnloadContent()
38        {
39        }
40
41        protected override void Update(GameTime gameTime)
42        {
43
44           if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
45               this.Exit();
46            base.Update(gameTime);
47        }
48
49       protected override void Draw(GameTime gameTime)
50        {
51            GraphicsDevice.Clear(Color.CornflowerBlue);
52            base.Draw(gameTime);
53        }
54 }
55 }

 

 

De la línea 1 a la 12 se agregan los namespace que necesitamos; estos incluyen las clases de la API de XNA que vamos a ocupar.

 

La línea 14 es la declaración de nuestro espacio de trabajo que es el mismo que ocupamos en Program.cs y es el mismo de nuestro proyecto.

 

La línea 16 contiene la declaración de nuestra clase principal que hemos nombrado Engine, esta clase hereda de la clase Microsoft.Xna.Framework.Game por lo que ya tendremos a nuestra disposición todos los métodos y características que requiere nuestro juego.

 

La línea 18 contiene la declaración de nuestro manejador gráfico. Básicamente es el encargado de controlar el hardware de video para desplegar la información en pantalla.  Este controlador nos ahorra el trabajo de configurar el flujo de datos con la tarjeta gráfica que de otra forma sería en verdad complejo hacerlo por nuestra cuenta.

 

La línea 19 declara un objeto de la clase SpriteBatch que es la clase que nos ayudara para la carga de recursos gráficos.

 

De la línea 21 a la 25 tenemos el constructor de nuestra clase Engine en la que instanciamos nuestra manejador gráfico. Le pasamos como parámetro la palabra reservada this, para decirle con que objeto asociar el manejador que en nuestro caso será la clase Engine. La línea Content.RootDirectory = “Content”; es para indicar al compilador que la carpeta raíz donde se almacenan los recursos será la carpeta “Content”.

 

De la línea 27 a la 30 sobrescribimos el método Initialize() de nuestra clase base; En el cuerpo del mismo hacemos un llamado al método Initialize() de nuestra clase base, que como su nombre lo indica, iniciará todos los parámetros necesarios antes de iniciar el ciclo de ejecución del juego. Dado que se ejecuta una sola vez al iniciar el juego, aquí se deben incluir todas las inicializaciones a los parámetros que necesitemos.

 

De la línea 32 a la 35 se sobrescribe el  método LoadContent() que se utiliza exclusivamente para cargar los recursos que vayamos a utilizar. Podríamos borrar este método y hacer nuestra carga utilizando el método Initialize() pero se incluye como un método aparte para tener más ordenado nuestro código. Como cuerpo del método se incluye la inicialización de nuestro objeto spriteBatch que declaramos previamente. Este método será invocado una sola vez en el juego.

 

En la línea 37 se sobrescribe el método UnloadContent() que utilizaremos para liberar la memoria de los recursos que dejemos de utilizar. Será invocado una vez al final del juego.

 

De la línea 41 a la 47 se sobrescribe el método Update() este es el método principal en cuanto a lógica del juego se refiere. Aquí se debe incluir por ejemplo el análisis de colisiones, el chequeo de las pulsaciones de los botones y cualquier actualización de información que se requiera durante la ejecución. El cuerpo del método solo incluye una validación del teclado que termina el juego si se presiona la tecla ESC y contiene un llamado al método Update() de la clase base.

 

Por último, de la línea 49 a la 53 se sobrescribe el método Draw() que es el encargado de refrescar la pantalla cada ciclo de tiempo. Se ejecuta después del método Update() para hacer la actualización gráfica del análisis hecho en este último. El cuerpo del método incluye la línea GraphicsDevice.Clear(Color.CornflowerBlue); que nos limpia la pantalla y la rellena del color azul que se muestra por default. Este puede ser cambiado por otro modificando el parámetro de entrada del método. Al final se incluye un llamado al método Draw() de la clase base.

 

 

Con esto terminamos el análisis de nuestro código, en el siguiente post empezaremos el desarrollo de un juego en 2D no olvides dejar tus comentarios, nos vemos en el siguiente post.

 

Piroshi


Tags:

XNA

 

Antes de empezar puedes checar el post “Consideraciones del lenguaje” si quieres repasar o aprender algunos apectos de C#.

 

Ya que tenemos todo nuestro entorno de desarrollo instalado correctamente lo primero que vamos a hacer es ejecutar Visual Studio Express de la ruta en la que quedó instalado.

 

Al abrir el programa podemos ver cuatro paneles; Del lado derecho tenemos el panel principal, el cual se conecta a Internet (si están conectados en ese momento) para mostrar noticias e información de Microsoft y sus productos.
Del lado izquierdo tenemos 3 paneles, el primero muestra la lista de los proyectos en los que hayamos trabajado recientemente y las opciones de abrir otro proyecto o crear uno nuevo.
En los paneles de abajo pueden encontrar ligas a tutoriales y ayuda para empezar a conocer Visual C#
Lo que haremos a continuación es seleccionar crear proyecto del panel superior izquierdo

 

Crear Proyecto

 

De la ventana que aparece vamos a seleccionar Windows XNA Game , y debemos seleccionar en la parte de abajo el nombre del proyecto y la ruta en la que se va instalar, de lo contrario se guardará el proyecto en la ruta por defecto y con el nombre por defecto, que es el que yo ocupé para el ejemplo.

 

SelecRutaNom

 

Presionamos el botón Aceptar y esperamos a que nos genere el esqueleto de nuestro proyecto.

 

Al terminar la generación y apertura del proyecto podemos observar que se han generado cuatro archivos, que explicaré en el siguiente post.

 

ArchivosProy

 

Lo interesante, por decirlo de alguna forma, es que con esos archivos ya tenemos la inicialización completa del entorno gráfico y la declaración de las funciones principales además el proyecto ya es compilable y ejecutable; esto lo comprobamos al presionar el botón de ejecutar que se muestra en la Imagen.

 

EjectProy

 

Al presionarlo veremos la siguiente ventana.

 

HolaMundoGrafico

 

Ya tenemos nuestro primer programa y lo que sería nuestro “Hola mundo gráfico” aunque no hemos escrito ninguna línea de código, pero lo mejor de todo es que el código que generó por nosotros el API es muy pequeño y fácil de entender pero de esto hablaremos en el siguiente post. No olvides dejar tus comentarios, nos vemos en el siguiente post.

 

 

Piroshi


Tags: ,

XNA

 

Lo primero que necesitamos es descargar los paquetes necesario para tener todo nuestro entorno de desarrollo listo, sin embargo es importante conocer los requerimientos mínimos que debe tener el equipo en el que se haga la instalación

 

Los sistemas operativos que soportan XNA son

 

Microsoft Windows XP    

    

    * Home Edition
    * Professional Edition
    * Media Center Edition
    * Tablet Edition

 

Windows Vista

 

    * Home Basic Edition
    * Home Premium Edition
    * Business Edition
    * Enterprise Edition
    * Ultimate Edition

 

Con respecto al hardware, la característica mas importante es la tarjeta de video, la cual debe soportar DirectX 9.0 o superior  y Shader 1.1 aunque es preferente que sea 2.0 o superior. 

 

Cabe señalar que yo utilizo una Macbook Pro con Windows XP y próximamente instalaré la versión 7 RC de Windows 7 para probar la compatibilidad.

 

Con respecto al software , es necesario tener Visual Studio 2008 o en su defecto bajar la versión de Visual C# 2008 Express Edition e instalar en tu computadora.

 

Una vez instalado, lo siguiente es descargar  XNA Game Studio  [XGS] ; Bajalo y sigue los pasos para instalarlo en tu pc.

 

Si tienes alguna duda o comentario será bien recibido, nos vemos en el siguiente post

 

 

Piroshi


Tags:

Powered by Wordpress
Theme © 2005 - 2009 FrederikM.de
BlueMod is a modification of the blueblog_DE Theme by Oliver Wunder