domingo, 31 de mayo de 2015

Las bases de MotoGT 2: overview

Hace un tiempo les comenté que pienso reescribir MotoGT desde cero. Llamo a este proyecto MotoGT 2. Aunque por fuera, al principio será un clon del primer MotoGT con apenas unos pocos detalles nuevos visibles, por dentro la historia es totalmente diferente. El objetivo es obtener una base de código más sólida con la cual experimentar fácilmente agregando y quitando cosas al juego, para que lo que limite su desarrollo sea mi creatividad y mi poca idea de game-design, y no un código complicado y difícil de modificar o mantener.

Entonces, tengo que escribir un pequeño motor de juego. Lo suficientemente general como para que me provea esa flexibilidad. No tan general como para renegar con cosas que no necesito. Con una interfaz de alto nivel para no lidiar con detalles finos de implementación al concetrarme en la lógica del juego. Con capas o subsistemas desacoplados para que, por ejemplo, la biblioteca que use para gráficos y sonido no me condicione la forma de programar esa lógica del juego. Y más, como para que un imprevisto no tire abajo el proyecto.

Diseñar un motor de juegos es una tarea harto compleja, pero también terriblemente divertida y desafiante. No siempre tiene sentido reinventar la rueda. Pero recuerden que lo hago porque me divierte, y para aprender en el proceso. Entonces no me asusta ni preocupa mucho esto de la millonésima rueda. Es más, lo estoy tomando como un ejercicio. Por un lado hay desafíos muy interesante de ingeniería de software. Por otro, es una pista de prueba para nuevas técnicas de programación, y una oportunidad para poner en práctica  un montón de cosas de C++11, C++14 y hasta tal vez C++17 que vengo estudiando en teoría desde hace un tiempo.

Hoy empiezo por contarles a grandes rasgos las "partes" o subsistemas de los que se compondrá esta nueva implementación. Más adelante, en otros artículos, iré develando detalles que considere interesantes y útiles sobre cada una de las partes. Por ahora hay 3 grandes partes a las que llamo Engine, Game y Bridge.

Componentes principales en la nueva arquitectura para MotoGT 2, con algunos ejemplos
de clases pertenecientes a cada uno, para dar una idea de su funcionalidad.

Bridge encapsula todo lo que sea entrada y salida. Es decir, todo lo que ofrece una biblioteca multimedia como sfml. Lo encapsulo a través de este subsistema por dos razones. La primera es que no quiero ver código sfml-dependiente en ninguna otra parte de la implementación, de forma que si el día de mañana cambio de biblioteca, o de versión de la biblioteca, solo tenga que actualizar unas pocas clases en este componente, y no pase lo que me pasó con el primer MotoGT, que me fue imposible migrarlo de sfml 1.6 a sfml 2.0. Entonces, el bridge ofrece una interfaz genérica, similar a la de sfml, pero muy simplificada (esta es la segunda razón) para esconder la gestión de recursos (carga de imágenes por ejemplo) y los detalles propios de una biblioteca particular, y proveer solamente el conjunto mínimo de funcionalidades que me permita implementar el juego. Además, podría tener con relativa facilidad varias implementaciones de este subsistema en simultáneo (misma interfaz, distintas bibliotecas multimedia por detrás) para elegir en tiempo de compilación según la plataforma.

Engine completa los servicios de Bridge con algunas abstracciones generales para el desarrollo de videojuegos. Introduce conceptos genéricos como "escena" y "objeto de juego". Agrega algunas funcionalidades como acceso a archivos de configuración con un formato genérico, una clase para cargar archivos con traducciones a distintos idiomas y consultarlas fácilmente, y algunas herencias algo más particulares como una estructura general para definir una escena que represente un menú, una clase particular que aplica una animación de transición para cambiar de una escena a otra, o algunos objetos de juego comunes con comportamientos básicos o programables (por ejemplo un fondo de pantalla, o un texto genérico). Se encarga además de la inicialización (y luego de destrucción) de todos los subsistemas, y esconde en el 99% de los casos las interacciones entre los sistemas Bridge y Game. Impone una forma de trabajo basada en escenas anidadas, que contienen objetos de juego (algo así como las ventanas y los controles en un sistema de widgets clásico). Y es además el que gestiona el loop principal, procesando y distribuyendo los eventos, encargándose encarga de otros "detalles" generales, como por ejemplo controlar el frame-rate, y algunas tareas de mantenimiento.

Finalmente, Game hace uso de los servicios y clases que proveen Engine y Bridge para implementar efectivamente este juego. Es decir, en este componente solo me preocupo por la lógica de mi juego de motos, y sus detalles de presentación propios, pero todo lo demás aquí casi no debe interferir. Por ejemplo, agregar el logo del juego al menú inicio consiste en agregar en la escena un objeto de juego basado en un sprite genérico, diciéndole cual archivo de imagen utilizar, y definiendo en una linea un comportamiento básico muy simple (la pequeña deformación que hace constantemente). Al escribir esto, no debe preocuparme (ni verse en ese código), por ejemplo, cómo se gestiona o muestra un sprite en la pantalla, o cómo la escena gestiona sus objetos de juego. El código debe ser simple y directo haciendo referencia casi exclusivamente a lo que quiero mostrar y cómo, en un "lenguaje" de alto nivel.

Pero este sistema Game tiene en realidad dos patas. Por un lado la lógica de presentación (cómo se ven los menúes, cómo se dibuja la pista, cómo se configuran las opciones del juego, etc), y por otro la lógica propia del juego (las reglas del modo carrera, las etapas de una carrera en particular, la física, los datos de un jugador, etc). A la primer parte la llamo GUI, a la segunda Logic.


El video muestra el menú principal en el MotoGT original y en la nueva versión.

Si bien planteo una arquitectura de juegos bastante genérica, en realidad voy implementando las funcionalidades bajo demanda, y muy enfocado en MotoGT 2 como objetivo. Eso hace que finalmente la arquitectura solo esté completa para este tipo de juegos en particular; no será un súper motor genérico, ni pretenderá serlo. Sobre esta base voy avanzando, buscando por ahora simplemente reproducir lo que ya tenía en el MotoGT original. Por ahora los tres componentes se van desarrollando casi a la par. En algún momento, Bridge y Engine estarán suficientemente completos como para que sólo me quede utilizarlos para terminar y posteriormente mejorar el Game (Gui y Logic). El video prueba que ya hay unas cuantas cosas resueltas (o al menos prototipos funcionando), y que ese momento está cada vez más cerca. Pero el proceso será muy largo, y habrá que esperar varios meses hasta ver finalmente el resultado completo. Mientras, veré de ir subiendo de a poco los avances al git, e ir comentando en este blog más detalles interesantes de la arquitectura, su ingeniería de software y lo que voy aprendiendo con su implementación.

No hay comentarios:

Publicar un comentario