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

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:

Fantasma

 

A la hora de desarrollar un video juego y si no contamos con un API como XNA nos toparíamos, como ya lo he dicho antes, con varios problemas que hay que resolver, uno de ellos es el bucle principal del juego que aunque no es mayor de los problemas con el que nos enfrentaremos al realizar un juego si sería algo latoso estructurar la lógica para que el juego corra como es debido. Para entender mejor de lo que estoy hablando pensemos primeramente en un programa estructurado clásico.

 

Diagrama_Clasico

 

Este diagrama muestra en programa muy sencillo pero que nos servirá muy bien de ejemplo.

 

En un programa estructurado como el del diagrama, el programa realiza una sería de instrucciones definidas por el usuario y al momento de solicitar un dato se detiene y no hace nada mas que esperar la entrada de datos, al recibir los datos pasa a la siguiente instrucción y aunque tenga condiciones y bucles que modifican el flujo del programa podemos decir que es lineal.

 

Un videojuego, por su lado, tiene una estructura diferente, la estructura básica esta dividida en bloques y aunque cada bloque puede estar dividido en varias partes podemos decir que principalmente tiene un módulo de carga de datos, un módulo de principal con un bucle infinito y un bloque de descarga de datos o de liberación de memoria.

 

El siguiente diagrama muestra un ejemplo del bucle principal de un juego.

 

Diagrama_Juego
Básicamente el bucle principal del juego consiste en una actualización a través del tiempo en la que se realizan varias acciones, por ejemplo, se checa si se presionó algún botón y de ser así verifica que sea un botón válido de acuerdo a las condiciones que nosotros hayamos definido. Una vez que pasó por la verificación de botonazos calcula la posición de los elementos que tienen una acción independiente a cualquier entrada del control como por ejemplo algún efecto visual que tenga nuestro escenario. Ya que  tenemos la información de todos nuestros elementos que aparecerán, se procede a desplegar en pantalla todos nuestra escena armada.

 

Este ciclo contiene generalmente mas verificaciones y se detiene hasta que se realiza una acción predefinida por nosotros que pone fin al juego, como por ejemplo, que se hay presionado la tecla ESC o algún botón en el control.

 

Ahora ya puedes consultar el post Análisis del código Inicial donde verás estos conceptos a nivel de código para un proyecto en C# y XNA.

 

 

No olvides dejar tus comentarios, nos vemos en el siguiente post.

 

Piroshi


Tags:

Fantasma
Para aquellos que aunque tengan experiencia en programación pero no han utilizado C# o ya no recuerdan bien incluyo como parte de la teoría básica antes de iniciar los proyectos en XNA una lista de consideraciones para que se entienda mejor el código.

 

Uso del punto y coma (;) después de una instrucción.

 

Siguiendo con la herencia de las anteriores versiones del lenguaje C al escribir un comando terminamos la línea con un punto y coma (;)

 

Ejemplo:

 

base.Update(gameTime);
Direccion = DERECHA;

 

Al escribir la definición de una función, namespace, clase o algún tipo de loop o condición no se termina la instrucción con (;)

 

Ejemplo:

 

protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
}

 

Esto más que llamarlo una excepción es mas bien por el hecho de que la instrucción no termina con definición de un bloque o una condición, por ejemplo, en el caso de un if si tenemos la condición:

 

1 If (Distancia==0)
2   {
3        Accion = NADA;
4       Personje.Correr(10,20);
5     }

 

Si se cumple la condición de la línea 1 el compilador leerá la siguiente línea pero como parte de la instrucción if. Si la siguiente instrucción es un bloque entonces ejecutara lo que esté dentro del bloque pero si colocamos un (;) al final de la línea 1 le estaremos diciendo al compilador que lo que tiene que hacer si se cumple la condición es “Nada” lo mismo ocurre al definir una función; después de escribir la declaración de la función el compilador busca la definición dentro de un bloque limitado por  {}.

 

Comentarios

 

Existen dos tipos de comentarios en  C#  que son los mismo que se ocupan en versiones anterior de C. Primero están los cometarios /* <texto>*/    Estos comentarios  pueden abarcar varias líneas  y todo lo que este escrito dentro de estos símbolos  no será tomado en cuenta por el compilador.

 

Ejemplo:

 

/*
Esto es un comentario
en varias
líneas
*/

 

Los siguientes tipos de comentarios se escriben con los símbolos // y  solo abarca una  línea, así que  lo que este escrito a la derecha no será tomado en cuenta por el compilador pero la siguiente línea ya no será tomado como comentario.

 

Ejemplo:

 

// Esta línea no será leída por el compilador.

 

Variables

 

La declaración de variables sigue manteniendo la sintaxis clásica de C <tipo de dato> <Nombre de la variable>;

 

Ejemplo

 

Int Distancia;
Float Energia;

 

El nombre de las variables no puede ser una palabra reservada del lenguaje, debe comenzar con una letra y puede contener también números y el carácter (_). Se recomienda utilizar un estándar  para nombrar las variables como por ejemplo empezar siempre el nombre con una letra mayúscula. Se pueden utilizar otras convenciones como la notación Hungara que consiste en incluir el tipo de dato en los primeros caracteres del nombre de acuerdo a la siguiente lista:

 

b Booleano (int)
by BYTE o UCHAR (unsigned char)
c Carácter (un byte)
dw Entero largo de 32 bits sin signo (double word)
f Flags empaquetados en un entero de 16 bits
h Manipulador de 16 bits (handle)
l Entero largo de 32 bits
lbl Objeto Label
lp Puntero a entero largo de 32 bits
lpfn Puntero largo a una función que devuelve un entero
lpsz Puntero largo a una cadena terminada con cero
n Entero de 16 bits
p Puntero a entero de 16 bits
e Enumeración
pt Coordenadas (x y) empaquetadas en un entero de 32 bits
rgb Valor de color RGB empaquetado en un entero de 32 bits
sz Cadena terminada en cero
txt Cajas de texto
w Entero corto de 16 bits sin signo (Word)

 

Ejemplo

 

Int IDistancia;
bool bColision;

 

Lo importante es siempre utilizar nombres que describan el uso de la variable.

 

Asignación de variables.

 

La asignación de valores como en varios lenguajes es  <Variable> = <Valor>;

 

Ejemplo:

 

Distancia = 10;
Energia =  2.4;

 

También se puede asignar un valor a una variable desde que se declara

 

Ejemplo:

 

Int Distancia = 10;

 

Métodos

 

Los métodos que son las funciones propias de una clase y cualquier tipo de función independiente se declaran con la sintaxis : <valor que regresa><Nombre de la función>(<valores que recibe>)

 

Ejemplo:

 

Void  Correr(int Direccion, float Distancia, float Velocidad)
{
// Aquí va el cuerpo de la función
}

 

para llamar una función se escribe el nombre de la función y los parámetros entre paréntesis, si se trata de un método se debe incluir el nombre de la instancia del objeto.

 

Ejemplo:

 

DespliagaMensaje(“Menú Principal”);
Personaje.Correr(SUR, 10.5,20.6);

 

Clases

 

Las clases se declaran con la palabra clave CLASS seguidas del nombre de la clase

 

Ejemplo:

 

public class Game1 : Microsoft.Xna.Framework.Game

 

Del lado izquierdo se escribe la visibilidad de la clase y los dos puntos (:) se utilizan para definir a la herencia, de esta forma la clase que se encuentra del lado derecho será la clase padre.

 

Para instanciar un objeto de una clase se utiliza la palabra reservada New

 

Ejemplo:

 

graphics = new GraphicsDeviceManager();

 

En  este ejemplo se instancia el objeto graphics de la clase GraphicsDeviceManager.
Ahora ya puedes pasar al post donde empezamos a analizar el código de XNA

 

No olvides dejar tus comentarios, 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