sábado, octubre 28, 2006

Logica de una plantilla "Windows Game XNA"

Buenas a todos, una vez de vuelta del viaje de esta semana, vamos a seguir aprendiendo acerca de las bases de un juego creado con XNA.

En este articulo, voy a intentar explicar la lógica del programa que "XNA Game Studio Express" diseña por nosotros cuando le ordenamos que cree un proyecto del tipo "Windows Game XNA". Esta plantilla, como ya vimos en artículos anteriores, esta diseñada para crear el famoso bucle de juego, que ya explicamos con anterioridad. En este punto, vamos a bajar a nivel del código fuente y vamos a ver como se ejecuta el programa, que líneas de código van corriendo, y como salta la ejecución del programa de un sitio a otro y porque.

El código que vamos a explicar es el de un juego vacío, es decir, el resultado de la ejecución de una plantilla "Windows Game XNA" sin ninguna modificación, es el de una pantalla de juego, con unas determinadas dimensiones, pero totalmente vacía. A pesar de esto, por debajo de esa pantalla, la ejecución del programa esta siguiendo el famoso modelo de bucle, y aunque parezca que el juego no hace nada, realmente demostraremos que cada segundo que pasa el programa esta actualizando la lógica del juego, aunque este vacía, y también esta dibujando la pantalla, igualmente vacía. Este ejercicio, aunque parezca que no tiene mucho misterio, nos será de gran ayuda mas adelante cuando queramos empezar a incluir código en nuestro juego, ya que sabremos donde situar ese código y porque.

Para seguir este articulo, creamos un nuevo proyecto, le decimos que queremos utilizar la plantilla "Windows Game XNA" y pulsamos F11. Al pulsar F11, le estamos ordenando al IDE (recordar que esto del IDE era el entorno de desarrollo), que ejecute el programa, pero en vez de ejecutarlo a toda velocidad, y sin que nosotros podamos controlarlo, le estamos diciendo que queremos que se detenga en cada una de las líneas que vaya a ejecutar para que podamos ver que esta haciendo en cada punto. De momento no nos va a ser necesario conocer todo el código que esta escrito, ni el por que se encuentra separado en varios componentes, solo queremos ver la lógica del programa, así que mas adelante hablaremos de estos otros temas. Comencemos:

static void Main(string[] args)
{
using (Game1 game = new Game1())


El programa empieza en este punto. En todo programa debe existir al menos un método Main(), siempre dentro de una clase. Recordar que estamos trabajando con un lenguaje que se puede considerar como orientado a objetos puro, donde todo debe estar dentro de objetos. En este caso, el método principal, por el que empieza todo programa, el método Main(), se encuentra dentro de una clase llamada Program, la cual se ha creado utilizando esta línea:

static class Program

Esta clase, como se puede ver en el código, lo único que contiene es el arranque del programa a través del método Main(). Dentro del método Main() nos encontramos con esta línea:

using (Game1 game = new Game1())

En esta línea realmente se están ejecutando dos instrucciones, y se podría leer de la siguiente forma:

Game1 game = new Game1();
using(game)

En la primera línea, lo que se ejecuta, es la creación de una instancia nueva de un objeto, cogiendo como plantilla para la creación de ese objeto la clase de objetos Game1.
La segunda línea, marca el objeto recién creado game para que pueda liberar la memoria fácilmente una vez no sea necesario utilizar. Ya hablaremos en artículos posteriores acerca de la sentencia using un poco mas a fondo.

Recordar que estas dos líneas estaban en el programa original como una sola, por lo que al invocar la creación de un nuevo objeto el programa va a saltar a lo que se denomina el constructor de ese objeto, así que pulsamos F11 de nuevo y vemos que salta a otra parte del código, veamos que tenemos por ahí...

partial class Game1 : Microsoft.Xna.Framework.Game
{
public Game1()
{
InitializeComponent();
}


Como la primera línea de código le ordenaba la creación de un objeto clase Game1, llamado game, el programa se ha ido a la definición de la clase Game1 para instanciar ese nuevo objeto. En este caso nos encontramos con que la línea donde empieza la creación de la clase es:

partial class Game1 : Microsoft.Xna.Framework.Game

esta línea indica que todo el código que aparezca a continuación entre las llaves {} será código de la clase, y que cada objeto creado a partir de esta clase debe llevar. En primer lugar, cada vez que se crea un objeto a partir de una clase, se llama a lo que se denomina el constructor de la clase. Este constructor no es ni más ni menos que un conjunto de sentencias que queremos que se ejecuten cuando se cree el nuevo objeto. En nuestro caso el constructor esta definido de esta f0rma:

public Game1()
{
InitializeComponent();
}

¿como se sabe que el constructor de la clase es esa parte del código?,.... pues muy sencillo, la respuesta es porque el constructor es un método que tiene exactamente el mismo nombre que el nombre de la clase, en este caso el nombre es Game1. Este constructor en concreto, solo tiene una instrucción, que simplemente hace una llamada para que se ejecute a un método llamado InitializeComponent(), que veremos mas tarde, ya que antes de continuar me gustaría anotar un par de detalles de la línea de definición de la clase:

partial class Game1 : Microsoft.Xna.Framework.Game

en esta línea, básicamente, le estamos diciendo que vamos a crear una nueva clase (class), que el código de esta clase no se encuentra completamente en este fichero, sino que tiene otras partes en otro fichero (partial), y que además esta clase se va a llamar Game1(Game1) y para finalizar, que esta clase Game1 es hija de una clase padre llamada Microsoft.Xna.Framework.Game ( : Microsoft.Xna.Framework.Game).

Si seguimos pulsando F11, y ejecutamos la línea donde se llama al método InitializeComponent(), vamos a ver que nos vuelve a saltar a otro sitio distinto, con el siguiente código:

partial class Game1
{
private void InitializeComponent()
{
this.graphics = new Microsoft.Xna.Framework.Components.GraphicsComponent();
this.GameComponents.Add(this.graphics);
}


Antes de nada, fijaros en que he ido quitando los comentarios, para que el código quede mas simplificado. Bien, en esta parte del código, vemos que empieza con una instrucción:

partial class Game1

tal y como hemos visto antes, cuando se definía la clase Game1, se utilizaba la palabra partial, que como he comentado, significaba que el código de la clase estaba repartido en varios ficheros. Pues esta línea indica que todo lo que quede entre las llaves {} de después de esta sentencia, forma parte también de la clase Game1. En este caso, al continuar con la ejecución, como hemos hecho una llamada al método InitializeComponent(), lo que va a hacer ese ejecutar las líneas de código de ese método:

this.graphics = new Microsoft.Xna.Framework.Components.GraphicsComponent();
this.GameComponents.Add(this.graphics);


Estas líneas empiezan a ser ya un poco mas complejas. En primer lugar, es importante conocer el significado de this.graphics . La palabra clave this hace referencia a la propia clase, es decir, en nuestro caso, ese this viene a significar lo mismo que Game1, que recordar que es el nombre del a clase donde se esta ejecutando este código. El punto(.) que separa el this de graphics, significa que lo que se encuentra a la derecha, en este caso la palabra graphics es algo que se encuentra dentro de lo que aparece a la izquierda, en este caso el this, que hace referencia como hemos visto antes a la propia clase (Game1). Pues bien, básicamente esta sentencia significa que, dentro de la clase Game1 (this), existe un contenedor llamado graphics(ahora veremos de donde sale), y que dentro de ese contenedor lo que queremos meter es un nuevo objeto (new) creado a partir de la clase Microsoft.Xna.Framework.Components.GraphicsComponent . Vamos por partes ahora, en primer lugar, ese contenedor graphics no se ha creado de la nada, sino que ha sido definido con anterioridad, aunque nosotros no lo hayamos visto a través del F11, y es que ocurre que algunas líneas del código, como la creación de variables, y algunas otras instrucciones, no se muestran al depurar el código usando el método de paso a paso (F11). En este caso, ese contenedor, que estamos viendo que va a contener un objeto, ha sido creado dentro de la propia clase Game1, y se encuentra unas pocas líneas mas abajo (pero fijaros que fuera del método InitializeComponent() ):

private Microsoft.Xna.Framework.Components.GraphicsComponent graphics;

fijaros, en esta línea, le estamos diciendo que dentro de la clase Game1, nos cree un contenedor llamado graphics y que va a ser del tipo Microsoft.Xna.Framework.Components.GraphicsComponent . Cuando hablamos de un tipo, estamos refiriéndonos a la forma que tiene que tener la información para caber en ese contenedor, en este caso, en graphics, solo entrara información con la forma de Microsoft.Xna.Framework.Components.GraphicsComponent , que en este caso es una clase de objetos.

En resumen, hemos creado un contenedor con una determinada forma, y luego hemos metido una instancia de un objeto, que justamente tiene esa forma, dentro de ese contenedor.

Este componente graphics, que es un objeto de la clase Microsoft.Xna.Framework.Components.GraphicsComponent va a contener una definición de nuestro display grafico. Esta definición nos va a ayudar mas tarde a muchas cosas, como por ejemplo a ver que resolución tendrá nuestro juego, a controlar si el display grafico esta listo cuando vayamos a dibujar, y un largo etcétera...

Me gustaría apuntar otro detalle importante. Recordar que en los primeros artículos, hablábamos de lo que era el XNA framework, y lo definíamos como una librería de código que íbamos a utilizar en nuestros juegos. Pues aprovecho para usar este ejemplo que acabamos de ver para que dejar los conceptos del framework totalmente claros. Imaginaros esta librería como pequeños trozos de código, organizados en una estructura como las carpetas de vuestro disco duro. En el caso que acabamos de ver (Microsoft.Xna.Framework.Components.GraphicsComponent), le estamos diciendo que dentro de la carpeta Microsoft, hay otra llamada Xna, que a su vez contiene Framework, esta a su vez Components, y dentro de esta carpeta Components, tenemos un fichero llamado GraphicsComponent. Este fichero realmente es una clase, que contiene una serie de código, y lo que estamos haciendo al meterlo dentro del contenedor graphics, que recordar se encuentra a su vez dentro de Game1, es disponer de todo ese código que se encuentra en GraphicsComponent, para que pueda ser utilizado en nuestro programa. Si no utilizásemos el framework de XNA, para usar el código de GraphicsComponent, tendríamos que escribirlo nosotros mismo, con la dificultad y perdida de tiempo que esto supone.

Bueno, siguiendo con el código, vemos que la siguiente línea que se ejecuta es:

this.GameComponents.Add(this.graphics);

Esta línea lo único que hace es organizar un poco nuestra clase. En este caso, dentro de nuestra clase Game1, que recordar en esta línea esta representada por la palabra clave this, tenemos definido un atributo llamado GameComponents, y que dentro de este atributo queremos añadir(add) el contenido de this.graphics, el cual ya conocemos lo que tiene del paso anterior. Básicamente, y para simplificar, lo que estamos haciendo es añadir this.graphics, dentro de una colección de componentes del juego. Este paso, aunque en un principio no nos diga nada, y no le veamos utilidad posteriormente, veremos que es totalmente necesario, ya que en el código que no vemos ni tocamos, es decir el código del propio framework de XNA, se hacen una serie de llamadas, como por ejemplo, a actualizar todos los componentes del juego, y si no hemos metido nuestro componente grafico dentro de esa colección, este componente grafico se nos va a quedar sin actualizar, ya que recordar, no estaría dentro de la colección de componentes del juego.

Perfecto.... espero que hasta este punto este todo más o menos claro. Si seguimos pulsando F11, vemos que llega a la sentencia:

game.Run();

Esta sentencia se encuentra dentro de la sentencia using que hemos visto justo al principio del articulo, ya que recordar que la ejecución del código se había ido a otro sitio ya que se tenia que instanciar una clase llamada Game1 la cual iba a tomar forma como objeto en game. Recordar esta línea

using (Game1 game = new Game1()),

ahora que estamos situados, comprobamos que lo que se realiza en game.Run() , es la llamada a un método (Run()), del objeto game (recordar que game es una instancia de Game1, y que esta clase a su vez es hija de Microsoft.Xna.Framework.Game). Pues bien, este método Run() esta definido dentro de la clase padre Microsoft.Xna.Framework.Game, y que es heredado por Game1, y que simplemente indica al programa que tiene que comenzar. En este momento es necesario que tengamos en mente el bucle de un juego, tal y como os mostré en los artículos anteriores (diagrama incluido). En ese bucle del juego, se llamaba alternativamente (no en todos los caso es alternativamente pero eso ahora no merece la pena discutirlo) a distintas partes del código del juego, una para actualizar la lógica del juego, y otra para mostrar en pantalla los distintos elementos gráficos. En nuestro caso, el método Run(), que recordar que no definimos nosotros, sino que viene en el framework de XNA, simplemente lo que hace (entre otras cosas que nosotros no vemos) es llamar al método draw(). Este método, como veremos a continuación, contiene la parte del código dedicada a mostrar por pantalla el resultado de nuestro juego. Este método Draw() se encuentra en nuestra clase Game1, y ahora veremos como funciona.

protected override void Draw()
{
if (!graphics.EnsureDevice())
return;
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
graphics.GraphicsDevice.BeginScene();
DrawComponents();
graphics.GraphicsDevice.EndScene();
graphics.GraphicsDevice.Present();
}


De nuevo he quitado los comentarios para que sea mas sencillo. Esta parte del código es el método Draw() creado por defecto, y que recordar que no hace nada en especial, simplemente contiene el código necesario para que, podríamos decir, se ejecute sin errores, y algunas líneas que nos ayudara a crear un juego usando esta plantilla. Estudiémoslo por partes:

protected override void Draw()
{
....
....
}


En primer lugar vamos a ver que tiene de especial la definición del método Draw(). Este método solo es accesible desde la propia clase o clases hijas (protected), aunque esto de momento no nos importe mucho. Además este método sobrescribe (override) la definición del método Draw() original definido en la clase Microsoft.Xna.Framework.Game.

Recordatorio importante: La clase Game1 que estamos definiendo es hija de la clase Microsoft.Xna.Framework.Game. Esta clase Microsoft.Xna.Framework.Game en su origen contiene una definición virtual del método Draw(), esto de "virtual" significa que al crear una clase hija, si nosotros queremos, podemos borrar la definición original y crear una nueva definición, que es justo lo que estamos haciendo al añadirle en la creación del método la palabra override.

A continuación se utiliza la palabra clave void para definir que este método se va a ejecutar pero no nos tiene que devolver nada como resultado, aunque de esto ya hablaremos en artículos posteriores y no nos interesa mucho ahora. Y finalmente, utiliza la palabra Draw() para definir el nombre del método.

Vamos con el código de dentro de Draw():

if (!graphics.EnsureDevice())
return;

estas dos líneas, se podrían leer de esta forma:

if (!graphics.EnsureDevice()) return;

fijaros en que solo hay un punto y coma, así que se puede leer como he puesto en una sola instrucción.

El significado de esta instrucción es: Si no tenemos un display grafico en estado usable termina con la ejecución del método Draw().

Si vamos por partes vemos que empieza con una sentencia If, esta sentencia condicional modifica la lógica de ejecución del código en función de una condición. En este caso la condición es !graphics.EnsureDevice() . El símbolo ! del principio indica negación (not), es decir va a alterar el resultado de graphics.EnsureDevice() . La instrucción graphics.EnsureDevice() indica que dentro de graphics existe un método llamado EnsureDevice(), recordar que graphics contenía una instancia de una clase que existía en el framework llamada Microsoft.Xna.Framework.Components.GraphicsComponent , y que si hacéis memoria, nos iba a ayudar a controlar algunos aspectos del display grafico, sin que nosotros tuviéramos que escribir ningún tipo de código para este propósito. Pues bien, al llamar a EnsureDevice(), este método nos devolverá un True (o indicador de acierto) si nuestro display grafico se encuentra activo y usable. Si juntamos ahora toda la sentencia vemos que estamos preguntando Si (if) no es cierto (!) que el dispositivo grafico (graphics) esta activo y usable (EnsureDevice())...... Si vemos ahora la otra parte de la sentencia, vemos que si esa frase se cumple lo que hará el programa es ejecutar la instrucción return, que únicamente indica al programa que debe de abortar la ejecución del método, en este caso el método Draw(). Como se puede adivinar, estas sentencias (que la hemos estudiado como una sola) simplemente controla que si no tenemos un display grafico activo y usable que deje de ejecutar las ordenes encargadas de dibujar en pantalla.

Seguimos pulsando F11 y nos encontramos con:

graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

esta instrucción hace una llamada a un método llamado Clear(), dentro de nuestro componente grafico graphics.GraphicsDevice (fijaros como nos esta siendo de utilidad el haber creado graphics), y a este método le estamos pasando un atributo, Color.CornflowerBlue.

De nuevo recordar que nosotros no hemos escrito el código que se encuentra dentro de graphics, sino que es un objeto instanciado a partir de la clase Microsoft.Xna.Framework.Components.GraphicsComponent, y por esta instrucción podemos adivinar que dentro de esta objeto tenemos un método llamado Clear(), que de nuevo nosotros solo estamos llamando para usarlo pero no lo hemos escrito, sino que ya esta dentro del framework de XNA. Este método Clear, simplemente lo que hace es limpiar el buffer que será mas tarde pasado a nuestro dispositivo grafico (Tarjeta grafica ---> Pantalla). A la hora de limpiar ese buffer lo puede hacer de muchas formas, como ya veremos en artículos posteriores, pero en este caso lo va a limpiar pintando todo el buffer de un color único. Este color se encuentra entre los paréntesis de la llamada al método, y este valor es lo que se denomina como un atributo, es decir, llamamos a las líneas de código del método Clear(), pero además lo llamamos pasándole un valor, que en este caso es un color, y que el código de dentro de la definición del método utilizara para lo que necesite. A la hora de pasarle el atributo, que en este caso es un color, se lo tenemos que pasar en un formato que lo entienda. En el caso que nos ocupa, este valor tiene que ir en un formato especial, que es el formato Color.

¿Como que formato color? mmmh no entiendo esto de que un color sea un formato....

bien, llegado a este punto, me gustaría hablar de un elemento que se utiliza en la programación orientada a objetos, y que nos va a ayudar a entender esto de que Color es un formato de datos. En la programación orientada a objetos vamos a poder crear nuestros propios tipos de datos, es decir, vamos a poder crear contenedores que tengan una forma concreta, y que a nosotros nos interesa por el motivo que sea, para poder luego meter información en ese formato. Un ejemplo muy sencillo para explicar esto es el que nos hemos encontrado en esta parte de código. Para el ordenador, un color no es el blanco, el azul, o el rosa, simplemente es la combinación de tres colores primarios, rojo, verde y azul (RGB, Red, Green, Blue). Pues bien, a la hora de pasarle el atributo a Clear(), le tenemos que dar un color, en el formato adecuado, que seria algo como esto (24,54,102), donde cada numero representa cada uno de los componentes fundamentales del color. Para simplificar las cosas, dentro del framework de XNA se ha creado lo que se llama una estructura, que es, como he contado antes, la definición de un contenedor con una determinada forma. En el caso de la estructura Color, en su definición, se ha establecido que para meter información dentro de esta estructura debemos de meter la información en este formato (byte, byte, byte), donde cada Byte tiene que ser un numero entre 0 y 255 (potencia de 2). En el código del programa vemos que llama a Color.CornflowerBlue. , pues simplemente le esta indicando que busque dentro del framework de XNA por la definición de la estructura Color, que ya hemos visto antes como esta definida, y dentro de esta definición busque por el valor de CornflowerBlue, que podría ser, por ejemplo (12,32,207). Este color CornflowerBlue viene definido dentro del framework de XNA por comodidad, al igual que muchos otros. Si quisiéramos, en vez de utilizar colores definidos por defecto podríamos crearnos para el parámetro que pasamos a Clear() nuestro propio color. Seria tan sencillo como hacer

graphics.GraphicsDevice.Clear(new Color(255,255,255));

donde le estamos diciendo que cree un nuevo (new) color (Color), con los componentes RGB 255, 255 y 255 ((255,255,255)).

Seguimos con F11 y nos encontramos:

graphics.GraphicsDevice.BeginScene();

Esta sentencia simplemente utiliza nuestro componente grafico graphics (de nuevo!!!!) y llama a un método llamado BeginScene(), el cual prepara el buffer para empezar a ser llenado. Antes he nombrado a este buffer pero no he explicado su función, así que aprovecho ahora para hacerlo. Dentro del método de dibujado Draw(), se suele realizar varios pasos: se borra el contenido de la ejecución anterior del método Draw(), de la forma que hemos visto antes con el Clear, después se guarda todo lo que queremos mostrar en un espacio de memoria, el famoso buffer, y cuando esta todo guardado simplemente se ejecuta una instrucción para que se vuelque el contenido de ese buffer en la memoria de la tarjeta grafica, la cual a su vez lo envía a nuestro monitor, permitiéndonos ver el resultado en pantalla.

El método BeginScene(), como he dicho antes, prepara el buffer para ser llenado. Este método se ejecuta antes de empezar a meter cosas en el buffer, y como veremos ahora, cuando ese buffer esta completo con todos los elementos que queremos mostrar, se va a cerrar con otro método, el método EndScene()

Continuamos con el F11......

DrawComponents();

Este método esta definido en la clase del framework XNA Microsoft.Xna.Framework.Game , y que ha su vez es la clase padre de nuestra clase Game1. Este método ejecuta una serie de instrucciones para enviar al buffer todos los elementos que le hayamos dicho que queremos dibujar, y esto a su vez para todos los componentes del juego que tengamos en nuestro juego. Esto de los componentes de juego lo veremos en artículos posteriores, pero básicamente, podemos dividir nuestro juego en varios elementos , para que sea mas fácil de controlar, por ejemplo, podríamos crear un componente de juego para que nos mostrarse el marcador de un juego de tenis y dibujar por un lado la cancha y por otro lado el marcador.

Una vez que hemos llenado el buffer, procedemos a cerrarlo con la siguiente instrucción:

graphics.GraphicsDevice.EndScene();

Si continuamos nos encontramos con:

graphics.GraphicsDevice.Present();

donde llamamos al método Present(), el cual envía a la tarjeta de video el contenido del buffer, haciendo que esta a su vez lo mande a nuestro monitor y veamos en pantalla representado un frame de nuestro juego. Daros cuenta que aunque no lo he mencionado, se vuelve a utilizar el objeto graphics para realizar todas estas operaciones.

Y con esto se termina el método Draw(), que era el encargado de mostrar en pantalla el contenido de nuestro juego. En un juego que no este vacío, como es nuestro caso, entre las líneas:

graphics.GraphicsDevice.BeginScene();

y

DrawComponents();

se debería incluir el código para ir metiendo en el buffer todos los elementos que queramos que aparezcan en escena, como por ejemplo, si fuera un juego de tenis, los jugadores, las raquetas, la pelota, la red, la cancha , etc....

Llegado a este punto, y si continuamos pulsando F11 vamos a ver que es posible que el método Draw() se ejecute varias veces seguidas, sin ninguna razón aparente. Aunque esto ya lo explicare mas adelante, resumiendo mucho podríamos decir que en función de los frames por segundo que queramos que tenga nuestro juego, nosotros controlaremos cuantas veces seguidas se repite el método Draw() por cada 1000 milisegundos. Lo dicho, no quiero entrar en mucho detalle con este asunto ya que lo explicare en otros artículos, así que vamos a seguir pulsando F11 hasta que veamos que deja de repetirse el código de Draw() y salta a otro sitio, concretamente al método Update().

El código contenido dentro del método Update(), como su nombre indica, estará encargado de actualizar la lógica del juego, poniendo el caso hipotético que he usado antes de un juego de tenis, en esta sección update deberíamos controlar cosas como variar la posición de la pelota si esta se encuentra en movimiento, chequear si el jugador ha pulsado alguna tecla para mover a los jugadores, o actualizar el marcador en el caso de que se haya producido un tanto nuevo.

Como es de esperar, el código del método Update(), en nuestro caso, no hace nada, simplemente como he comentado antes, las justas y necesarias para que no haya problemas en la ejecución del juego, y alguna otra que nos han sido creadas para cuando empecemos a crear un juego nuevo usando esta plantilla.

Echémosle un vistazo al código...

protected override void Update()
{
float elapsed = (float)ElapsedTime.TotalSeconds;
UpdateComponents();
}



Para empezar, y como hicimos con Draw(), veamos la definición del método Update():

protected override void Update()

Exactamente igual que Draw(), este método esta protegido (no nos importa mucho esto ahora), sobrescribe la versión original de Update() de la clase padre (override), no devuelve ningún valor (void) y obviamente, se llama Update() (Update()).

La primera línea que nos encontramos es:

float elapsed = (float)ElapsedTime.TotalSeconds;

En esta línea, estamos se crea un contenedor (variable) llamado elapsed el cual tiene un formato en concreto, que es float. Float nos permite guardar números con hasta 7 decimales, desde Aprox. 1.5 x 10-45 a 3.4 x 1038 . Ahora veremos porque se utiliza este tipo de datos para la creación de este contenedor. En la segunda parte del código, después del igual, el cual representa que el contenido de la parte de la derecha se va a meter en la parte de la izquierda (en nuestro contenedor float) vemos que se hace una llamada una propiedad llamada ElapsedTime. Esta propiedad, definida en la clase padre de nuestra clase Game1 (recordáis cual era la clase padre verdad), contiene el tiempo que ha pasado desde la ultima vez que se llamo al método Update(), y además, al añadirle el TotalSeconds, le esta indicando que a la hora de devolver el valor, lo exprese en forma de segundos completos mas la parte decimal. Al incluir además de prefijo a esta llamada la parte (float) , le estamos diciendo que el valor del tiempo pasado desde el ultimo update sea representado en formato float, que como hemos visto antes tendrá una parte entera y hasta 7 decimales. En resumen, al ejecutar esta instrucción vamos a guardar en el contenedor elapsed el tiempo que ha pasado desde la ultima ejecución del método Update, en un formato de este estilo X.XXXXXX , y representado en segundos. Aunque no quiero de nuevo entrar en profundidad, comentaros que en la plantilla por defecto este valor va a ser siempre 0,0166666 segundos, que es uno partido sesenta (1/60) segundos. Este valor, y la forma en el que lo utilicemos, nos ayudara a controlar en nuestros juegos el flujo de actualización del mismo, por ejemplo, y para que quede claro, podemos hacer que nuestra bola del juego de tenis varíe en un pixel su posición en pantalla cuando haya pasado 0.5 segundos, es decir, cuando elapsed haya pasado aproximadamente 3 veces (es decir 3 updates). Ya veremos también en artículos posteriores como controlar este parámetro, y como hacer que el método Update se ejecute mas lento, mas rápido, o simplemente se ejecute tan rápido como la CPU de nuestro ordenador pueda.

Seguimos con el código, que ya estamos terminando y nos encontramos con:
UpdateComponents();

La llamada a este método, el cual este definido como podéis imaginar, dentro de la clase padre (ahora si que ya no digo el nombre...) de nuestra clase Game1, se encarga de enviar las instrucciones necesarias a todos nuestros componentes de juego, para que se actualicen. Daros cuenta que este método es similar al método que vimos en Draw() llamado DrawComponents(), el cual tenia el mismo propósito pero para el método de dibujado.

En el caso de un juego que no este vacío como este, nuestro código debería incluirse entre

float elapsed = (float)ElapsedTime.TotalSeconds;

y

UpdateComponents();

ya que es muy probable que queramos usar la variable elapsed en nuestro código propio de Update() y finalmente una vez modificada la lógica del juego tengamos que llamar al método UpdateComponents() para actualizar todos los componentes de juego.

Y con esto termina el código de la parte de Update(). Si seguimos pulsando F11 continuamente, veremos que al igual que ocurría con Draw() se ejecuta varias veces seguidas para finalmente volver a pasar al método Draw(), el cual se ejecutara tal y como vimos anteriormente y volverá a llamar a Update() y así continuamente hasta que terminemos la ejecución del juego, creando el ya conocido bucle de juego.

Para terminar, deciros que espero que este articulo, aunque extenso(me ha llevado varias horas), os sirva para entender completamente que nos encontramos al crear un juego nuevo usando la plantilla de "Windows game XNA" y que a partir de estas bases podáis ser capaces de entender donde tenemos que empezar a incluir nuestro código para la creación de nuestro juego.

Tal y como ocurrió la semana pasada, esta semana por motivos de trabajo también estaré de viaje, concretamente en Bilbao, así que veo difícil el poder actualizar el blog hasta la semana que viene a estas horas, así que espero que tardéis en digerir este articulo para que no os quedéis pendientes de actualizaciones, por lo menos en eso, una semana...

Un saludo a todos

domingo, octubre 22, 2006

Regalo por unos dias sin actualizaciones

Como esta semana que entra voy a estar fuera de Madrid, concretamente en Viena(Austria), y no voy a poder escribir mucho en el blog, he pensado en dejaros un pequeño juego que estoy haciendo. Es simplemente una prueba de concepto, por lo que no esta ni mucho menos terminado, y el código no esta totalmente ordenado, pero os puede ser interesante para que veáis como funcionan algunas cosas con XNA.

El juego es una versión moderna del antiguo Gozilla, que venia incluido con el QBASIC en MSDOS y algunas versiones tempranas de Windows.

Aquí os dejo algunas capturas para que lo veáis antes de descargarlo, para que veáis si os puede interesar.


Ver en grande...




Ver en grande...

La descargar del codigo fuente con todos los objetos necesarios para ejecutarlo se encuentra en este enlace: DESCARGAR FUENTES


Para comenzar el juego hay que pulsar Intro o botón derecho del ratón. Después, para parar la flecha de selección del ángulo de disparo hay que usar el espacio, y finalmente, para detener la barra de potencia hay que usar la tecla "f". Como ya he dicho antes, es simplemente una prueba de concepto, así que no os desilusionéis si el mono de la derecha os parece invencible.
Lo dicho, para el viernes de la semana que viene estaré de nuevo de vuelta, y preparado para meter algún contenido que tengo pensado. Entre otras cosas, tengo la intención de escribir un articulo para explicar el código por defecto de una plantilla de "XNA game", escribir también acerca del control del tiempo de ejecución del bucle, y también empezare a escribir algunos fragmentos de código sencillos que tengo pensados, para empezar a entrar en materia.

Un saludo a todos.

jueves, octubre 19, 2006

¿que ocurre cuando se ejecuta un juego?

Es importante conocer, sobre todo si se viene de programar aplicaciones que no sean juegos, de que forma se ejecutan las instrucciones dentro de un juego. Hay que hacer incapie en la idea de que, en una aplicacion corriente, por norma general, la aplicacion trabaja segun las ordenes que le envia el usuario en forma de distintos eventos, tales como pulsar un boton, elegir una opcion de menu, o marcar una casilla de comprobacion (checkbox). Mientras el usuario no realice ninguna accion, el programa suele estar completamente ocioso, limitandose a esperar que ocurra algo.



En un juego, el programa tambien va a esperar que ocurran determinados eventos, como por ejemplo pulsar un boton para que una nave dispare, o pinchar con el raton en un punto para que el personaje del juego se mueva a ese punto, pero al contrario que ocurre con una aplicacion normal, en un juego, el flujo del programa se esta ejecutando continuamente, realizando lo que se denomina un bucle de juego (loop). Este bucle, como se muestra en la figura, se encarga de que cada segundo, la pantalla se refresque con nuevo contenido, tanto si ha cambiado como si no. La peculiaridad de un juego, es que, el programa responde a los eventos modificando la forma en la que se ejecuta ese bucle, es decir, podemos dejar a un juego sin ningun evento que estara ejecutando el bucle indefinidamente, pero cuando detecte que el usuario ha realizado alguna accion, evaluara esa accion, y si procede cambiara de que forma se ejecuta ese bucle, haciendo que en ese momento el resultado de esa accion tenga su presentacion en pantalla.



He creado un grafico, copiado de una version en ingles, en el cual se puede ver que acciones realiza un juego normalmente en un bucle de juego. Obviamente hay juegos que no se limitan a un bucle tan sencillo, pero en este caso utilizaremos este diagrama para no complicar demasiado este asunto.






En post posteriores, veremos como se refleja este diagrama en la plantilla que vimos en el post anterior, es decir, a la hora de crear un nuevo juego usando la plantilla "XNA game".

martes, octubre 17, 2006

Tren con destino XNA... próxima parada.... C#

Resulta emocionante instalar todo el software descrito en el post anterior y que de repente te aparezca en los programas instalados un tal "XNA Game Studio Express". Con ese nombre, parece como si al abrirlo, nos fuese a aparecer una herramienta súper intuitiva que nos permitiese construir nuestros propios juegos casi sin despeinarnos. Nada mas lejos de la realidad...., una vez que abrimos XNA GSE, nos damos cuenta que es exactamente igual al Visual Studio C# 2005 Express Edition, salvo por algunos cambios que veremos luego. Es decir, lo que nos encontramos es un entorno de desarrollo para el lenguaje C#, pero con algunas sutiles diferencias. Básicamente, la principal diferencia de este IDE, por lo menos ahora que esta en versión beta, son las plantillas para la creación de nuevos proyectos. Dentro de estas plantillas vamos a encontrar dos que están relacionadas con el desarrollo de juegos.












Para simplificar, empezaremos hablando de la primera plantilla "Windows Game(XNA)". Esta plantilla, si la seleccionamos, nos creara un nuevo proyecto de desarrollo en el cual vamos a disponer de una parte del código ya escrito. Este código, no es ni más ni menos que los cimientos sobre los que levantar un juego basado en XNA. De hecho, y para empezar a investigar, podemos crear un nuevo proyecto usando esta plantilla, y pulsar F5, para ejecutar el proyecto, y veremos el resultado en pantalla del código escrito. Básicamente nos encontraremos una pantalla de juego, vacía....





Es labor nuestra ahora, el ser creativos, y ver de que podemos llenar esa pantalla... En los próximos post de este blog veremos como ir incluyendo algunos elementos dentro de nuestro proyecto de juego, y como modificar su comportamiento, y a la vez iremos aprendiendo el lenguaje de programación que nos va a permitir ir levantando nuestro proyecto, sobre estos cimientos que acabamos de colocar. Recordaros que este lenguaje es C#, con el cual estoy haciendo yo también mis primeros pinitos, por lo que no os asustéis que esta dentro de mucho no vamos a empezar con código realmente complejo.

Es requisito indispensable, si realmente queremos hacer algo con XNA el conocer C#, o por lo menos, como es mi caso, conocer el mínimo de C# como para poder pegarme un poco con XNA. En mi caso, y espero que a vosotros os ayude también, empecé leyéndome una guía muy completa de un tal José Antonio González, y la cual podéis encontrar en esta página: GUIA, cada uno puede aprender C# como le venga mejor, pero en mi caso, lo que he ido haciendo, para que se haga mas ameno, es ir intentando leer código de proyectos XNA de las distintas paginas que hay en Internet, sobre todo las de la pagina de XNArecursos, que tiene código con comentarios en español, y a la vez, usando la guía de C# de José Antonio de referencia. Esta guía de C#, en mi opinión, es demasiado teórica, y los ejemplos, suelen ser complejos de comprender, por lo menos para mi, por lo que os recomiendo que si os parece muy compleja empecéis por los miles de recursos que existen acerca de C# en internet, como por ejemplo este otro curso.

En resumen, si no conoces nada acerca de programar, es mejor empezar adquiriendo un poco de conocimiento sobre como hacerlo, ya que si no este blog te podrá parecer demasiado complicado. Si ya conoces algo de programación, en otro lenguaje que no sea C#, o que no sea un lenguaje de programación orientado a objetos, como lo es C#, utiliza las guias que he comentado anteriormente para poder seguir el blog. Y si finalmente, ya conoces C# y dominas la programación orientada a objetos, estarás en disposición de empezar a aprender XNA, aunque tal vez en ese caso, este blog te parezca demasiado sencillo.

lunes, octubre 16, 2006

Bien... ¿por donde empezamos?

Ahora que tenemos claro que queremos empezar a trastear con XNA y Game Studio Express... ¿por donde empezamos?

En primer lugar necesitamos preparar nuestra maquina para que sea una plataforma de desarrollo. Estos son los elementos que vamos a necesitar, y donde podemos descargarlos.

  • Microsoft XNA Game Studio Express (Beta) que lo podemos descargar aquí DESCARGAR
  • Visual c# 2005 Express Edition que lo podemos descargar aquí DESCARGAR
  • DirectX Software Octubre 2006 Development kit que lo podemos descargar aquí DESCARGAR

Es recomendable que a la hora de instalar el Visual c# 2005 Express Edition le marquemos que queremos descargar e instalar también la librería MSDN. Esto nos lo preguntara durante el proceso de instalación. Esta librería es una guía de referencia del lenguaje c#, del .NET framework y del propio entorno de desarrollo Visual Studio Express.

Por otro lado, ya que vamos a desarrollar juegos, deberíamos incluir algún software de edición de imágenes a nuestra colección de software, ya que nos será muy útil mas adelante. Por ejemplo podemos instalar "The Gimp" que es de código fuente abierto, libre, y lo podemos descargar de forma gratuita desde aquí DESCARGAR

Con todo esto ya tendríamos nuestra máquina preparada para empezar a desarrollar.

Preguntas mas frecuentes

Antes de entrar en materia, es importante resolver algunas de las dudas mas frecuentes que se puede hacer una persona que se haya encontrado con el termino XNA mientras buscaba información acerca de programación de juegos.

¿Que es XNA y para que sirve?

- Originalmente, XNA se definió como una filosofía de desarrollo para la creación de juegos.
- Esta filosofía, que adelanto para decir que ha sido creada por Microsoft, estaba definida por varios puntos. En primer lugar, permitiría desarrollar juegos de una forma mas sencilla a lo que los desarrolladores estaban acostumbrados, tanto para los desarrolladores profesionales como los que no lo son.
- Por otro lado, se decia que gracias a XNA, se podrían desarrollar juegos multiplataforma, para PC y Xbox, realizando muy pocos cambios. (Nota: cuando apareció XNA se pensaba que cuando se hacia referencia a Xbox se trataba de la primera versión de la consola. A día de hoy XNA, de momento, esta orientado a la consola Xbox 360).
- Prácticamente desde Agosto del 2006, esta filosofía de desarrollo se ha plasmado en algo tangible, que es con lo que vamos a trabajar en este blog. Microsoft liberó en esas fechas el XNA framework y Game Studio Express (versión beta de momento).
- Gracias a estos dos elementos, vamos a disponer de unas herramientas totalmente orientadas al desarrollo de juegos. Con estas herramientas, vamos a poder realizar ese juego que siempre habíamos soñado hacer, pero que nunca habíamos sabido como atajarlo.

- Analicemos en detalle cada uno de los dos componentes:

- XNA Framework:

- Un framework, para los iniciados, no es ni más ni menos que una biblioteca de código. Gracias a esta biblioteca, podemos sentarnos delante de un entorno de desarrollo, y ordenar al ordenador que realice instrucciones avanzadas de una forma sencilla. Es decir, una biblioteca de codigo(Framework), lo que contiene es, una colección de trozos de código, desarrollado por programadores expertos, y documentación acerca de como utilizar esos trozos de código de una forma simple.
- Por poner un ejemplo, imaginar que estamos desarrollando un juego, como por ejemplo el clásico pong, en el cual una pelota viaja de un extremo a otro de la pantalla chocando con las "raquetas" de los jugadores (entrecomillo raquetas ya que en la versión mas básica del pong, son simples rectángulos puestos en vertical). Bien, si no utilizásemos un framework como el de XNA, tendríamos que desarrollar una gran cantidad de código, con complejos cálculos matemáticos, para determinar en que momento la pelota, en su viaje para alguno de los lados, choca contra una de las raquetas. Pues bien, por ejemplo, con el XNA framework, vamos a disponer de esos cálculos ya realizados, y simplemente tendremos que acceder a ellos utilizando unas pocas instrucciones de código, las necesarias para invocar el trozo de código de la biblioteca que gestiona la colisión entre, en este caso concreto, un objeto de forma esférica con un objeto de forma rectangular.

- Game Studio Express (Beta):

- Uno de los productos con más éxito de Microsoft ha sido Visual Studio. Este producto no es ni mas ni menos que lo que se denomina un IDE (Integrated Development Enviroment, vamos, entorno de desarrollo integrado para los amigos). Un IDE es un producto desde el cual vamos a desarrollar las aplicaciones o juegos, para luego compilarlas y publicarlas. (Nota: ¿compilarlas?.....: Compilar es el proceso de convertir el código que hemos escrito en un lenguaje en concreto a código entendible por la maquina). Pues bien, en el caso que nos ocupa, Microsoft lo que ha hecho es crear una versión de Visual Studio, basada en la ultima versión de este producto, pero preparada para el desarrollo de videojuegos utilizando XNA, y eso no es todo, lo mejor es que este IDE es totalmente !!!GRATIS!!!. Gracias a este IDE, cualquier persona, va a poder, por lo menos, intentar crear sus propios juegos, y tal y como hemos explicado antes, este IDE tiene acceso directo al framework de XNA, por lo que vamos a poder desarrollar nuestros juegos usando la biblioteca de código de XNA (recuerda que framework = biblioteca de código) sin mucho esfuerzo.


Espero que con este post hayáis podido comprender que es y para que sirve esta tecnología. En post posteriores explicare como preparar nuestra maquina para empezar a trabajar con GSE (Game Studio Express) y XNA, he intentare explicar otras preguntas frecuentes....

El primer contacto....

Este blog nace con el ánimo de compartir mis experiencias y conocimientos acerca de XNA con todas las personas que estén interesadas en interesarse en este mundillo.

El autor de este blog, no es ningún programador experto, ni es un entendido en el mundo del C#, DirectX y XNA, de hecho, se podría decir que es un iniciado.

Mi experiencia como programador es bastante básica, habiendo tocado un poco de muchos lenguajes, pero nunca profundizando en ninguno de ellos. De hecho, actualmente es cuando estoy dando el salto de mis conocimientos en programación estructurada a programación orientada a objetos.

Por estas razones, el contenido de este blog estará orientada a las personas que, como yo, se encuentran en el punto cero, es decir, les sobran ganas de aprender, disponen de las herramientas necesarias para empezar, pero se encuentran perdidos entre tanta y tanta información que hay dispersa por la red.

En definitiva, si eres totalmente novato, tienes problemas para pegarte con la documentación en ingles y no sabes por donde empezar, este es tu sitio...