Desde mi experiencia de 10 años ya haciendo "ingeniería inversa" a muchos juegos de RPG de SNES, tengo una ligera idea de cómo se programaban los juegos antes, y me lo llegó a confirmar un interesantísimo artículo que leí hace unos meses en el que el jefe de programación de Secret of Evermore hablaba de cómo se hizo el juego.
Más o menos, se hacía así:
* En primer lugar, nada de lo que se iba a programar se "compraba" a otros: ni físicas, ni "drivers" ni nada por el estilo, al menos entre los 4 juegos que yo he visto de Squaresoft, que a pesar de ser de la misma compañía, lo hacían grupos diferentes. En los 6 juegos (Chrono Trigger, Secret of Mana, Seiken Densetsu 3, Romancing Saga 3, Final Fantasy 6 y Treasure Hunter G), solo he visto en común ¡¡1 RUTINA!! y ni tan siquiera está programada igual (es decir, se nota claramente que la hicieron dos personas diferentes a pesar de ser el mismo algoritmo). Esta rutina era una rutina de descompresión de datos genérica.
* El director encargaba a cada grupo que se encargara de una cosa: unos diseñaban los mapas y los gráficos para ellos, otros diseñan personajes y mostruos y otros componen la música. Todo esto se hace en ordenadores dedicados a ello y no tiene relación en primer lugar con el código programado.
* Cada grupo de programación se encarga de programar en código los efectos gráficos (magias, efectos de lluvia, deformaciones y demás), los menús, las rutinas de audio (lo que vendrían a ser "drivers" de audio que se encargan de cargar en la APU las muestras de audio), las rutinas de video (acceso a VRAM, OAM para sprites y ceran el "driver" de video que se ejecuta en cada NMI, es decir, en cada VBlank) el motor de animación y el motor principal del juego.
* Cuando los gráficos se van definiendo en cuanto a tamaño de sprites, tamaño de mapas y demás, los grupos de programación van adaptando el código generado a las necesidades concretas: parámetros a pasar para las animaciones, parámetros para seleccionar músicas o sonidos, formato del script de texto y de comandos...
* Lo que programan los programadores son funciones grandotas que van llamando a otras menores para realizar el trabajo específico. Por ejemplo en el FF6 y en el RS3, el banco $C3 tenía TODO el código para los menús: cuando se pulsa el botón START, el motor principal del juego llama a la rutina $C3/0000 que toma el control para dibujar los menús, elegir las opciones que selecciona el usuario, cambiar items, equiparlos, etc. Estas rutinas modifican las variables globales que contienen el equipamiento, HP, MP, modificadores al estado, parámetros del personaje, etc, es decir, las mismas variables globales que se graban en SRAM para luego continuar la partida. En estos dos juegos, el banco $C5 contenía todo el driver de sonido, de modo que si hay que reproducir un sonido cuando mueves el cursor, las rutinas de $C3 llaman a la correspondiente en $C5 que hace que suene el sonido de aceptar, cancelar o simplemente el "cling" típico al mover el cursor.
* En cuanto al motor del juego, es un bucle infinito que va ejecutando 4 ó 5 rutinas una detrás de otra para que el juego funcione:
1) comprueba la posición del personaje y la acción que haya ejecutado (hablar con alguien, mirar un cofre, luchar)
2) comprueba si la acción va acompañada de un sonido
3) actualiza el sprite del personaje en la posición que corresponda de la animación
4) actualiza el estado de la inteligencia artificial (posición de enemigos, acciones que ocurren en paralelo, etc)
El motor del juego, en el caso del
Secret of Evermore y según las palabras de su propio programador jefe, iba ejecutando los comandos que se metían en un lenguaje de scripting que ellos mismos habían creado para el juego. Este lenguaje, convertido en pseudo-código sería:
- <SET_GRAPHIC_MAP 0x54>
- <SET_TILEMAP 0x31>
- <SET_NPC 0x10>
- <SET_EVENT 0x07>
BUCLE
- <UPDATE_GRAPHICS>
- <UPDATE_NPC>
- <UPDATE_CHARACTERS>
- <CHECK_INPUT>
- <BRA BUCLE>
- <IF SALIDA_IZDA = 1 GOTO XXXXX>
-
cosas a hacer si el personaje sale por la izquierda del escenarioXXXXXX
- <IF SALIDA_DCHA = 1 GOTO YYYYY>
-
cosas a hacer si el personaje sale por la derecha del escenario............
Por ejemplo, para una de las escenas del juego donde el personaje se mueve por la ciénaga, se cargan en VRAM los gráficos (bloque 0x54, que podría estar comprimido en ROM), carga la configuración del tilemap que contiene la posición en pantalla donde van esos gráficos (bloque 0x31 con la información de la posición de cada tile en pantalla), carga la lista de enemigos presentes o de otros personajes no manejados por el jugador (lista de enemigos 0x10) y crea la lista de eventos, es decir, las posiciones donde el jugador interactúa (lugares donde hay escondidos materiales para la alquimia, salidas del escenario, personajes con los que se habla) y luego entra en un bucle en el que se actualizan los gráficos del escenario (animación de la hierba, árboles...), los gráficos de los enemigos, los del personaje principal y el perro y por último, comprueba la pulsación del mando para saber dónde mover el personaje en el siguiente frame, o si se ha salido del escenario, etc...
Todo este script de comandos se codifica luego en hexadecimal y tiene esta forma:
- 0x00 0x54
- 0x01 0x31
- 0x02 0x10
- 0x03 0x07
- 0x10
- 0x11
- 0x12
- 0x13
- 0x80 0xFB
Así, las rutinas del motor lo que hacen es ir leyendo este código hexadecimal de esta forma:
* Leo un 0x00, así que sé que es la rutina que vuelca a VRAM los gráficos
* Al ejecutar esa rutina, necesito un parámetro para saber qué gráficos son los que vuelco a VRAM, y ese parámetro es 0x54, por lo que accedo a un array en ROM y en su posición 0x54 obtengo el puntero donde empiezan los datos gráficos en ROM (comprimidos o no)
* Luego leo un 0x01, que es la rutina que crea el tilemap de los gráficos
* Al ejecutar esa rutina, necesito un parámetro de nuevo para saber cuál es la composión exacta del tilemap, y ese byte es 0x31, por lo que accedo a otro array diferente en ROM donde obtengo el puntero a una tabla en ROM que es el tilemap
* Y así sucesivamente hasta que se acaba el script que es cuando se ha terminado el juego.
Este tipo de script de comandos puede estar separado del script de texto con todo el diálogo, como en el caso del
Final Fantasy 6 o del
Secret of Evermore, o bien mezclado todo en el mismo script, como en el
Treasure Hunter G, o mitad y mitad, como en el RS3, que el script de comandos hace cosas genéricas y luego en cada escena le "pasa el control" al script de texto para decidir qué ocurre según los personajes que hayan en el grupo y qué cosas ha de decir cada uno.
En cuanto al lenguaje de programación, normalmente todas las rutinas se hacen en ASM, intentando que no sean muy largas para evitar complicarlas. Por ejemplo, en el
Treasure Hunter G, el motor principal parece claramente creado en C, puesto que por aquella época ya era bastante popular y además se nota en la forma de pasar los parámetros a las rutinas. Sin embargo, cada rutina que ejecuta una pequeña acción en concreto, como descomprimir los gráficos, descomprimir los tilemaps, actualizar la posición de los sprites y tal, están claramente programadas en ASM, ya que la forma de pasarle los parámetros no es la misma que la de las funciones principales.
Espero que no os haya liado demasiado y que os haya parecido interesante