Monthly Archives: August 2011

You are browsing the site archives by month.

Interrail 2011

Este mes de Septiembre por fin voy a poder hacer algo que llevaba bastante tiempo deseando: el día 13 me voy 2 semanas de Interrail. Para aquellos que no lo sepan, el Interrail es un billete que te da acceso para usar la mayoría de trenes de Europa, ya sean regionales, entre paises, etc. con unas pocas restricciones. Por supuesto en segunda clase, pero, ¿qué le importa esto a un viajero auténtico?

En total serán 2 semanas en las que está previsto visitar hasta 8 paises y un buen puñado de ciudades. No es un viaje cuyo objetivo sea ver ciudades de Europa lo mejor posible; para esto haría falta varios días y una cantidad pequeña de ciudades seleccionadas. Pero así no se aprovecha bien un Interrail. Empezando por Montpellier, los destinos previstos son: Marseille, Lyon, Geneve, Bern, Zurich, Innsbruck, Graz, Maribor, Budapest, Krakovia, Varsovia, Gdansk (Danzig), Szczecin, Berlin, Leipzig, Frankfurt, Bruselas, Paris, Bordeaux y Bayonne; pasando por 8 países distintos: Francia, Suiza, Austria, Slovenia, Hungría, Polonia, Alemania y Bélgica. Además haré un pequeño recorrido por varios pueblos de Austria, para llegar a Maribor, que incluyen: Zell am See, Worgl y Bischofshofen.

europa

Además está destinado a ser un recorrido esencialmente cultural: visitar en la medida de lo posible la mayor cantidad de sitios emblemáticos y monumentos de cada una de las ciudades. Ver y sentir la vieja Europa en tus propias carnes en sitios clave como Auschwitz, Danzig o el muro de Berlín, sin contar una cantidad inmensa de patrimonio en forma de Iglesias, Catedrales, múseos de arte, monumentos, plazas, etc. Europa, la vieja europa que vió nacer a la mayor cantidad de genios en todas las disciplinas que se puedan imaginar: filosofía, música, pintura, arquitectura… La misma Europa en decadencia de nuestros días, a la espera de poder encontrar algún rastro de su ya olvidada gloria.

Haré un balance del viaje, si logro regresar vivo…

Diario de desarrollo (motor en 2D): memoria y game loops

Aun a pesar de que no dispongo todo el tiempo del que gustaría para actualizar este “diario”, he seguido trabajando de modo continuado en el motor gráfico. Estas 2 primeras semanas me he dedicado más que nada a repasar y leer nuevos materiales. También he dedicado bastante tiempo a ver código fuente de otros motores, algunos clásicos como los de Quake o Half Life, para ir cogiendo ideas de cómo desarrollar las cosas. También añadir que en los libros que tengo suelen meter código fuente de motores reales a modo de aprendizaje, lo que sin duda se agradece bastante.

La primera tanda de código base ya está disponible en el GitHub del proyecto: https://github.com/AlbertoFEM/SeventhEngine; podéis echarle un vistazo.

boost::shared_ptr<>

Una de las primeras decisiones que he tomado para programar esto es el tiempo de recursos que voy a usar. Normalmente, en los motores gráficos más potentes, se usa un Resource Manager custom, es decir, personalizado al máximo para el motor en sí. Y no sólo es importante el tipo de memoria que manejamos, sino la estructura y la forma en la que la gestionamos. Son muchas las técnicas que se usan, pero la más común es el Stack-Based Allocator, el cuál por ahora no voy a usar, pero que seguro en el futuro habrá que, o bien implementarlo, o usar alguno ya hecho. Si queréis más información sobre esta técnica, podéis visitar los siguientes enlaces:

No obstante, puesto que memoria dinámica hay que usar sin más remedio, he decidido recurrir a las útil y potente librería Boost++. Puesto que el auto_ptr se quedaba corto (más información: http://www.aristeia.com/BookErrata/auto_ptr-update.html), voy a usar las class templates que dispone Boost para gestionar la memoria dinámica, a saber: shared_ptr, weak_ptr, scoped_ptr, scoped_array y shared_array. Usarlas es bastante fácil:

Además hay que tener cuidado a la hora de incluirlas como miembros de una clase, puesto que inicializarlos puede ser peligroso (sobre todo cuando no podemos, por razones de diseño, hacerlo en el constructor). Simplemente usando el método reset(), lo hacemos de forma segura:

Game Loop & FPS

Otro de los temas que más tiempo me han ocupado ha sido el de los FPS. Un juego por definición es un bucle infinito en el que pasan cosas. Obviamente necesitamos una manera de limitar el número de veces que calculamos lo que va a pasar y cómo se lo mostramos al usuario. Esto se hace desarrollando una clase que controle los tiempos dentro del motor internamente. Hay bastantes métodos para solucionar este problema: Velocidad del juego dependiente de FPSs variables, Velocidad del juego constante con un máximo de FPSs, etc. En este artículo explican bastante bien las diferentes maneras: http://www.koonsolo.com/news/dewitters-gameloop/.

Por ahora voy a usar la implementación de Velocidad del juego constante con un máximo de FPS, puesto que es la que más facilidad dá para desarrollar el resto de módulos del engine. No obstante, he decidido extraer la clase CClock, usando un Singleton, ya que es interesante que en el futuro se pueda implementar diferentes tipos de game loop, incluso dejar al cliente decidir que técnica quiere usar y con qué parámetros quiere usarla. Aquí una vez más se nos plantea el problema de la división de responsabilidad, el qué dejar como algo interno del motor, o cómo trabajo para el programador de videojuegos. enlaces en cuanto a diseño del game loop:

El game loop por ahora tiene la siguiente pinta:

Como podéis observar es bastante sencilla. Aún no he incluido el tema de los eventos ni los respectivos update() y render(). Sin embargo podéis ver como está integrado el reloj interno, definido en la clase include/Engine/CClock.h. Puesto que está en una clase aparte y única, en el futuro será fácil implementar distintos tipos de relojes, y también distintos relojes independientes entre sí. No obstante, puesto que las distintas implementaciones del game loop depende de cómo manejes el reloj, habría que implementar distintos game loops y cargar uno u otro según nos convenga; el método RunGame() está separado del resto por esto mismo: no sería difícil cargar un game loop u otro dependiendo de una variable de configuración.

La semana que viene hablaré sobre 2 sistemas bases que ya he empezado a programar: CDisplayCore y CGameplayCore, y dentro de ellos el CStateManager y el CTextureManager.

Diario de desarrollo (motor en 2D): fundamentos y primeros esquemas

Siguiendo la primera entrada de esta serie del diario de desarrollo, hoy voy a explicar muy por encima los fundamentos del motor gráfico. Lo más importante a tener en cuenta a la hora de diseñar un sistema de este estilo, es que apenas hay nada escrito. Quiero decir, no está estandarizado, no hay un manual para hacer motores, y solamente encontraremos por Internet ejemplos de desarrollo de otras personas, con sus diseños particulares y sus formas de resolver problemas. No obstante, en algunas cosas puntuales podemos recurrir a las soluciones ya implementadas y cuya eficacia ya ha sido probada: iremos hablando de estos campos específicos a medida que vayan surgiendo.

Así pues, en esta entrada voy a exponer la primera fase del sistema base que estoy diseñando. He usado un programada llamado NClass (http://nclass.sourceforge.net/), que es un diseñador UML; sin embargo lo he usado solamente para esquematizar mis ideas, porque siempre viene bien tenerlas anotadas y relacionadas en un formato de este estilo:

 umlschema
(pulsa para ampliar)

En primer lugar tenemos las dependencias. Por ahora voy a usar las 4 librerías que véis en el esquema:

  • SDL y SDL_image: esta librería, montada sobre OpenGL, libre y multi-plataforma, es la que me servirá como capa de bajo nivel, no sólo para mostrar elementos por pantalla, sino para muchas otras cosas como manejo de eventos (teclado, ratón, input en general); buffers 2D, etc.
  • TinyXML++: esta librería es un excelente parser de documentos XML, puesto que voy a usar este formato estándar para almacenar datos de los juegos (como los mapas) para evitar la recompilación del programa si queremos cambiar la lógica del juego en sí (esto se usará más tarde para implementar algunos sistemas usando DDDAS: http://en.wikipedia.org/wiki/Dynamic_data_driven_application_system)
  • STL: una de las cosas cruciales de cualquier motor es qué contenedores usamos. Normalmente se programan a las necesidades de cada sistema, pero voy a intentar evitarlo en la medida de lo posible. No obstante, no descarto realizar desde cero algún contenedor si realmente veo que lo necesito por temas de rendimiento (y no, no se debe derivar el STL)

En el futuro iré añadiendo más librerías (como SDL_mixer), pero eso será cuando realmente vayan a ser necesarias en el desarrollo.

Subsistemas base

Como véis, el motor se localizará en una clase principal llamada Engine. Esta clase deberá ser derivada (para definir tareas de inicialización por parte del cliente). La clase principal, al igual que en el diseño más típico de motores gráficos, se encarga de initializar a su vez todos los subsistemas. El orden en que inicializamos los subsistemas es importante, puesto que tenemos que tener en mente que sistemas dependen de otro, por lo que es común usar el patrón de diseño Singleton, para evitar problemas con instancias de la misma clase y el orden en que se construyen. Sin embargo, el método más fácil y sencillo es simplemente iniciar los sistemas a mano, llamando a sus respectivos métodos de inicialización (normalmente init()) y dejar los constructures vacíos. Así nos ahorramos quebraderos de cabeza, sobretodo cuando metemos en la ecuación la memoria dinámica y los resource manager, cosa de la que ya hablaremos más adelante. Un ejemplo de esta implementación podría ser la siguiente:

Pero todavía no vamos a meternos en materia. Vamos a ver el resto de sistemas por encima para hacernos una idea de lo que tenemos que implementar:

  • DisplayCore: este es el sistema que controlará todo lo que se muestra por pantalla. Todo lo que te puedes imaginar que implícitamente se refiere a lo que se puede ver irá aquí (texture manager, layers, mapas, etc.).
  • SoundCore: como su propio nombre indicar, todo aquello que controlará los sonidos de los futuros juegos, tanto de música contínua como efectos de sonido, etc.
  • InputCore: un juego no es un juego sino permites cierta interactividad al jugador, lo que lo diferencia de las películas por ejemplo. Este subsistema controlará todo lo relacionado con las acciones que jugador el usuario para interactuar con nuestro juego.
  • AssetManager: además, necesitamos tener bien ordenados y accesibles todos los recursos de un juego. Por ello, una clase que controle y categorize todos los contenidos es esencial y necesario. Además, usando clases genéricas, podemos dar mucha flexibilidad a los clientes del motor.
  • GameplayCore: este subsistema incluirá todo lo relacionado con la forma en la que jugamos a un juego, es decir, todo aquello que ocurre en el juego (transiciones de un mapa a otro, eventos especiales, reproducción de vídeos, etc.), vamos, lo que da vida al juego. Aquí también suelen ir lenguajes de scripting, para permitir dinamizarlo de manera que separemos el código del juego en sí con la jugabilidad (cosa que hacen muchos juegos en día, como el famoso World of Warcraft).
  • EntityCore: si bien no estoy seguro de incluir el tema de las entidades como una subclase directa de Engine, si que es muy importante diseñar de manera sólida nuestro sistema de entidades, puesto que es de las cosas que está en continua relación con el resto de subsistemas del juego, y necesita una comunicación continua.

Además, hay que hacer notar una cosa muy importante a la hora de diseñar todo esto. Puesto que esto es el diseño de un motor gráfico, no de un juego, hay que definir bien la línea de separación entre lo general y lo específico, es decir, no adentrarse en la tarea que el programador de videojuegos debe realizar, sino que dar una herramienta sólida que ayude al cliente a construir su producto final. Más adelante iré poniendo ejemplos concretos de esta separación, a medida que me vaya encontrando con ellos.

Para finalizar esta primera parte me gustaría decir que esto es solamente un diseño preliminar, que si bien usa elementos extraídos de todas las lecturas que he ido haciendo, introduce cosas nuevas producto de la mente del creador. El tiempo dirá si son válidas o no, pero creo que si bien es importante fijarse en lo que otros programadores han hecho previamente, igualmente importante es intentar resolver los problemas por uno mismo.

En la próxima entrada hablaré de lo que llevará cada subsistema, así como los progresos que llevo hechos en la clase principal Engine. Si tenéis alguna observación o algún enlace útil podéis ponerlo en los comentarios!