jueves, 29 de diciembre de 2011

Game Development Tools

music: SUICIDE COMMANDO -"We Are The Sinners"-


Bueno, pues ya he leído suficientes capítulos de este libro (he leído once de los veintidós capítulos que consta) y gastado suficiente tiempo como para decir: quien sea capaz de sacarle partido o utilidad, mejor para él. Yo me planto. Tengo mejores cosas que mirar.

Cuando lo compré esperaba encontrarme con un libro en que explicaran la estructura y composición de las principales herramientas middleware (lo que aquí llamarían Digital Content Creation tools -DCC tools-) necesarias para dar soporte al motor y crear el videojuego, así como código explicativo de referencia para poder crearlas. Es decir, lo que parece que explican en este otro libro de aquí . Lo que me he encontrado no es exactamente eso, algo tiene de ello... pero poco.

En sí mismo es una colección de artículos escrita por varios autores, entre los cuales reconozco algún que otro nombre como, por ejemplo, D. Eberly. Está dividido en tres partes:

-En la primera se dan una serie de explicaciones generales relacionadas principalmente con el mundo del game asset pipeline, asset tracking y asset pipeline systems. En general, todo muy teórico y retórico. De esta parte lo más potable sea el último capítulo 7, que propone la construcción de un sencillo pero robusto sistema de serialización.

-La segunda parte es aquella con la que el Editor pensaba comenzar el libro, antes de recibir la sugerencia de realizar la primera parte: en ella se tratarán temas como Constructive Solid Geometry (CSG), COLLADA, Terrain Decimation, Mecanismo Command débilmente Acoplado, Uso de XML en Tools, Debug para Audio, etc. La cosa promete de veras pero de nuevo, me encuentro con un conocimiento nada clarificador, al menos para mis necesidades: bastante generalista y poco elaborado en ciertos apartados, bastante denso y complicado de seguir en otros. Es aquí donde comienzas a sospechar que este libro sólo pretende ser una guía orientativa para mostrar ciertas mejoras en determinadas técnicas y características a gente que ya está acostumbrada a tratar con el que se suponía era el tema de fondo: las Game Development Tools.

-La última parte hablará, a modo de introducción, de ciertas herramientas "third-party" públicas y presentará algunas otras que han sido realizadas dentro de la propia empresa del responsable del artículo, en cada caso. Muchos de los nombres que verás en esta sección -p.Ej: Intel Task Build Blocks, YAML nVidia Parallel Insight, FBX- son, sin duda alguna para el Editor del libro, nombres que conoces perfectamente ("You doubtlessly know about many of these tools."). Yeah, whatever...

En conclusión: un libro del que, más allá de un par de ideas generales, he podido sacar bien poco, y cuyo título puede llevar a creer lo que no es. Personalmente no lo recomiendo. Parece estar orientado a gente que ya lleve mucho tiempo trabajando en una empresa grande, tenga ya mucha experiencia, y haya trabajado en proyectos grandes y de gran complejidad. Si no reunes estas condiciones lo que aquí puedan mostrarte, en la práctica, te va a venir al pairo. Y si trabajas en una empresa grande... pues ya vas a saber lo que hay, no necesitas que te lo cuenten en un libro.

Si hubiera sabido antes de la existencia del otro libro, ese es el que posiblemente me hubiera comprado. En fin... ¬¬

Hasta otra. :P

(** EDIT 26/04/2012 **) "El otro libro" -no voy a poner el link por tercera vez- llegó a casa el pasado día 16 de abril. :P

domingo, 9 de octubre de 2011

GAMEFEST 2011

music: ALICE COOPER -"Brutal Planet"- / -"Under My Wheels"-



Cuatro días, 16.000 m^2, 65.000 personas y demás cifras que ya habréis podido leer en otras fuentes es lo que ha arrojado esta segunda edición de Gamefest, la cual parece estar consolidándose como la feria de videojuegos más importante de España. Aún así, tengo como asignatura pendiente visitar Gamelab y GamerLand, pero ya veremos. ; )

Sábado y domingo fueron los días en los que más se llenó el evento. Las colas en algunos sitios eran prohibitivas (ej: probar PSVita) con lo que de algunas cosas pasé. En esta ocasión, y al contrario que el año pasado, no tenía ningún juego que probar en especial, así que me concentré en las ponencias y de vez en cuando cómo la gente jugaba a Rage, Catherine y Deux Ex: Human Revolution.

Este año vino un taller de pintado de figurillas de Warhammer 40.000, algo que siempre me ha llamado la atención por lo espectacular que resulta una partida de estas.

Una serie de sucesos encadenados hicieron que el jueves y la mañana del viernes no pudiera asistir, así que espero poder encontrar en vídeo la ponencia Qué debes saber de la propiedad intelectual si quieres crear videojuegos, una de las que más ganas tenía de ver. Respecto a Qué habilidades debe desarrollar un buen programador de videojuegos... por la cuenta que me trae, debería sabérmela ya. :P

Con Trabajar en el extranjero: ¿huida o progreso? supe algunas cosas más a tener en cuenta a la hora de plantearte ir a trabajar a EE.UU y Canadá, y En busca del personaje perfecto me dió la oportunidad de poder conversar con dos de mis excompañeros de Pendulo (¡nos vemos en la próxima!). ;P

En cualquier caso, ya se ha subido a la red la que considero que fue la mejor de entre las que pude ver:


Este año el stand de Gamelab estuvo más protegido gracias a una especie de enorme tienda de campaña transparente de plástico, y no hubo tantos problemas con el ruido producido por el resto del evento. Un gran acierto. Desgraciadamente, cuando el sábado por la tarde se puso a tocar una banda de música ni nuestra sufrida tienda nos salvó.

Por cierto, hacía tiempo que no jugaba a Sir Fred y, al verlo puesto en el stand de estos señores de aquí, la inercia se apoderó de mí y acabé haciéndole un tiento (no en vano, considero que este es uno de los mejores juegos que se han hecho en España). Aunque apolillado al comienzo, al final me piqué y conseguí llegar al final de una de las diferentes partidas que tiene este juego, que es una de las cosas que siempre me fascinaron de él. Lástima que F. Rada no estuviera por allí, me hubiera gustado preguntarle cuántos tipos de partidas diferentes se crearon en total (que recuerde, yo he logrado pasarme seis).

En resumen: más grande, y en ciertos momentos mejor. Pero tengo una cierta sensación de estancamiento. Como si aún siguiera faltando algo en la fórmula... pero dados los buenos resultados que estas dos ferias han cosechado con la fórmula actual ya no se fuera a completar, y yo fuera a seguir teniendo esa sensación de estancamiento, una vez los focos se apagan y la música termina. Esa es la impresión que tengo, pero... ¿por qué?. Bueno, seguro que en la próxima edición hago un repaso de las tres y lo averiguo. ; )

Hasta otra. :P

Enlace

sábado, 10 de septiembre de 2011

Primer vídeo

music: SPACE AGE PLAYBOYS -"The Band Gets High"-



Pues... eso. Hasta otra. :P

viernes, 9 de septiembre de 2011

El problema "curioso" (y su resolución)

music: PJ HARVEY -"This Is Love"-

Durante las últimas semanas me he dedicado a solucionar el problema "curioso" del que ya hablaba en la entrada del día 12/05/2011.

Se hacía imprescindible poder dar una solución a esto si pretendía grabar el inplay del juego; como comentaba en la entrada anterior, era imposible hacer un vídeo que se viera decentemente debido al enorme lag que se producía.

Afortunadamente ya dejé hace tiempo las pistas necesarias por donde podía ponerme a empezar a trabajar para solventar esto... y los resultados han sido muy satisfactorios. : )

Al contrario de lo que pensaba el Componente LogicaPelota no era el problema... pero sí la puerta de entrada para ir, poco a poco, "buceando" en el código hasta llegar al núcleo principal de los problemas: los métodos IBounding::GetCollision e IBounding::GetPoints.

La optimización, realizada principalmente en dichos métodos, ha llevado a aumentar el framerate del juego (estando en modo Debug) hasta los... ¡¡¡147-150 FPS!!!. Teniendo en cuenta que, en estas mismas condiciones, las medidas marcaban 46-48 FPS ( hace tiempo hice algunas pequeñas optimizaciones que lo subieron hasta ahí desde la situación de (12/05/2011) ) el empuje ha sido espectacular. : )

Resumo las modificaciones que se han realizado para llevar a cabo la hazaña:

a) Dejar de devolver por valor un std::vector. Una de las cosas que he aprendido gracias a Quimera Engine es que, cuando devuelves por valor el resultado de un método, está sucediendo lo mismo que con los parámetros de entrada por valor: se está creando UNA COPIA del contenedor que alberga el resultado. Si dicho resultado es un std::vector, se tiene que crear UNA COPIA del vector... con lo que se estaba gastando inútilmente un montón de tiempo en cada iteración de GetPoints. Solución: poner un std::vector como un parámetro de salida en GetPoints, y por referencia.

b) Por lo que he podido ver, llamar al método std::vector.clear() en cada iteración de un método puede ser algo ASESINO. Hay que evitar a toda costa llamar a std::vector.clear() en un lugar que tiene que ejecutarse en cada iteración del juego, y con la mayor rapidez posible.

c) Asimismo, realizar múltiples llamadas a std::vector.push_back(elemento) en una misma iteración de un método (en concreto, unas 94 veces), también puede llegar a ser ASESINO. Hay que evitar realizar tantas llamadas a push_back en un lugar que tiene que ejecutarse en cada iteración del juego, y con la mayor rapidez posible.

d) La mezcla de b) y c) había dado un cóctel particularmente letal para el rendimiento. La solución ha sido sencilla:

d.1) Se fija un límite máximo de tamaño para los std::vector (en concreto, para los std::vector de puntos 3D) y se declaran ya con dicho tamaño. Así, ya no es necesario emplear ni clear para vaciarlos en cada iteración, ni push_back para añadir un elemento. Pueden ser los elementos introducidos directamente como si fueran arrays.

d.2) Al tener tamaño fijo ya no es posible emplear el método std::vector.size() para conocer cuántos elementos posee el vector. Por lo que se devolverá también, como parámetro de salida, una variable unsigned int que contendrá la cantidad de elementos que poseerá el vector. Dicho valor podrá ser empleado como centinela para recorrer el vector en un bucle for.

e) Por si fuera poco, había un fallo tonto en IBounding::Draw que estaba haciendo consumir inútilmente un montón de tiempo, y que ya ha sido subsanado.

Aparte... ciertos trucos de más bajo nivel, principalmente declaración de algunos grupos de variables locales a un método como static, que han hecho ganar algunos frames más... pero el gran avance en el rendimiento del juego ha sido gracias a lo explicado anteriormente.

La solución final es terminar de plantear e implementar el sistema de detección y respuesta de colisiones de una forma distinta a la actual, pero por el momento la optimización realizada ha sido suficiente. Al fín se ha podido grabar un vídeo. Ya veremos si continuo optimizando más o me voy a hacer otras cosas más urgentes (como introducir audio, por ejemplo).

Hasta otra. :P

sábado, 20 de agosto de 2011

Reduciendo iteraciones

music: AEROSMITH -"Beyond Beautiful"- / -"She's On Fire"-

Partamos de la siguiente base: el bloque de código más rápido es aquel que nunca se ejecuta. Con esta premisa es evidente que nos interesa lograr, de una forma u otra, reducir la cantidad de veces que ciertos bloques de código se ejecutan para mejorar el rendimiento, si ello es posible. Independientemente de lo mejor o peor implementados (y optimizados) que estén dichos bloques.

Una de las razones por las que no he podido llegar a mucho en términos de optimización es porque concebía la optimización exclusivamente como la "aceleración" de la ejecución de ciertas partes de código particularmente costosas computacionalmente, concentrándome demasiado en este enfoque. No se trata de olvidar esto ni mucho menos, sino de anteponer una política de reducción de iteraciones en ciertos puntos clave. Y en el caso de Super Pong, estos puntos clave son los Componentes.

Existen compomentes que no necesitan ser ejecutados todo el tiempo, en cada tick (la IA o la visión de la raqueta, por ejemplo). Desgraciadamente, el engine no estaba preparado para poder establecer una política así, en la que se establezca que un determinado componente no va a ejecutar su método Update en cada iteración, sino sólamente cada vez que haya transcurrido un determinado espacio de tiempo. Y eso es a lo que me he estado dedicando durante los últimos días.

El punto clave de todo esto es la clase IComponent, de la cual heredan TODAS las demás clases componente. El siguiente resúmen explica los puntos más importantes:

AÑADIDOS en IComponent:

Atributo m_fElapsedTime_Max: El Elapsed Time Establecido para el compoente (en ms, pero expresado en segundos). Representa el intervalo de tiempo que debe transcurrir para dar permiso al componente para ejecutar su método Update_Exec. Se asignará en TODOS los componentes un valor para m_fElapsedTime_Max en el método Init del componente. Asignando 0.0f vamos a hacer hacemos que el componente ejecute Update_Exece en cada tick.

Atributo m_fElapsedTime_Acum: El Elapsed Time (en ms, pero expresado en segundos) actualmente acumulado en el componente. Cuando se alcance el Elapsed Time Establecido, se ejecutará el método Update_Exec del componente.

Método virtual Update_Exec() vacío. Este será, como tal, el antiguo método Update. Todos los componentes heredarán este método vacío, sin código; se sobreescribirá en TODOS los componentes (salvo en aquellos que no tengan elementos que actualizar), y se ejecutará cuando se haya alcanzado el Elapsed Time Establecido para el componente.

MODIFICACIONES en IComponent:

IComponent::Update deja de ser un método virtual vació para pasar a ser no virtual y albergar el bloque de código que comprueba que, si se ha alcanzado el Elapsed Time establecido para ese componente, se ordene ejecutar sú método Update_Exec.

(** TO-DO: ya me acordaré algún día de cómo se tabulaba esto... **)

void IComponent::Update(const float& fDelta)
{
m_fElapsedTime_Acum += fDelta;
if ( m_fElapsedTime_Acum >= m_fElapsedTime_Max )
{
m_fElapsedTime_Acum = 0.0f;
this->Update_Exec(fDelta);
}
}

Este método es heredado por todos los componentes, y cuando la Entidad ordena a sus componentes que se actualicen (manda hacer Update a todos sus componentes, vamos), está llamando a este Update heredado de IComponent.

Tras esto, se puede comenzar a pensar un plan de "presupuestos" (Budget es la palabra) de tiempo para cada componente... y el engine está preparado. Por el momento, tengo establecido que los componentes Vista e IANPC ejecuten el método Update_Exec cada 500 ms (sus m_fElapsedTime_Max tendrán almacenado 0.5f, al estar expresado en segundos).

Hice una prueba loca y asigné 10 segundos a los componentes Vista e IANPC, y el framerate no aumentó respecto del caso anterior. Pero hice una segunda prueba loca asignando también 10 segundos al componente LógicaPelota... ¡¡¡y el framerate aumentó en casi VEINTE FRAMES!!!. El componente LogicaPelota, por lo tanto, está suponiendo un cuello de botella importante (como ya sospechaba). Habrá que optimizarlo acelerando su ejecución... y antes de ello reduciendo el número de iteraciones. ; )

Hasta otra. :P

viernes, 5 de agosto de 2011

Orientación de Entidad y Orientación Visual

music: VENDEMMIAN -"All Is Lost (All Is Gone)"-

Ayer logré otra de las cosas que el juego estaba pidiendo como agua de mayo: hacer que la pelota ruede sobre sí misma mientras se desplaza por el escenario. : )

Para ello se me ha ocurrido un pequeño truco: añadir a las entidades los Ángulos de Orientación Visual, que se unirían a los ángulos que las entidades ya poseían (pasándose éstos últimos a llamarse Ángulos de Orientación de Entidad). Y así, las entidades poseen ahora dos tipos de orientación:
  • DE ENTIDAD: Orientación lógica, real, de la entidad en el mundo.
  • VISUAL: Aquellas entidades visibles en el juego tendrán ahora una orientación de su representación visual en el mundo, la cual NO SIEMPRE coincidirá con la Orientación de Entidad; esto dependerá, en última instancia, del tipo de entidad.
Se asignará el valor de los Ángulos de Orientación de Entidad a los de Orientación Visual al comienzo de la ejecución del método Update de las entidades; de esta forma se asegura que, por defecto, el valor de ambos tipos de ángulos será el mismo al comienzo del método Update.

Y así, dejamos que sea alguno de los componentes de la Entidad quien, en última instancia, determine el valor definitivo de los Ángulos de Orientación Visual por el mero hecho de poseer ó no la Entidad algún componente que los calcule y asigne en la Entidad.

Por otro lado se modifica la llamada al método IRenderContext::DrawMesh (dentro del método Draw del componente Visualization3D), pasando ahora como ángulos de orientación del mesh los Ángulos de Orientación Visual.

Y aquí está la clave: que la pelota (es decir, la Entidad Esfera3D) que va a tener un componente que va a calcular definitivamente los Ángulos de Orientación Visual (y establecerlos en dicha Entidad): el componente LógicaPelota, el cual ya existía (gracias a él la pelota se desplaza y ordena realizar la Collision Detection/Response de ésta)... convenientemente modificado para ir calculando en cada Update unos Ángulos de Orientación Visual que hagan el efecto de rodar sobre sí misma y cambiar el sentido de giro cuando choque contra una raqueta. Simple. :P

Todo esto está muy bién, pero... ¿y por qué no cuelgo un vídeo?. Pues porque estoy desde ayer intentando hacer uno con este programa pero, por mucho tutorial y mucha configuración, me salen con un lag que parece que estuvieran reproduciéndose con un gnomo dando pedales. Así que si logro hacer uno decente ya lo colgaré (** EDIT: ir a entrada de 10/09/2011 **).

Hasta otra. :P

lunes, 1 de agosto de 2011

Suben de nivel...

music: ARCH ENEMY -"Diva Satanica"-

¿Creíste alguna vez, Anderson_JAG, que vivirías lo suficiente como para ver algo así en Informe Semanal?.

Tiene dos años y no es la primera vez que lo veo, pero la pregunta es perpetua y eterna.

Las cosas cambian. A los hechos me remito. Aunque lo hagan a paso de tortuga con artritis...

martes, 26 de julio de 2011

Carga Multietapa del Nivel y Movimientos de Cámara

music: ALICE IN CHAINS -"Sea Of Sorrow / Bleed The Freak"-

Estos últimos dos fines de semana han sido de lo más fructíferos. : )


Carga Multietapa del Nivel:

Por una parte, he perfeccionado el sistema de carga de un nivel:

-Sacando la orden de carga fuera del Bucle de Mensajes de la Ventana (la carga comenzará ahora habiendo salido de la iteración actual del Bucle de Mensajes).

-Haciendo que los diversos elementos del nivel se carguen en varias iteraciones o etapas de carga (actualmente, cuatro) para que el programa salga de la sección Update del Bucle Principal del juego y entre en la sección Render de dicho Bucle.

-Estableciendo un nuevo estado (M_LOADING_END), el cual será alcanzado al finalizar toda la carga del nivel; el Bucle Principal detectará que se ha alcanzado dicho estado y entonces se alcanzará el estado "Modo Juego" (M_JUEGO);

CONCLUSIÓN: ya no hay que establecer un tiempo concertado de sincronización como comentada en la anterior entrada, para esperar a que el flujo de programa cargue el nivel y abandone el Bucle de Mensajes de la Ventana... sino que los elementos se sincronizarán con el flujo de programa gracias a los estados M_LOADING y M_LOADING_END.


Movimientos de Cámara:

Y por otra... esto.

Lo había intentado antes varias veces, pero esta vez me dije que del pasado fin de semana no pasaba. Y así, tras un exhaustivo análisis del código y los apuntes, al fin ha logrado comprender el modo de funcionamiento de uno de los puntos negros del engine: la cámara.

Ello me ha permitido hacer los añadidos y modificaciones oportunas al código para poder hacer que, al desplazarse la raqueta, se produzca una leve inclinación horizontal y/o vertical de la escena; más pronunciada ó menos cuanto más se aleja la raqueta de su particular posición de comienzo, al inicio de la partida.

Pongo unos screenshots para mostrar cómo queda el invento a partir de ahora.











Hasta otra. :P

miércoles, 20 de julio de 2011

Loading screen

music: GARY NUMAN -"Cars"-

A veces, la mente funciona de un modo extraño. El caso es que el sábado pasado me levanté inspirado y me vino a la cabeza una idea para solucionar una de las cosas que me parecían más feas del juego.

Es algo que sólo sucedía al comienzo de la primera partida. Cuando seleccionas Jugar, mientras se van creando los elementos del nivel (el mundo, las entidades, los componentes asociados a dichas entidades, etc) se producía una especie de disincronía que hacía que, cuando aparecía la imagen del juego, la pelota ya hubiera avanzado un buen trecho. Resultado: la raqueta controlada por el ordenador no podía alcanzarla y se producía un tanto seguro.

Para solucionarlo, he añadido al motor un nuevo estado de "carga de datos" (M_LOADING) que implica que está cargando datos y, durante este estado, el fDelta que se envía al método principal Update de la aplicación es cero. Al seleccionar la opción Jugar pasamos directamente a este estado y, una vez se hayan cargado los datos y haya pasado un determinado tiempo concertado de sincronización, se pasa automáticamente a "Modo Juego" (M_JUEGO).

Mientras se cargan los datos aparece una pantalla de carga; este es el resultado:


¿Funciona?. Pues sí, funciona. Al entrar en "Modo Juego" todo queda sincronizado y la pelota permanece en el centro de la acción, tal y como puede verse en la captura. ¡¡¡Al fin!!!. :D

Pues nada, veamos si la Musa de la Inspiración me visita en otras ocasiones, ahora que llevaba tiempo dándole un respiro a esto. Si hasta estoy pensando en borrar lo que puse el día 1 y todo. XD

Hasta otra. :P

viernes, 8 de julio de 2011

De Cuaterniones y Cuaterniones Duales

music: MOONSPELL -"The Butterfly FX"-

Aquí podemos ver las bondades de Cuaterniones y Cuaterniones Duales en técnicas avanzadas como Skinning.

Hasta otra. :P

viernes, 1 de julio de 2011

Punto y aparte

music: COPTIC RAIN playing "Painted Bird [DB Mix]"-

No, no ha abandonado aún este mundo, ni me habían secuestrado. Desde mediados de enero he entrado a formar parte del Grupo de Programación Kinesis, y me he centrado de lleno en el proyecto que se está llevando a cabo, Quimera Engine.

Con lo cual Super Pong ha quedado parado de nuevo. Insisto en que no es un punto y final, sino un punto y aparte. Al estar inmiscuído en Quimera estoy necesariamente dándome de bruces con conceptos que había tratado de abordar en el pasado, sin demasiado éxito alno tener una motivación que superar. Por ejemplo, Cuaterniones y su utilización como un método alternativo a matrices y para efectuar rotaciones en 3D. Cosas que ahora comienzo, al fin, a comprender. Eso sí, los Cuaterniones han traído consigo a los Cuaterniones Duales y ha sido como "¡Venga, va...!". XD

Aunque no haga mucho ruido... por aquí sigo.

Hasta otra. :P

domingo, 9 de enero de 2011

Un poco de todo

music: THE GONE JACKALS -"Legacy"- / -"Trapped"-

Sin novedad en el frente, o mejor... con multitud de pequeñas novedades. Tras el prototipo de dos pantallas he hecho un poco de aquí y otro de allá, centrándome en aumentar el rendimiento general del juego. Al fin puede decirse que funciona a un rendimiento aceptable tanto en Modo Ventana como en Fullscreen.

Por cierto, Las opciones de Menú ya pueden ser seleccionadas, por cierto, a través del ratón. En un futuro incorporaré la posibilidad de manejar el juego a través de un pad (como se indica en el Documento de Especificación de Requisitos). ; )

A la "espera" de estudiar más la sintáctica de UML y ver alguna herramienta de modelado como Rational Rose o eDraw UML Diagram he decidido aparcar por un tiempo el tema de Patrones de Diseño, algo con lo que me interesa mucho ponerme, y centrarme en algo que también es crucial: la incorporación al engine de un Grafo de Escena.

También he estado estudiando algunos capítulos del libro Video Game Optimization pero aún es pronto para hacer una valoración. He varios libros que me llaman la atención pero, por el momento, vamos a aguantar la tentación...

Hasta otra. :P