December 2009 - Artículos

Buenas noticias! Se ha abierto la convocatoria a uno de los concursos de desarrollo de videojuegos más jugosos: DreamBuildPlay 2010! Y este año, además de poder ganar hasta 100.000$ (que no está nada mal), existe como premio adicional el hecho de que el juego será promocionado y vendido en XBOX Live! Eso significa... más dinerillo y bastante fama... recordemos que XBOX Live es un mercado mundial, con millones de usuarios y clientes potenciales.
Las bases son sencillas... tener al menos 16 años, vivir en uno de los países que pueden participar, y por supuesto... desarrollar un videojuego basado en la tecnología XNA Game Studio 3.1, todos los detalles e información puedes encontrarla en la web oficial del concurso:
http://www.dreambuildplay.com/main/default.aspx
En la primera parte vimos como una máquina de estados finitos permitía a una entidad de nuestro juego realizar una serie de acciones teniendo en cuenta algunos factores internos (hambre, cansancio, ganas de usar el WC...). Ahora veremos una versión mejorada de esta FSM, en la que entra en juego otra entidad, y la interacción entre ambas. Ahora pasaremos a tener la entidad Madre, además de la entidad Boy. Para que ambas FSM interactúen utilizaremos un sistema de mensajería entre objetos. Así pues la madre y el hijo se comunicarán entre sí (como tiene que ocurrir en toda buena familia).
En la primera versión, el niño, iba al WC cuando tenía ganas... ahora, además de eso, cuando haya terminado su estado de WCIng... volverá a la tarea a la que estaba anteriormente, fuera cual fuera (en la versión simplemente se ponía a jugar). Además, cuando tenga hambre, enviará un mensaje a su madre para que le haga la comida y seguirá con su tarea. Cuando la madre reciba el mensaje, dejará su tarea y se pondrá a hacer la comida, y cuando termine de hacerla, enviará un mensaje de notificación a su hijo, que ya podrá comer.
Esto podría implementarse sin sistema de mensajería... ignorando la estructura de la FSM y demás... pero es algo poco recomendable. Esta implementación (o cualquier mejora de ella), proporciona un código muy extensible, que encapsula muy bien la funcionalidad de la FSM, mejorando además su mantenibilidad. Observemos algunos cambios en el diseño de la entidad base:

Vemos que cada clase hija deberá implementar el método HandleMessage. Este lo que hará será llamar al método de manejo del mensaje en su instancia local de StateMachine. Esta, a su vez, enviará el mensaje al estado en el que se encuentre la clase. Hecho esto el mensaje será manejado... o no... esto dependerá de si el estado en el que se encuentra la entidad debe responder a ese mensaje.
Los mensajes son gestionados con la clase MessageDispatcher, que lo que permite es enviar estructuras del tipo Telegram entre entidades. Estos mensajes contienen el emisor/receptor, y el propio mensaje en sí mismo. Además contienen la propiedad "delay", por si queremos que la entidad receptora no reciba dicho mensaje hasta transcurrida X cantidad de milisegundos. Esto puede ser interesante por ejemplo en un juego en el que debemos esperar cierto tiempo (en un juego tipo command & conquer entrenaríamos un soldado y esperaríamos x tiempo a que finalizase el entrenamiento, transcurrido dicho tiempo, la unidad pasaría a estar disponible, notificándolo al sistema mediante uno de estos mensajes).

Otro cambio interesante es la clase StateMachine. Mediante la propiedad PreviousState puede volver a su estado anterior, si es necesario, tras finalizar la acción en su estado actual. Así, en el ejmplo que he implementado, el niño, tras usar el WC, volverá a la tarea en la que estaba anteriormente, fuera cual fuera. Esto dota de mayor dinamismo a la acción del juego.

Dejo el código disponible para quien quiera jugar con él o hacer propuestas de mejora!
Las Finite State Machines son uno de los medios mas utilizados desde tiempos inmemoriales en el desarrollo de videojuegos, con especial interés en la parte de Inteligencia Artificial. Si hablamos de IA en el más puro sentido académico... una FSM, por su propia definición, no sería inteligencia... pero sí permite simular inteligencia. En los videojuegos, al fin y al cabo, lo que queremos es que el jugador crea que está jugando contra una inteligencia, y que este, al vencer, sienta que es más inteligente que la máquina. Sí... siento decepcionarte pero así es, has vivido engañado, hasta hoy :-)
Las FSM permiten definir una serie de estados y que cada uno de ellos de un comportamiento distinto a las entidades que los contienen. Por ejemplo... un mísil tierra-aire podría tener los estados: Detenido (en el avión), Volando hacia vector fijo (una vez disparado), Persiguiendo objetivo (cuando está lo suficientemente cerca del avión enemigo).
El diseño de una FSM puede hacerse de muchas formas distintas... existen pseudo-patrones para ello (de hecho, no se les llaman patrones normalmente, lo hago yo porqué según he visto muchas personas y empresas los utilizan de forma común). Así pues, tendremos que intentar utilizar un diseño sólido (que puede basarse en uno de estos patrones) para no terminar teniendo un montón de código spagetti, y sobretodo, separar la lógica de cada entidad de la lógica de cada uno de los estados.
En el ejemplo de hoy utilizaré una idea muy básica y sencilla, que representa las acciones que hace un niño durante el día (y perdonad que sea tan simplista... es sólo un ejemplo!): Jugar, Comer, Dormir e ir al WC. Esto lo traduzco en los siguientes estados:

El diagrama es muuuuy sencillo, pero bueno, ya nos va bien para hacer unas pruebas, ¿no? Nuestra FSM se implementará del siguiente modo:

Por un lado tenemos la clase genérica State, de la cual heredan todos los estados. Esta clase asegura que todos los estados implementen el método Enter (cuando se entra en el estado), Execute (durante cada ciclo de ejecución), y Exit (cuando se sale del estado). Entonces cada clasae "hija" implementará esos métodos.
La clase Boy básicamente es una entidad, que es la que da sentido a la FSM, o cuyos estados son representados por la FSM. Los métodos interesantes son Update y ChangeState:
public override void Update(GameTime gameTime)
{
if(_currentState != null)
_currentState.Execute(this);
}
public void ChangeState(State<Boy> newState)
{
// Salimos del estado actual
if (_currentState != null)
_currentState.Exit(this); // El estado actual pasa a ser el nuevo estado
_currentState = newState;
// Entramos en el estado
_currentState.Enter(this);
}
Así de fácil! Ahora vamos a ver la implementación del estado Playing:
public class Playing : State<Boy>
{
if(entidad.IsWCNeed())
entidad.ChangeState(new WCing());
if(entidad.IsFatigued())
entidad.ChangeState(new Sleeping());
if (entidad.IsHungry())
entidad.ChangeState(new Eating());
}
entidad.Action = @"Playing>Execute> Jugando!";
public override void Execute(Boy entidad)
{
entidad.Fatigue += 1;
entidad.Hungry += 1;
entidad.WCNeed += 1; public override void Exit(Boy entidad)
{
entidad.Action = @"Playing>Exit> Apagando la consola...";
}
public override void Enter(Boy entidad)
{
entidad.Action = @"Playing>Enter> Enciendo la consola...";
}
}
Los string que se guardan en "Action" son los que utilizaremos para debugar. En realidad no serían necesarios en un juego real... pero dado que en este ejemplo no tenemos una interfaz gráfica nos irá perfecto para ver los estados por los que pasa la entidad Boy.
Este es el resultado de la ejecución:

La idea de cómo diseñar la FSM la he obtenido de este libro: Programming Game AI by Example, una verdadera joya que me recomendó un amigo y su vez os recomiendo :-)
En esta web encontraréis cientos de cursos gratuitos de alto nivel sobre todos los temas... desde informática a física pasando por arquitectura, química o idiomas... El contenido de los cursos no tiene precio, y no lo digo sólo porque sean gratis... el nivel que tienen es universitario (y a veces incluso más). Encontraremos completísimos apuntes, vídeos, ejercicios resueltos e incluso exámenes. Algunos de los cursos incluso están traducidos a varios idiomas distintos al inglés, entre ellos el castellano.
La web acepta donativos... y sin duda esta es una buena causa.

El caso es que esta web hace años que existe... pero no la he descubierto hasta hoy... os la dejo para quienes, como yo, no la conocíais.
http://ocw.mit.edu/OcwWeb/web/courses/courses/index.htm o simplemente: http://ocw.mit.edu
Hoy os explico qué necesitamos para desplegar nuestros juegos hechos con XNA sobre XBOX 360, e incluso ponerlos a la venta en XBOX Live Indie Games y sacarnos algún dinerillo con ello. Todo ello sin sacar un euro de nuestros bolsillos. Antes que nada, advertir que no puede subir juegos a XBOX Live Indie Games máquinas desde cualquier país. Por algún motivo que desconozco (pero que seguro que tiene algún sentido), sólo pueden subirse juegos desde alguno de los países siguientes:
- Australia, Canada, Denmark, France, Ireland, Italy, Netherlands, New Zealand, Norway, Singapore, Spain, Sweden, Germany, Japan, United Kingdom, y United States
- Conexión a Internet -aunque si lees esto será que ya tienes :-P
- XBOX 360
- Ordenador conectado a la misma red que la XBOX, con Visual Studio (en cualquiera de sus ediciones), XNA Game Studio, y por supuesto una aplicación hecha en XNA
- Cuenta de Creators Club (es de pago, pero veremos como obtener una cuenta gratuita)
Lo primero será iniciar la XBOX y conectarnos a XBOX Live, es un proceso totalmente gratuito y bastante trivial, así que no entraré en detalle en este asunto. Sólo recuerda que cuando te registres el sistema te ofrece la opción de conectarte pagando (suscripción GOLD) o sin pagar (suscripción SILVER). La diferencia entre ambas cuentas es que con la suscripción GOLD puedes jugar online, conectarte a Facebook o twitter desde la XBOX... pero a nosotros, para publicar juegos de XNA a XBOX, nos vale con una cuenta SILVER. Tras el registro tendremos nuestro nuevo GAMERTAG.
Una vez hecho esto... necesitamos registrarnos a la web de XNA Creators, para ello accede y regístrate a la siguiente web: http://creators.xna.com, este registro es gratuito. Una vez registrados veremos que tenemos la opción de tener una suscripción GOLD del creators club (que no es lo mismo que la suscripción GOLD de XBOX Live). El caso es que no necesitamos una suscripción gold para que funcione... con una Trial nos vale (tiene menos funcionalidades, pero nos vale para lo que queremos hacer). ¿Cómo hacemos para obtener una cuenta Trial? Si eres estudiante de FP o Universidad estás de suerte, porqué de forma fácil puedes obtener una, en www.dreamspark.com, o mediante el DotNetClub de tu centro de estudios (www.dotnetclubs.com), en mi caso, el de la UOC, del que soy coordinador: http://uoc.dotnetclubs.com. Si en tu centro de estudios no existe ningún dotnetclub... crea tu uno!
Vale, tenemos la suscripción a XBOX Live Silver, la cuenta de Creators y la suscripción XNA Creators Club (Trial). Ahora necesitamos asociar la suscripción Trial con nuestro gamertag. Para ello accede de nuevo a http://creators.xna.com, haz login y ve a "editar perfil", allí, en la parte inferior de la pantalla, tienes una opción que pone "Asociar con mi gamertag". Y con esto hemos acabado por lo que a registros se refiere...
Ahora ve a XBOX Live, y conéctate al Bazer de Juegos, ve a la pantalla que los muestra todos por orden alfabético, y ve a la X (de XNA claro), y allí podrás descargar e instalar en tu XBOX el XNA Game Studio Device Center. Una vez descargado... ejecútalo. Te dará una especie de número de serie.
Ahora ve a tu PC y abre cualquier proyecto que tengas en XNA (que no utilice librerías COM ni cosas raras, porqué en cuyo caso en XBOX no va a funcionar), pulsa con el botón derecho sobre el nombre del proyecto y marca la opción: "Create a Copy of project for XBOX 360". Esto lo que hará es crear una copia idéntica del proyecto en la solución, pero que será ejecutable en XBOX 360. Establece el proyecto como proyecto de inicio, pulsando con el botón derecho sobre el, como antes, y marcando la opción "Set as startup project" (o "Establecer como proyecto de inicio", yo lo tengo en inglés...). Ahora, en el menú de Visual studio, ve a VIEW->TOOLBARS, y asegúrate que está activada la toolbar: "XNA Game Studio Device Management".


Para conectar a la XBOX, pulsa el botón cuyo icono es una cruz verde (Add a new device), y selecciona la XBOX 360. Entonces se te pedirá una descripción para el dispositivo y un "número de serie", es el número que aparece en la XBOX, introdúcelo y acepta. Con esto ya habrás habilitado la ejecución de aplicaciones XNA sobre XBOX 360. Selecciona en el desplegable de la misma toolbar el dispositivo que acabas de crear y compila.

Tu juego debería estar ejecutándose en XBOX 360 :-) Recuerda que si tu juego es lo suficientemente bueno... podrías llegar a colgarlo en XBOX Live Indie Games. Puedes echar un vistazo a los Indie Games existentes en el Bazar de Juegos de XBOX Live, e incluso bajarte demos gratis para probarlos.
Cuidado porque si XNA y XBOX ya son adictivos por separado... imagíante juntos! :-)
¿Cómo ofrecer la funcionalidad al usuario de realizar capturas de pantalla de nuestros juegos en XNA? Como siempre XNA nos facilita este tipo de cosas tan elementales, de forma que podamos reservar nuestra cabeza para dedicarla a cosas más complejas... En esta ocasión las clases ResolveTexture2D y PresentationParameters hará todo el trabajo por nosotros.
El código es muy sencillo... y es el que os presento a continuación
PresentationParameters parametros = GraphicsDevice.PresentationParameters;
ResolveTexture2D datosTextura = new ResolveTexture2D(GraphicsDevice, parametros.BackBufferWidth, parametros.BackBufferHeight, 1, GraphicsDevice.DisplayMode.Format);
GraphicsDevice.ResolveBackBuffer(datosTextura);
datosTextura.Save(@"c:\captura.png", ImageFileFormat.Png);
Tenemos una instancia de PresentationParameters, que a su vez utilizamos para inicializar una instancia del objeto ResolveTexture2D. A continuación llamamos al método ResolveBackBuffer del GraphicsDevice, que lo que hace es precisamente pasar los datos del backbuffer a nuestra instancia de ResolveTexture2D. Finalmente esta misma instancia tiene el método Save, que nos permitirá guardar nuestra captura como archivo en multitud de formatos gráficos: Bmp, Dds, Dib, Hdr, Jpg, Pfm, Png, Ppm, Tga (incluso diría que demasiados formatos, la mitad ni siquiera los conozco...).
La clase PresentationParameters es muy interesante -podríamos haber utilizado dicha propiedad directamente en el GraphicsDevice con los mismos resultados-, tiene información del BackBuffer, podemos saber si estamos utilizando antialias (multisampling) así como establecer su calidad, si estamos en pantalla completa, etc.
Os dejo con mi captura de pantalla y el proyecto de Visual Studio completo para el que quiera probarlo.
