Bajo un océano de bits, mes VI

Por Javier Albizu, 11 Julio, 2018
Llegó el momento de lanzarse a la piscina; ergo he empezado a tirar líneas de código.
No es que me sienta especialmente preparado para ello, pero tengo la impresión de que leyendo sin más no voy a lograr llegar mucho más adelante. Cosas mías.

Tras continuar con mis búsquedas, por fin logré dar con los materiales completos del curso de Antonio García Alba1 detrás de los que andaba el mes pasado. El enlace a ellos que aparecía en la primera versión de la wiki que encontré (OSL2) estaba mal, pero no fue muy complicado adivinar cuál podía ser la url correcta.
Aun así, y mientras buscaba más información acerca del autor, a través del repositorio de proyectos de fin de carrera de su universidad llegué hasta otra wiki con ese mismo material. Vista la fuente, a partir de ese momento asumí que esa era la web oficial y con la que continué.

Pero la cosa no ha terminado muy bien. Una vez finiquitada la parte más descriptiva del libro, las últimas ciento y pico páginas están dedicadas a un caso práctico, lo cual está muy bien, pero...

Esta sección está centrada en explicar el proceso de creación de un juego de principio a fin, empezando con la parte más organizativa y terminando con el código completo. La primera parte, a pesar de ser un tanto pesada era comprensible, la teoría es lo que tiene, lo aguanta todo, pero una vez superada esta llegó el batacazo.

Porque, a pesar de tener el código, de ser capaz de compilarlo, y de lograr que me desaparecieran algunas advertencias debidas a métodos obsoletos de C++ que usaba (no olvidemos que el curso es de 2008), no he sido capaz de “leer” el flujo interno de cada una de las clases y objetos que creó. Y esto es un problema.

En la sección ligada a código del juego el foco está centrado en tratar de explicar algunas de las decisiones de diseño que se han tomado... y son decisiones que no acabo de entender. No se trata de que yo las habría afrontado de otra manera (que sin duda será lo que haré), sino que la forma de abstracción que ha elegido es demasiado… abstracta para mi.
Al final la programación consiste en eso, en realizar abstracciones. En buscar otra manera de observar el mundo real y modelar un caso de forma que lo entendáis tanto tú como el ordenador. Y la manera en la que ha planteado el juego el autor se me hace muy difícil de seguir. No se trata únicamente de que los mecanismos lógicos por los que ha optado me resulten demasiado abstractos, sino que tampoco soy capaz de aterrizar lo que explica a través de la lectura del propio código.

Me falta una base muy seria en cuanto a la programación estructurada en general, y la programación orientada a objetos en particular (por no hablar de C++).
Comencé mirando cada comando, cada “include” y cada función en la web2, pero esto no me hacía entender el conjunto. Demasiados detalles entrelazados, demasiadas cosas por probar y entender sin tener claro el contexto para el que han sido creadas.

Así pues, toca ignorar el vértigo, empezar a ensuciarse las manos y equivocarse mucho. Toca hacer, que es la única manera que sé de entender.

Pensando sobre el asunto me ha dado por darle vuelta a la expresión de "tirar" líneas. No sé si la primera persona que lo utilizó quería decir "arrojarlas" sobre la pantalla o la CPU o, como lo hago yo, tirar línea tras línea a la papelera de lo inservible.
Vaya, como en Daegon.
En fin.

Porque sí, apenas he empezado y con cada revisión se va mucho más de lo que había en origen.

Claro está, es muy probable que mi nivel de madurez programática tenga mucho que ver en esto. Que estoy muy verde.
Al menos eso espero.
Me queda año y medio para descubrirlo.

Haciendo caso a una de las alternativas que me planteé en los comienzos de este reto, no voy a lanzarme directamente a por "mi" juego, sino que empezaré rehaciendo desde cero uno que ya existe. A escribirlo utilizando mis propias palabras.

Con esto espero no desviarme de la parte pura del aprendizaje con temas de diseño. Conociéndome como me conozco, sé que me iba a empezar a centrar en los asuntos que me resultan más interesantes o cómodos y lo otro iba a quedar en un segundo plano. Ya habrá tiempo para eso. Por ahora empezaremos analizando otro. Teniendo un objetivo concreto que reflejar.

Así pues, voy a empezar con un clon del Traz de Commodore 64, que no deja de ser una evolución del Arkanoid, que no deja de ser una evolución del Breakout de Wozniak (que no Jobs) para Atari, que no deja de ser una evolución del Pong, que no deja de ser una copia del Tennis de la Magnavox, que no deja de ser una evolución del Tennis for two “programado” en un osciloscopio en el 583.
Y hago esto porque es un juego que siempre me gustó, porque parece una idea en apariencia sencilla y asequible, porque tiene un editor de niveles y porque... viejunismo.

En paralelo a todo esto también he comenzado a buscar a las personas que desarrollaron el juego original para pedir su beneplácito a la hora de usar las ideas, gráficos y música que ellos crearon, pero la cosa está complicada.

Por lo pronto he empezado a extraer los gráficos a partir de un emulador de Commodore. Esto no se limita a los "Tiles" en sí mismos, sino también a cada uno de los frames de animación. Un coñazo, pero algo mucho más sencillo y rápido que hacerlos yo desde cero.
Tengo pendiente mandar correos a la gente que anda metida en el desarrollo retro para indagar acerca de la posibilidad de extraer esta información desde el binario de la versión original, pero no tengo muchas esperanzas sobre ello. Lo máximo que he logrado por el momento ha sido sacar un volcado en ensamblador y hexadecimal del archivo "prg" gracias a Droid D644, pero tener eso es lo mismo que no tener nada.

Ya que estábamos, también he buscado varias implementaciones de Arkanoid hechas con SDL5 para ver si saco ideas pero, como de costumbre, queda mucho para que sea capaz de entender la totalidad de su código.

Pero dejémonos de dar vueltas, y vayamos a lo concreto. Despiecemos esta cosa y vayamos por partes.

Objetivo inicial:
Crear cuatro versiones del juego cada una de ellas con una biblioteca de funciones distinta.
Estas bibliotecas serán:

SDL 1.2
SDL 2
SFML
OpenGL (… ya veremos)

Como no quiero limitarme a aprender a utilizar una API, me lanzo a lo bruto. La parte puramente programática será común, pero la forma en la que se pintan los gráficos, se utiliza la música o se interactúa con los los dispositivos de entrada me las tendré que currar con cada una de ellas.

A grandes rasgos, el programa completo tendrá tres bucles:

1. Menú de selección.
2. Editor de niveles.
3. Juego.

Ahora mismo me estoy centrando en el primero de esos bucles el cual, a su vez, está dividido en otros tres bucles menores.

Casos de uso del Menú.

- Primer bucle

Al arrancar el programa suceden dos cosas:
Se muestra una pantalla con el menú.
Suena una canción.

Gráficamente el menú consiste en tres bloques:
El fondo.
Centrado en la parte superior tenemos el logo, que tiene una animación propia (39 pasos distintos de animación, de los cuales no sé cuántos frames debería ocupar cada uno).
Centrado en una franja marrón de la parte inferior tenemos una rotación de textos (Programador, grafista, músicos, y TOP 10 jugadores)

- Segundo bucle

Cuando se pulsa cualquier tecla la parte inferior da inicio el segundo bucle, y cambian dos cosas:
El ciclo de textos se detiene para ser sustituido por el menú de selección.
Se puede interactuar con él.

En esta interacción se puede seleccionar entre las 3 opciones que da el menú:

- Un jugador
- Dos jugadores
- Editor de niveles.

- Tercer bucle

Una vez que se ha terminado una partida se vuelve al menú.
El programa te dice que has logrado una puntuación digna de estar dentro del Top 10.
Se muestra un cursor para que el jugador introduzca su nombre. Este cursor aparece en la franja inferior, pero el color de esta franja no es marrón sino azul.
Se actualiza el Top 10 que se muestra en el primer bucle con ese valor.
Se vuelve al primer bucle.

Y hasta aquí puedo leer por el momento. Una vez que consiga hacer funcionar esto, "lo fácil", me pondré con el siguiente bucle (aunque sí que tengo una idea general de lo que me espera).

De todo lo descrito por ahí arriba, por ahora sólo he conseguido pintar un rectángulo sobre la pantalla y que esta muestre una imagen estática que tengo almacenada en el disco duro.
A partir de ahí comienzan los problemas.

Porque quiero que ese rectángulo sea escalable.
Con SDL 1.26 he conseguido que el rectángulo pueda cambiar de tamaño, pero no que la imagen que se encuentre en interior le acompañe.
Aparte de esto, esta librería me ha dado un problema adicional: Si no controlas los tiempos de refresco de pantalla se zampa todos los recursos de la máquina.
He conseguido paliar esto parcialmente, pero aún me queda para terminar de entender todo el proceso.

Por si con esto no fuese sucifiente, a la hora de cambiar el tamaño de la ventana también me hace cosas raras. En fin, es una biblioteca de funciones algo vieja, pero se han hecho cosas interesantes con ella, así que yo no seré menos.
Por más que me requiera más trabajo también me resulta la más interesante. Por un lado es en la que está basado el curso que he leído y, por otro, al ser más básica también “ofusca” menos cosas que las demás. Tengo que entender mejor lo que estoy haciendo, así que me quedo con ella.

Con SDL 27 he conseguido que, si defino un multiplicador al tamaño inicial del rectángulo, este también se aplique a la imagen, pero si cambio el tamaño de la ventana de manera manual tengo el mismo resultado que con la versión previa (pero sin los raros).
Espero que cuando termine de entender el segundo enlace pueda finiquitar esta parte.

Con SFML8 he conseguido que haga ambas cosas y es con la que menos código he usado. Por un lado, guay, por otro, casi todo es totalmente opaco.
Quizás cuando entienda todo lo que sucede por detrás me decante por ella, a fin de cuentas es la biblioteca más nueva, sobre la que más documentación y libros he encontrado, y la que parece más sencilla de usar.

Con OpenGL9, tras mucho buscar, también he conseguido que lo haga todo... aunque ha sido "tuneando" uno de los ejemplos de Lazyfoo. Un ejemplo bastante complejo cuya lógica aún no he terminado de pillar que, de premio, me da un warning al compilar.
Funciona, sí, pero tengo que entender el por qué y arreglar ese mensaje de alerta.
Antes de llegar hasta este punto había conseguido crear el rectángulo escalable con facilidad, pero no hubo manera de lograr que pintase nada en su interior.

OpenGL es con mucho la más clásica de las bibliotecas que he usado, pero su filosofía no tiene nada que ver con la de las otras tres. Tira mucho de otras bibliotecas externas para otras funcionas auxiliares como la carga de gráficos (de las que probé con libpng, SOIL y STB) y todo se vuelve un poco cristo.
Al final la cosa ha quedado funcionando con DevIL, la que se usaba en el ejemplo, pero tengo la impresión de que esto va a ser lo que más me retrase, así que no sé si llegaré a mandarla a paseo antes de terminar.

Y creo que ya vale por hoy. Como broche final diré que la Fundeu acepta el vocablo “Renderizar”10, lo que me ha dejado un poco loquer, pero bueno, hay que modernizarse. Igual cualquier día de esto aparece en uno de esos diccionarios de la RAE que están regalando.

Enlaces:

1. Repositorios de la Universidad de Cadiz
- Proyectos de fin de carrera
- Wikis – UCA: Ejemplo de la creación de un videojuego
- Wikis – UCA: Ejercicios del curso
- OSL2 – UCA: Ejemplo de la creación de un videojuego
- OSL2 – UCA: Ejercicios del curso

2. La C con el ++
- la STL - Standard Template Library
- Programación en C++ - Objetos y Clases
- Repaso a la stl, introduccion a los contenedores
- C++ Punteros y referencias
- C++ Map
- Headers and Includes: Why and How

3. Tennis for Two y sus clones
- El original
- La Magna disputa
- La causa de la disputa
- La historia del Breakout de Atari
- Y las secuelas

4. Droid D64, tú y yo tenemos que conocernos mejor

5. Clones de Arkanoid con SDL
- Breakout
- SDL-ball
- Breaker 10
- Breaker 10 dentro de una suite de emuladores para la consola Coolbaby RS-97
- Breaker 10 dentro de una suite de emuladores para Dingux (consola Dingoo) ya no disponible

6. SDL
- Resizable Windows
- SDL - Regulating Frame Rate
- Resizable Windows and Window Events

7. SDL2
- Draw an Image with SDL2
- SDL Scale Surface

8. SFML
- SFML and Linux
- SFML - Sprites and textures

9. OpenGL
- Libpng
- STB
- DevIL - A full featured cross-platform Image Library
- SOIL - Simple OpenGL Image Library
- OpenGL Window Reshaping
- OpenGL - Lesson 06 Texturing Update
- OpenGL - Loading a Texture

10. Renderizar en Fundéu

El contenido de este campo se mantiene privado y no se mostrará públicamente.

Plain text

  • No se permiten etiquetas HTML.
  • Las direcciones de correos electrónicos y páginas web se convierten en enlaces automáticamente.
  • Saltos automáticos de líneas y de párrafos.