Post on 09-Apr-2020
Página 2
1. Justificación y Objetivos
Con el tiempo, las tecnologías han mejorado permitiendo hacer juegos cada vez más
potentes, pero al igual, hay más gente que nos les saca todo el potencial posible a estas
tecnologías. Como ingenieros, debemos aprender todas las formas existentes para sacar
el mayor provecho posible a estas tecnologías, y esto solo se puede hacer de una
forma, conociendo las bases.
Conocer los fundamentes y la estructura de la plataforma en la que se está
desarrollando, nos permite optimizar el código de formas inimaginables en un principio, no
todas las plataformas funcionan igual, ni estructuran la información de la misma forma.
Aprender los principios de las consolas y computadoras actuales es una tarea muy
compleja para empezar, por esto, es mejor empezar por consolas más simples. En nuestro
caso empezaremos por una de las primeras consolas portátiles en salir al mercado, la
Game Boy.
El objetivo principal del proyecto no reside solamente en aprender los fundamentos de la
Game Boy. Se espera que además, se realice un juego aplicando todos los conocimientos
aprendidos, teniendo que cumplir los siguientes objetivos:
• Analizar el funcionamiento de la Game Boy. Aprender cómo funciona de forma
interna la Game Boy, para posteriormente poder analizarlo para sacar el mayor
provecho de cada uno de sus componentes.
• Aprender ensamblador. Lenguaje en el que se desarrollará el juego, además hará
falta entender el funcionamiento del ensamblador para utilizar sus funcionalidades
más importantes.
• Diseñar un juego. Crear un documento de diseño donde se especifiquen las
características de un juego nuevo para esta consola.
• Realizar un juego. Programar un juego donde se pueda ver plasmado todo el
conocimiento aprendido sobre el funcionamiento de la Game Boy.
Página 3
2. Agradecimientos
Este trabajo no se hubiera sido lo mismo sin la colaboración de una serie de personas que
me han ayudado en algunos apartados del juego, acelerando el tiempo de desarrollo.
Carlos Berenguer Carrión, un compañero mío, me ayudo con el aspecto gráfico del juego.
Mientras yo me dedicaba a la parte de la programación, el iba diseñando los diferentes
tilesets que se utilizan en el juego.
AntonioND, desarrollador del sistema de sonido del juego, además de la canción. Al tener
todo lo que necesitaba para reproducir música, y muy bien explicado, utilicé su librería
desarrollada expresamente para Game Boy.
Jeff Frohwein, reinterpreté parte de su código para realizar las transiciones en el modo
color de la Game Boy. El código me dio una buena base por donde empezar, además de
que al estar bien comentado era fácil de entender.
Figura 2.1. Uno de los tilesets diseñados por Carlos Berenguer.
Página 4
3. Dedicatoria
Este trabajo no se ha hecho de un día para otro, ha habido un largo proceso de
preparación por detrás. Han sido muchas las personas que me han ido ayudando y que
han hecho posible que hoy este aquí entregando el trabajo de fin de grado.
Durante estos cuatro años que llevo en la universidad de Alicante todos los profesores
me han ayudado en lo que hacía falta y siempre se han portado de la mejor forma
haciendo que mi paso por la universidad sea de lo más productivo posible. En especial a
mi tutor, que aun habiéndoselo puesto difícil me ha apoyado durante todo el desarrollo.
También quería dedicárselo a todos mis compañeros, con los cuales me he podido
apoyar a la hora de enfrentarme a las asignaturas más difíciles, además de haber
convertido un camino de rocas y trabajo en un paseo de rosas y diversión. Sobre todo, a
mi pareja que me ha aguantado en épocas de exámenes teniendo que escucharme como
repetía miles de veces los mismos temas una y otra vez.
Finalmente, y más importante, quiero dedicárselo a toda mi familia que me ha tenido que
soportar desde bien pequeño. Y que es gracias a ellos, a que siempre me han apoyado a
la hora de aprender nuevas cosas, que he podido llegar al punto en el que estoy ahora.
Gracias a todos. Sin vosotros esto no hubiera sido posible.
Página 5
4. Índice
5. índice de figuras……………………………………………………….....................8
6. índice de tablas……………………………………………………….....................11
7. Terminología…………………………………………………………......................12
8. Cuerpo del Documento…………………………………………………………….13
9. Game Boy…………………………………………………………………………….14
9.1. Historia………………………………………………………………………14
9.2. Versiones……………………………………………………………………16
9.3. Especificaciones técnicas…………………………………………………18
9.4. CPU: Sharp LR35902……………………………………….....................20
9.5. Mapa de memoria………………………………………………………….23
9.6. Organización de la ROM…………………………………………………..25
9.7. Sistema de vídeo…………………………………………….....................27
10. Juegos…….………………………………………………………………………...36
10.1. Tetris…..……………………………………………………………………36
10.2. Pokémon Plata……..……………………………………………………..38
11. Herramientas de Desarrollo…….………………………………………………..40
11.1. Herramientas Predecesoras………………………………....................40
11.2. Herramientas Actuales…………………………………………………...42
11.3. Herramientas Usadas…………………………………………………….44
12. Diseño del Juego……………………………………………………....................54
12.1. Galahad Escape!................................................................................54
12.2. Historia……………………………………………………………………..54
12.3. Gameplay……………………………………………………...................55
Página 6
12.4. Personajes…………………………………………...……………………56
12.5. Ataques…………………………………………………………………….57
12.6. Utilidades……………………………………………………....................58
12.7. Enemigos……………………………………………………....................58
12.8. Jefes finales…………………………………………………...................60
12.9. Diagrama del juego…………………………………………...................61
12.10. Interfaz………………………………………………………..................62
12.11. Controles……...………………………………………………………….63
13.Desarrollo……………………………………………………………………………64
13.1. Planificación……………………………………………………………….64
13.2. Iteración 1: Primeros pasos………………………………….................66
13.3. Iteración 2: Núcleo del juego…………………………………………….68
13.3.1. Input……………………………………………………………...68
13.3.2. Gráficos………………………………………………………….70
13.3.3. Scroll…………………………………………………................73
13.3.4. Físicas……………………………………………………………74
13.3.5. Sonido……………………………………………………………79
13.3.6. Tipos de objetos………………………………………………...81
13.3.7. Player…………………………………………………………….83
13.3.8. Enemigo…………………………………………………………86
13.3.9. NPCs……………………………………………………………..91
13.3.10. Props……………………………………………………………92
13.3.11. Carga de Mapas……………………………………………….93
13.3.12. Preparación Mapas…………………………………………...96
13.3.13. Ventana………………………………………………………...97
Página 7
13.3.14. Color…………………………………………………………..101
13.4. Iteración 3: Dragón……………………………………………………...102
13.4.1. Reutilización de la memoria………………………………….102
13.4.2. Funcionamiento del dragón………………………………….104
13.4.3. Cambio de objetos…………………………………………….106
13.5. Iteración 4: Preparación Sprites……………………………….108
13.5.1. Tiles Reservados……………………………………………...108
13.5.2. Pack de Tiles…………………………………………………..110
13.5.3. Transiciones……………………………………………………111
13.6. Iteración 5: Golem……………………………………………………….113
13.6.1. Nuevo personaje: Minero…………………………………….113
13.6.2. Funcionamiento del Golem…………………………………..114
13.6.3. Fin del juego…………………………………………………...118
13.7. Iteración 6: Animación inicial…………………………………………...119
13.7.1. Animación 1: Vista general del reino………………………..121
13.7.2. Animación 2: Bosque…………………………………………122
13.7.3. Animación 3: Golpe…………………………………………...123
13.7.4. Animación 4: Caída a la cueva………………………………124
13.7.5. Press Start……………………………………………………..125
13.8. Cambios………………………………………………………………….126
14. Conclusiones……………………………………………………………………..127
15. Bibliografía y referencias……………………………………………………….129
16. Anexo………………………………………………………………………………131
Página 8
5. Índice de figuras
Figura 2.1. Uno de los tilesets diseñados por Carlos Berenguer…………………………...04
Figura 9.1.1. Game & Watch………………………………………………………………………..14
Figura 9.1.2. Game Boy, Game Gear y Lynx ……………………………………………………15
Figura 9.2.1. Game Boy …………………………………………………………………………….16
Figura 9.2.2. Game Boy Pocket …………………………………………………………………...16
Figura 9.2.3. Blíster de la Game Boy Light Famitsu Skeleton ………………………………17
Figura 9.2.4. Game Boy Color …………………………………………………………………….17
Figura 9.4.1. Registros del microprocesador Zilog Z80 ……………………………………...22
Figura 9.7.1. Memoria de vídeo …………………………………………………………………...27
Figura 9.7.2. Estado del Flag de Modo en el tiempo ………………………………………….30
Figura 9.7.3. Ventana, Fondo y Sprites del juego Super Mario Land ……………………...31
Figura 9.7.4. Imágenes formadas con sprites de 8x8 o 8x16 píxeles ……………………...32
Figura 9.7.5. Proceso de obtención de los bytes de un tile …………………………………34
Figura 9.7.6. Estructura de los colores en GBC ……………………………………………….35
Figura 10.1.1. Portada del juego Tetris ……………..…………………………………………...36
Figura 10.1.2. Partida Tetris …..…………………………………………………………………...36
Figura 10.1.3. Animación victoria Tetris …..…………………………………………………….37
Figura 10.1.4. Multijugador Tetris ….…………………………………………………………….37
Figura 10.2.1. Portada del juego Pokémon Plata ……..……………………………………….38
Figura 10.2.2. Comparación de pantalla monocromática y color ……..……………………39
Figura 10.2.3. Buffer de fondo con zonas editadas …..……………………………………….39
Figura 11.1.1. Mapa del juego Fantasy World Dizzy …………………………………………..40
Figura 11.1.2. Pantalla de carga del juego Astro Marine Corps …………………………….41
Figura 11.1.3. Kit de GBC diseñado para conectarse a un N64 …………………………….41
Figura 11.2.1. Script de Construct2 ……………………………………………………………...42
Figura 11.2.2. Interfaz ZBrush …………………………………………………………………….43
Figura 11.3.1. Interfaz GBTD ………………………………………………………………………44
Página 9
Figura 11.3.2. Interfaz GBMB ……………………………………………………………………...45
Figura 11.3.3. Interfaz Sublime ……………………………………………………………………46
Figura 11.3.4. Información del juego Pokémon Plata ………………………………………...49
Figura 11.3.5. Pestaña BG map …………………………………………………………………...50
Figura 11.3.6. Pestaña Tiles ……………………………………………………………………….51
Figura 11.3.7. Pestaña OAM ……………………………………………………………………….51
Figura 11.3.8. Pestaña de Paletas ………………………………………………………………..51
Figura 11.3.9. Ventana Debugger ………………………………………………………………...52
Figura 12.4.1. Guerrero ...…………………………………………………………………………..57
Figura 12.4.2. Arquero ……...………………………………………………………………………58
Figura 12.4.3. Minero ……...………………………………………………………………………..58
Figura 12.7.1. Gigante ………………………………………………………………………………59
Figura 12.7.2. Raptor ………………………………………………………………………………..59
Figura 12.7.3. Bestia ……...………………………………………………………………………...59
Figura 12.8.1. Dragón ……………………………………………………………………………….60
Figura 12.8.2. Golem ………………………………………………………………………………..62
Figura 12.10.1. Diseño del HUD …………………………………………………………………..63
Figura 12.11.1. Controles del juego …...…………………………………………………………64
Figura 13.2.1. Cabecera de la ROM ………………………………………………………………66
Figura 13.3.1. Método de obtención de las teclas recién pulsadas ………………………..69
Figura 13.3.2. Error visual provocado por una mala actualización de los Sprites ……...70
Figura 13.3.3. Imagen formada por dos sprites de 8x16 ……………………………………..71
Figura 13.3.4. Comparativa posición Sprite con diferentes valores de Scroll …………...73
Figura 13.3.5. Metadatos de un mapa ……………………………………………………………75
Figura 13.3.6. Error que pueda dar no tratar la posición …………………………………….75
Figura 13.3.7. Conversión de posición a dirección de memoria ……………………………76
Figura 13.3.8. La superposición en ambos ejes es signo de colisión …………………….77
Figura 13.3.9. La superposición en solamente un eje no es signo de colisión ………….77
Figura 13.3.10. Componentes de la envolvente de los dos primeros canales …………..80
Página 10
Figura 13.3.11. Diferentes objetos del juego …………………………………………………...82
Figura 13.3.12. Proceso de cambio de personaje en memoria ……………………………..85
Figura 13.3.12. Tipos de rango de visión de los enemigos ………………………………….88
Figura 13.3.13. Rango de visión y ataque ………………………………………………………89
Figura 13.3.14. Estructura final de los niveles …………………………………………………94
Figura 13.3.15. Preparación de los mapas ……………………………………………………...95
Figura 13.3.16. Diferentes usos de la ventana …………………………………………………96
Figura 13.3.17. Memoria de vídeo con todos los caracteres ………………………………..98
Figura 13.3.18. Uso de los caracteres en el juego ………………………………………….....99
Figura 13.3.19. HUD del juego ………………………………………………………………….....99
Figura 13.3.20. Comparación versión monocromática y color ……………………………100
Figura 13.3.21. Cantidad de memoria reutilizada para el dragón …………………………103
Figura 13.3.22. Rango de movimiento del dragón …………………………………………...104
Figura 13.3.23. Fase de la batalla contra el dragón ………………………………………….106
Figura 13.3.24. Transición de apagado ………………………………………………………...111
Figura 13.3.25. Las 4 diferentes direcciones del Golem ……………………………………113
Figura 13.3.26. Agujero y estalagmita ………………………………………………………….115
Figura 13.3.27. Diferentes fases de la batalla contra el Golem ……………………………116
Figura 13.3.28. Pantalla de Game Over ………………………………………………………...117
Figura 13.3.29. Pequeños cambios realizados a una imagen para ahorra 400kB ……..119
Figura 13.3.30. Animación 1: Vista general de reino ………………………………………..120
Figura 13.3.31. Animación 2: El bosque ……………………………………………………….121
Figura 13.3.32. Animación 3: El golpe …………………………………………………………122
Figura 13.3.33. Animación 4: La caída …………………………………………………………123
Figura 13.3.34. Pantalla Press Start …………………………………………………………….124
Página 11
6. Índice de tablas
Tabla 9.3.1. Especificaciones técnicas ………………………………………………………….18
Tabla 9.5.1. Mapa de memoria de la Game Boy ………………………………………………..23
Tabla 9.6.1. Organización de la ROM …………………………………………………………….25
Tabla 9.7.1. Funcionalidades del Registro de Control del LCD …………………………….28
Tabla 9.7.2. Funcionalidades del Registro de Estado del LCD ……………………………..28
Tabla 9.7.3. Propiedades del Sprite ……………………………………………………………...33
Tabla 13.3.1. Funcionalidades del registro de input ($FF00) ………………………………..68
Página 12
7. Terminología
Durante el documento se van a hacer uso de varias palabras técnicas, abreviaturas y
símbolos los cuales son explicados a continuación:
● Sprite: Elemento visible por encima de la pantalla.
● Píxel: Unidad básica de imagen por pantalla.
● Tile: Combinación de 8x8 Píxeles.
● GB: Game Boy.
● GBC: Game Boy Color.
● BCD: Binary-Coded Decimal.
● E/S: Entrada y Salida.
● N64: Nintendo 64.
● NES: Nintendo Entertainment System.
● SNES: Super Nintendo.
● Flag: Señal/Aviso.
● kB: Kilobyte.
● Frame: Fotograma, ejecución del juego.
● $: Símbolo utilizado para expresar los valores en hexadecimal.
● %: Símbolo utilizado para expresar los valores en binario.
Página 13
8. Cuerpo del Documento
En la actualidad, existen muchas herramientas que facilitan el desarrollo de videojuegos,
haciendo que casi cualquier persona que se ponga delante de un ordenador, y sea capaz
de seguir un par de tutoriales, saque un juego.
Este documento no va a tratar sobre las facilidades que existen actualmente para el
desarrollo de videojuegos, más bien, espero que sirva como modelo para poder observar
la dificultad que había hace unos años para el desarrollo de juegos en Game Boy.
A modo de ejemplo, voy a desarrollar mi propio juego para Game Boy, en ensamblador,
mostrando el desarrollo del mismo en este documento. De esta forma se podrán ver las
diferentes partes que componen un juego y la forma en que tienen que hacerse las cosas.
El documento está estructurado en 5 puntos diferentes:
● Game Boy: Resumen con todos los datos importantes sobre la Game Boy,
empezando por su historia hasta datos más técnicos relacionados con el hardware
que utiliza la consola.
● Ejemplos de Juego: Breve vistazo a un par de juegos que se realizaron para Game
Boy para dar un poco de contexto sobre los títulos de la época.
● Herramientas de Desarrollo: Empezaremos viendo las herramientas que más se
están utilizando actualmente en el desarrollo de videojuegos, para seguidamente
poder contrastar con las que utilizaban los programadores de la época. A modo de
punto medio mostraré las herramientas que existen actualmente para el desarrollo
de juegos de Game Boy.
● Diseño del Juego: Bases del juego. Exposición de los diferentes puntos que
conforman el juego Galahad Escape.
● Desarrollo: Explicación del desarrollo del juego desde sus inicios más tempranos
hasta el producto final en el que se ha convertido. Mostrando cómo ha ido
evolucionando cada parte del juego y como se han solucionado los problemas que
se iban planteando durante el desarrollo.
Página 14
9. Game Boy
9.1. Historia
La Game Boy salió al mercado el año 1989 a manos de la empresa Nintendo. Estas no
eran las primeras consolas portables de Nintendo, antes de la Game Boy estaban las Game
& Watch, una línea de 59 consolas donde cada una poseía un único juego con pantalla
LCD, reloj y alarma.
Mientras que el mercado apuntaba por los juegos coloridos y las tres dimensiones,
Nintendo optó por crear una consola que sacara el mejor partido a una tecnología un poco
más desfasada. Con esta filosofía salió la primera Game Boy, la cual contaba con un
microprocesador de 8 bits a 4.19MHz, una memoria RAM de 8 KB y una pantalla con
solamente cuatro niveles de verde sin iluminación.
Dos años después de su lanzamiento, Sega sacó su consola portátil, la Game Gear. Esta
fue ideada para hacerle frente a la Game Boy con una tecnología muy superior, que le
permitía usar una amplia paleta de colores en sus juegos con una buena resolución. Al
igual que la Game Gear de Sega, existía la consola Lynx de Atari, la cual también contaba
con una pantalla a color retroiluminada.
Figura 9.1.1. Game & Watch.
Página 15
Estas dos consolas no acabaron triunfando, aunque tecnológicamente eran muy superiores
a la Game Boy, debido principalmente a la baja autonomía que tenían. Mientras que la
Game Boy permitía una autonomía de 30 horas con 4 pilas AA, la Game Gear y Lynx
apenas llegaban a las 6 horas con dos pilas más.
La serie de consolas Game Boy perduró hasta el año 2006 con el lanzamiento de la
Nintendo DS. Durante los años que estuvo en el mercado dominó el sector de las consolas
portátiles llegando a vender más de 200 millones de unidades.
Durante estos 17 años que estuvo en el mercado, Nintendo sacó diferentes versiones de
la Game Boy, mejorando y cambiando ciertos aspectos de consola.
Figura 9.1.2. Game Boy, Game Gear y Lynx
Página 16
9.2. Versiones
Game Boy (1989 – 1995)
Primera consola de la serie Game Boy. Salió al mercado
el año 1989 haciéndose un hueco en el sector de las
consolas portátiles junto con Sega y Atari. La pantalla
de la consola tan solo contaba con 4 tonos diferentes
de verde sin retroiluminación. Usaba 4 pilas AA que le
proporcionaban un tiempo de juego de hasta 30 horas.
Aunque no lo implementaron muchos juegos la consola,
podía soportar hasta 16 jugadores simultáneamente
haciendo uso del cable link. La Game Boy se
caracterizó por los buenos juegos que contaba, entre
estos estaba el Tetris, juego que impulso el éxito de la
consola.
Game Boy Pocket (1996 – 2000)
El año 1996 Nintendo saco la Game Boy Pocket, esta
consola era más pequeña que la primera versión,
haciéndola a la vez más ligera y fácil de transportar.
También redujeron el consumo de energía, usaba 2 pilas
AAA que proporcionaban 10 horas de juego. Se cambió
el diseño del puerto link haciéndolo más pequeño, diseño
que perduró en las siguientes versiones de la Game Boy.
En esta versión, además, cambiaron la pantalla que
utilizaban, cambiando a una paleta de 4 grises, de blanco
a negro.
Figura 9.2.1. Game Boy.
Figura 9.2.2. Game Boy Pocket.
Página 17
Game Boy Light 1998
Esta versión de la Game Boy solamente se distribuyó en
Japón durante 1998. Fue la única consola de la serie que
contaba con una pantalla retroiluminada hasta el año
2003, con el lanzamiento de la Game Boy Advance SP.
Usaba 2 pilas AA que proporcionaban 20 horas de juego
sin retroiluminación, y 12 horas de juego con la
retroiluminación activada.
Game Boy Color (1998 – 2003)
Consola sacada al mercado el año 1998. Esta versión se
caracterizó por el gran número de colores que permitía utilizar.
Tenía una paleta de 32.000 colores diferentes de los cuales
56 podían aparecer simultáneamente en pantalla. Usaba 2
pilas AA que le proporcionaban hasta 10 horas de juego. Los
juegos anteriores a esta versión eran compatibles con la
consola e incluso permitía jugarlos con cierta variedad de
color. Además, mejoraron los aspectos técnicos de la consola,
poniéndole el doble de velocidad de procesamiento, y una
memoria RAM 4 veces mayor que la original.
Figura 9.2.3. Blíster de la Game
Boy Light Famitsu Skeleton.
Figura 9.2.4. Game Boy Color.
Página 18
9.3. Especificaciones técnicas
En este punto vamos a ver más a fondo las características de las primeras Game Boy,
pudiendo ver cómo fueron evolucionando en su aspecto técnico. Vamos a empezar con
una pequeña tabla donde podremos comparar las especificaciones técnicas de las
primeras Game Boy, para más adelante, centrarnos poco a poco en el funcionamiento
interno de la Game Boy. En ver cómo hacen las cosas y porque las hacen a su manera.
Game Boy Game Boy Pocket Game Boy Color
Microprocesador Sharp LR35902 Sharp LR35902 Sharp LR35902
Frecuencia de reloj 4,19 MHz 4,19 MHz 8 MHz
RAM 8 KBytes 8 KBytes 32 KBytes
VRAM 8 KBytes 8 KBytes 16 KBytes
Dimensiones 148 x 90 x 32 mm 127,6 x 77,6 x 25,3
mm
133,5 x 78 x 27,4
mm
Peso 220 g 125 g 138 g
Consumo de energía 78 - 80 mA hr.
4 pilas AA (1,5 V)
89 - 90 mA hr.
2 pilas AAA (1,5V)
70 - 80 mA hr.
2 pilas AA (1,5V)
Tiempo de juego Hasta 30 horas Hasta 10 horas Hasta 10 horas
Tipo de pantalla LCD LCD TFT
Tamaño de pantalla 4,7 x 4,3 cm 4,8 x 4,4 cm 4,4 x 4 cm
Resolución de
pantalla
160 x 144 píxeles 160 x 144 píxeles 160 x 144 píxeles
Página 19
Colores 4 (tonos de verde) 4 (tonos de gris) 32.000 colores
56 simultáneamente
Sonido Estéreo, 4 canales Estéreo, 4 canales Estéreo, 4 canales
Tabla 9.3.1. Especificaciones técnicas.
Hay muchos aspectos de la Game Boy que han ido cambiando con el tiempo, como el
consumo de energía o el tamaño de la memoria RAM, pero hay un aspecto en el que se
mantiene constante y ese es su microprocesador, el Sharp LR35902. Un microprocesador
que fue diseñado especialmente para Nintendo cogiendo partes del Zilog Z80 y el Intel
8080.
Página 20
9.4. CPU: Sharp LR35902
En este punto, vamos a ver el núcleo de la Game Boy, su microprocesador. Este es el
encargado de leer y ejecutar todas las instrucciones que vaya encontrado. Pero para
poder leer y entenderlas, estas deben estar en su juego de instrucciones.
Se dice que el microprocesador de la Game Boy, el Sharp LR35902, es una versión del
Zilog Z80, ¿pero por qué? El microprocesador Zilog Z80 cuya arquitectura está basada en
el Intel 8080, añade nuevos registros y un juego de instrucciones ampliado para trabajar
con ellos. Aunque el microprocesador de la Game Boy no cuenta con los nuevos registros
que el Z80 añade, si es cierto que su juego de instrucciones es más parecido a este.
Los registros de un microprocesador permiten almacenar valores en memoria y realizar
diferentes operaciones con ellos. Estos microprocesadores cuentan con registros de 8 bits,
que pueden juntarse en grupos de dos para realizar operaciones de 16 bits, muy lejos de
los 64 bits que utilizan los microprocesadores actuales. El Sharp LR35902 cuenta con 10
registros diferentes, al igual que el Intel 8080.
El registro A, es uno de los más importantes de todo el conjunto ya que hace el papel de
acumulador, esto es debido a que la gran mayoría de operaciones aritméticas del
microprocesador se realizan sobre este registro, almacenando en este el resultado final.
El registro F, se encarga de almacenar los distintos flags que diferencia el
microprocesador. Cada flag es un bit distinto del registro, y estos van cambiando de valor
en función de las operaciones que se vayan realizando. En particular, el microprocesador
Sharp LR35902 cuenta con 4 flags diferentes. No todas las instrucciones editan los flags,
en caso de hacerlo, se pondrán a 1 los flags correspondientes.
● Flag Zero (Z), se marca en el caso de que el resultado valga 0.
● Flag de Acarreo (C), se marca en el caso de que haya habido acarreo.
● Flag de Medio Acarreo (H), se marca en el caso de que haya habido acarreo en
los 4 bits menos significativos. Hecho especialmente para operaciones BCD.
● Flag de Resta (N), se marca en el caso de que la última operación haya sido una
resta.
Los registros H y L, están pensados para almacenar direcciones de memoria, donde el
registro H almacena el byte más significativo, mientras el L el menos significativo. Existen
muchas instrucciones que o bien cogen el valor almacenado en la dirección HL, o llaman
Página 21
a la rutina almacenada en este registro. Además, al igual que el registro A se encarga de
acumular operaciones aritméticas, pero en este caso, en operaciones de 16 bits.
Los registros B, C, D, y E se utilizan como registros auxiliares, y pueden agruparse en BC
y DE para almacenar valores de 16 bits. Estos registros no solamente sirven para
almacenar datos, al contar con un pequeño set de instrucciones que nos permite realizar
operaciones muy básicas en ellos, podemos darles diferentes funcionalidades, entre ellas,
tenemos la de funcionar como contadores y, al poder trabajar con valores de 16 bits
también podemos usarlos como dirección de destino de los datos, mientras en HL
tenemos la dirección de origen.
El registro SP o Stack Pointer es uno de los registros de 16 bits del microprocesador. En
este registro se almacena la dirección donde está almacenado el último valor de la pila
de llamadas. Cada vez que hacemos PUSH o CALL estamos añadiendo valores a esta
pila y es este registro el que se encarga de apuntar al último valor añadido, esto sirve a la
hora de hacer POP o RET, para saber qué valor tenemos que recuperar o por qué parte
del código hay que seguir ejecutando.
El registro PC o Program Counter también es un registro de 16 bits. Encargado de indicar
la dirección por la que va la ejecución.
El microprocesador Zilog Z80 añadió 11 registros nuevos, 7 de los cuales (A’, B’, C’, D’,
E’, H’ y L’) son registros alternativos a los 7 principales del Intel 8080 (A, B, C, D, E, H y L).
Estos registros se reparten entre el banco principal y el alternativo, mejorando la
velocidad en presencia de interrupciones al poder cambiar de un banco al otro. Por otra
parte, tenemos los registros de refresco e interrupciones, y los registro IX e IY. Estos
últimos, son dos registros de 16 bits indexados que permiten acceder a un amplio número
de direcciones a partir de la dirección almacenada en estos registros.
Página 22
Una vez hemos visto toda la información correspondiente a los registros, podemos pasar a
las instrucciones. El microprocesador sabe qué instrucción tiene que ejecutar mirando los
primeros bytes de esta, en el caso de que solamente se usará un byte para identificar
cada instrucción, solamente podríamos hacer uso de 256 instrucciones. Esto no sucede
en la realidad, para ampliar el número de instrucciones se hace uso de un byte de prefijo.
En el caso del Zilog Z80, cuenta con 4 prefijos diferentes, lo que le da un total de 1268
posibilidades, aunque no se utilizan todas. El Sharp LR35902, al contar con menos
registros solamente hace uso de 1 prefijo, lo que da 512 posibles instrucciones, de las que
se utilizan 501.
Podemos englobar todas las instrucciones en los grupos siguientes:
● Transferencias de información: LD, PUSH, POP...
● Operaciones aritméticas: ADD, INC, DEC, CP...
● Operaciones lógicas: AND, OR, XOR...
● Cambios y rotaciones: RLC, RRC, SRA...
● Manipulación de bits: RES, SET, BIT...
● Instrucciones de Control: HALT, NOP, DI, EI...
● Saltos: JP, CALL, JR...
Figura 9.4.1. Registros del microprocesador Zilog Z80. De color blanco
los registros que compartían el Zilog Z80 y el Sharp LR35902.
Página 23
9.5. Mapa de memoria
Para que el microprocesador pueda trabajar fácilmente con todos los elementos de
Entrada y Salida de la Game Boy, como la pantalla, los botones y los altavoces, esta tiene
la memoria mapeada. Es decir, parte de la memoria de la Game Boy está reservada para
los elementos de E/S, de esta forma facilita la lectura y escritura al poder utilizar
instrucciones de transferencia de información sobre sus datos, en vez de tener que utilizar
instrucciones especializadas para la entrada y salida de datos como tiene el Zilog Z80.
Al poder almacenar direcciones de memoria de hasta 16 bits, le permite a la Game Boy
tener hasta 216 bytes de información, o lo que es lo mismo 65.535 bytes. Para tratar las
direcciones de memoria vamos a hacerlo en hexadecimal, por consecuente en la siguiente
tabla se puede ver como la memoria de la Game Boy va desde $FFFF hasta $0000.
$FFFF Registro de activación de Interrupciones
$FF80 - $FFFE RAM Alta Interna
$FF4C - $FF7F No usable
$FF00 - $FF4B Puertos de Entrada/Salida
$FEA0 - $FEFF No usable
$FE00 - $FE9F Atributos de Sprite (OAM)
$E000 - $FDFF Espejo de los 8kB de RAM interna
$C000 - $DFFF 8kB de RAM interna
$A000 - $BFFF 8kB banco intercambiable de RAM
$8000 - $9FFF 8kB Video RAM
$4000 - $7FFF 16kB banco intercambiable de ROM
$0000 - $3FFF 16kB Banco 0 de ROM
Tabla 9.5.1. Mapa de memoria de la Game Boy.
De entre los 64 kB con los que cuenta la Game Boy, al final solamente tenemos disponibles
32 kB, organizados en dos bloques de 16 kB cada uno. No todos los juegos de Game
Boy caben en tan poco espacio. Por ejemplo, el juego de The Legend of Zelda, Link’s
Página 24
Awakening ocupa 512 kB, pero ¿cómo consiguen ocupar más espacio del que tiene la
propia Game Boy?
Esto se consigue a través de una técnica llamada banking, que nos permite intercambiar
diferentes bloques de memoria. Los bloques de memoria preparados para poder ser
intercambiados se encuentran en la dirección $4000 y $A000. Hay cartuchos de Game Boy
que incorporan mappers, de forma que pueden tener bancos de memoria ROM externa
que poder intercambiar por estos bloques. Al igual que con la memoria ROM, hay mappers
que también permiten intercambiar memoria RAM, estos llevan una pila interna para no
perder el contenido al apagar la consola.
Página 25
9.6. Organización de la ROM
El primer bloque de ROM debe tener una cierta estructura si queremos que la Game Boy
sea capaz de leerlo, en el caso de que no guarde esta estructura el juego puede llegar a
detenerse.
$4000 - $7FFF 16kB banco intercambiable de ROM
$014F - $3FFF Banco 0 de ROM
$014E Checksum
$014D Prueba de complemento
$014C Versión de ROM
$014B Código de licencia antiguo
$014A Código de zona
$0149 Tamaño RAM
$0148 Tamaño ROM
$0147 Tipo de cartucho
$0146 Soporte de SGB
$0144 - $0145 Código de licencia
$0143 Soporte de GBC
$0134 - $0142 Nombre del cartucho
$0104 - $0133 Logo de Nintendo
$0100 - $0103 Punto de entrada a la ejecución del programa
$0000 - $00FF Vectores de interrupción
Tabla 9.6.1. Organización de la ROM.
Página 26
Dentro de los diferentes campos que hay que escribir en el primer bloque de ROM, el más
versátil de todos es el primero, donde tenemos los vectores de interrupción. Cada una de
las posibles interrupciones que podemos controlar en la Game Boy tiene una dirección
de memoria predefinida en esta zona, donde podremos llamar a la rutina que queremos
que se ejecute, pero, en el caso de no gastar interrupciones, podremos gastar esta zona
para almacenar código propio.
Página 27
9.7. Sistema de vídeo
La Game Boy no almacena información del color de cada píxel de la pantalla, como
hacen la gran mayoría de los dispositivos, en cambio, almacena información sobre el tile
de cada zona. Cada tile corresponde a un bloque de 8x8 píxeles.
La Game Boy tiene un buffer de 256 x 256 píxeles, que equivalen a 32 x 32 tiles. No
obstante, no todo el buffer se muestra por pantalla, el área visible solamente es de 20 x 18
tiles. Para poder mover el área visible a través del fondo contamos con un par de registros
que controlan la posición de éste área respecto al fondo, el Scroll X y el Scroll Y. Además,
el buffer es cíclico, por lo que cuando el área visible llega a uno de los extremos del buffer
se visualiza el contenido del lado opuesto.
Además de los registros de Scroll, hay una gran serie de registros que permiten controlar
el sistema de vídeo. A continuación, vamos a ver los más importantes:
Figura 9.7.1. Memoria de vídeo
Página 28
$FF40 - Control del LCD
Este nos permite editar cada uno de los parámetros de la pantalla LCD de la Game Boy.
Cada bit del registro tiene una funcionalidad diferente.
Bit Funcionalidad Valor 0 Valor 1
Bit 7 Control de la pantalla Off On
Bit 6 Selección del buffer de la Ventana $9800 - $9BFF $9C00 - $9FFF
Bit 5 Control de la Ventana Off On
Bit 4 Selección del Mapa de Tiles a utilizar $8800 - $97FF $8000 - $8FFF
Bit 3 Selección del buffer del fondo $9800 - $9BFF $9C00 - $9FFF
Bit 2 Tamaño de los Sprites 8x8 8x16
Bit 1 Control de los Sprites Off On
Bit 0 Control del fondo Off On
Tabla 9.7.1. Funcionalidades del Registro de Control del LCD.
Los bits de control permiten activar o desactivar cada una de las partes que componen la
imagen final que se ve por la pantalla. Más adelante vamos a ver en profundidad cuales
son cada una de estas partes y se entenderán mejor cada una de las opciones.
$FF41 - Estado del LCD
En este registro podemos consultar en cualquier momento que está haciendo el LCD,
además de que podremos activar las diferentes interrupciones con las que cuenta.
Bit 6 Interrupción LYC = LY Off On
Bit 5 Interrupción Modo 2 OAM Off On
Bit 4 Interrupción Modo 1 V-Blank Off On
Bit 3 Interrupción Modo 0 H-Blank Off On
Bit 2 Flag de coincidencia (Sólo Lectura) LYC <> LY LYC = LY
Página 29
Bit 1-0 Flag de Modo (Sólo Lectura) 0: H-Blank
1: V-Blank
2: Buscando OAMs
3: Transfiriendo datos al LCD
Tabla 9.7.2. Funcionalidades del Registro de Estado del LCD.
Como se puede observar, el registro permite controlar en qué modo se encuentra en todo
momento a partir de los bits de lectura, además, de poder habilitar las interrupciones que
se activarán cuando la Game Boy entre en el modo correspondiente.
La Game Boy puede encontrarse en cuatro modos diferentes. Dependiendo del modo
podremos acceder a unos bloques de memoria o en su contra los tendremos bloqueados.
En el caso de tener un bloque de memoria bloqueado, al intentar acceder a su información,
siempre nos devolverá el valor nulo, $FF.
• Nos encontramos en el modo 0 o H-Blank durante el tiempo en el que el LCD ha
terminado de pintar una de las líneas de la pantalla y tiene que pasar a la siguiente.
Durante este periodo podremos acceder tanto a la VRAM como a los atributos de
los Sprites (OAM).
• En el caso del modo 1 o V-Blank es el periodo de tiempo que pasa entre que el
LCD termina de pintar la última línea de la pantalla hasta que vuelve a la primera
línea. Durante este modo, es aconsejable editar todos los elementos gráficos
requeridos, ya que en caso de hacerlo durante los modos 2 o 3 podremos dar lugar
a errores gráficos. En el caso de que vaya a haber un gran cambio podremos
apagar el LCD durante este modo. Al igual que durante el H-Blank podemos
acceder tanto a la VRAM como a los atributos de los Sprites.
• En el modo 2 el controlador del LCD está accediendo a la memoria de los atributos
de los sprites, por esto, durante este periodo no podremos acceder a esta memoria.
• En el modo 3 el controlador del LCD está leyendo tanto la memoria de los atributos
de los sprites como la VRAM, por lo que no podremos acceder a ninguna de estas
memorias durante este periodo o como ya hemos dicho dará lugar a errores
gráficos.
Página 30
$FF42 Scroll Y - $FF43 Scroll X
Con estos registros podemos editar la posición del área visible del fondo, los valores van
de 0 hasta 255. Editar estos valores supone actualizar la pantalla, por lo que deberemos
actualizarlos en el V-Blank.
$FF44 LY
En este registro encontramos la fila por la que va pintando el LCD, en este registro
encontramos valores entre 0 y 153, aunque la pantalla solamente tiene 144 filas. Estos
valores de más se corresponden al tiempo que pasa durante el V-Blank.
Ahora que ya sabemos la utilidad del registro LY puedo explicar el flag de coincidencia
del registro $FF41. En el registro LYC ($FF45) podemos almacenar el valor que mejor nos
convenga, de esta forma el flag valdrá 1 cuando el LCD pase por la fila que hemos
almacenado en el registro LYC, o lo que es lo mismo, el valor en LY será el mismo al
almacenado en LYC.
$FF4A Posición Y de la Ventana - $FF4B Posición X de la Ventana
Con estos registros podremos ajustar la posición en la que se va a encontrar la ventana.
A lo largo de este punto se han ido utilizando las palabras fondo, ventana y sprites
repetidamente, pero sin llegar a dar una explicación clara de cuál es el papel de cada uno
de estos elementos, ni de dónde podemos encontrarlos. Estos tres elementos forman la
estructura de la imagen.
• El fondo, se utiliza para pintar mapas y escenarios, y es la parte que más presencia
tiene en la pantalla.
• La ventana, se utiliza para pintar elementos que pretenden dar información, datos
que no van ligados con el fondo. Por ejemplo, se puede utilizar para pintar el HUD,
el menú de opciones o los caracteres de una conversación.
Figura 9.7.2. Estado del Flag de Modo en el tiempo
Página 31
• Los Sprites, se utilizan para los elementos primarios del juego, aquellos que
queremos poder mover por la pantalla con la menor complicación posible. Como
personajes, enemigos, proyectiles, etc.
Tanto el fondo como la ventana están almacenados dentro de la memoria de vídeo, en
bloques de 1024 bytes, ya que cada uno de estos puede llegar a medir hasta 32x32 tiles.
La Game Boy tiene reservados específicamente dos bloques para estos dos elementos, el
primero en $9800-$9BFF y el segundo bloque en $9C00-$9FFF, en el registro de control
del LCD marcaremos que elemento queremos que gaste cada bloque.
En el caso de que activemos la ventana, en el registro de control del LCD, está aparecerá
por encima del fondo. Podemos editar la posición en la que empieza la ventana con dos
registros reservados específicamente para esto. A la hora de situar la ventana, lo más fácil
es ponerla o en la zona inferior o en la zona de la derecha, en caso contrario los tiles vacíos
de la ventana también sobrescribirán el fondo. De todas formas, existen diferentes
métodos para situar la ventana tanto en la zona superior como en la zona izquierda, pero
habría que hacer uso de las diferentes interrupciones.
Tanto el fondo como la ventana, no almacenan la información de cada píxel que están
mostrando por pantalla, en cambio, como he ido diciendo reiteradamente, trabajan con
tiles, al igual que los sprites.
Los sprites son aquellos elementos gráficos que se mueven por encima del fondo, y a
diferencia del fondo y la ventana puede utilizar dos tamaños de tiles diferentes. Los tiles
Figura 9.7.3. Ventana, Fondo y Sprites del juego Super Mario Land.
Página 32
de los Sprites pueden ser tanto de 8x8 píxeles como de 8x16 píxeles. El hecho de que
utilicemos sprites de 8x16 píxeles no significa que el tamaño de los tiles sea más grande,
para utilizar está opción los tiles deberán estar preparados de antemano, de forma que
cada sprite utilice el tile asignado más el siguiente.
La Game Boy solamente puede dibujar 40 sprites, ya sea en el modo de 8x8 como en el
8x16, además, que por limitaciones del hardware solamente pueden haber 10 sprites en
una misma fila o estos empezarán a parpadear o incluso no llegar a pintarse. A
diferencia del fondo, los sprites no utilizan los 4 colores, con los que cuenta cada tile,
solamente utilizan 3 de ellos y el cuarto lo utilizan como color transparente. Además,
cada uno de los sprites cuenta con su conjunto de atributos propios, estos están
almacenados en la dirección $FE00.
Los atributos de los sprites están formados por un conjunto de 4 bytes. Estos valores editan
directamente los valores de los Sprites por lo que deberemos actualizarlos durante el V-
Blank:
● En el byte 0 tenemos la coordenada Y del Sprite. Si queremos que el sprite se
pinte en la primera línea de la pantalla deberemos poner un 8, de forma que si
queremos mostrar el sprite parcialmente por arriba, podemos utilizar los valores
de 0-7. Al igual si queremos que se muestre en la última línea de la pantalla
deberemos almacenar un 140.
● En el byte 1 tenemos la coordenada X del sprite. Este registro funciona igual que
el anterior, su primer valor es el 8. La única diferencia es que para pintar en la última
línea deberemos almacenar el valor 160.
● En el byte 2 tenemos el número del sprite. Le decimos al sprite cual es el tile que
queremos que muestre por pantalla.
● En el byte 3 contamos con las diferentes propiedades o efectos que le podemos
Figura 9.7.4. Imágenes formadas con sprites de 8x8 o 8x16 píxeles.
Página 33
aplicar al sprite. Al igual que otros registros cada bit tiene una funcionalidad
diferente.
Bit 7 Prioridad Por encima del
fondo
Por debajo del
fondo
Bit 6 Reflejo Y Normal Espejado vertical
Bit 5 Reflejo X Normal Espejado horizontal
Bit 4 Número de paleta (Solo en GB) OBP0 OBP1
Bit 3 Banco de VRAM (Solo en GBC) Banco 0 Banco 1
Bit 2-0 Número de paleta (Solo en GBC) Paleta 0-7
Tabla 9.7.3. Propiedades del Sprite.
Como se puede observar en la tabla, hay algunos bits que o bien solo sirven para la
Game Boy Color como otros que, al contrario, solo se utilizan en la Game Boy y Game
Boy Pocket. Esto permite que existan juegos que funcionen tanto en Game Boy como en
Game Boy Color, cada uno con una paleta de colores diferentes que se adapta a las
posibilidades de la consola.
El hecho de que la consola sea en color o monocromática no importa a la hora dibujar
cada uno de los tiles. Cada tile se compone de cuatro colores diferentes, estos colores
cambiarán en función de la paleta que estemos utilizando para ese tile. En el caso de que
el tile vaya a utilizarse como Sprite solo contaremos con 3 colores visibles, ya que el
primero de todos tendrá el papel de color transparente.
Como contamos con 4 colores diferentes por tile, cada uno de los píxeles del tile ocupará
2 bits. Como hemos dicho anteriormente los tiles son de 8x8 píxeles, es por eso que el
tamaño total de cada tile es de 128 bits, o lo que es lo mismo 16 bytes.
Ahora bien, ¿cómo transformamos un tile en información que podamos utilizar en nuestro
juego? Cada línea del tile se repartirá en dos bytes diferentes, en el primer byte
pondremos los bits menos significativos de cada píxel de la hilera, mientras que en el
segundo byte pondremos los bits más significativos. A la hora de saber cuáles son los
bits más y menos significativos de un valor, debemos saber que cuanto más a la
izquierda nos encontremos más significativo es, por ejemplo, en el caso de un byte el bit
más significativo sería el séptimo.
Página 34
En la figura 9.7.5. podemos ver un ejemplo de cómo se obtiene la información de un tile.
Arriba en grande podemos ver el tile original desde el que hemos partido, y el valor de
cada uno de los colores que tiene la imagen. En la zona inferior, podemos ver los dos bytes
que forman cada hilera, en binario y en hexadecimal. Al ser el bit menos significativo el de
la derecha, podemos ver en la imagen inferior izquierda como todos los píxeles que tenían
el valor 1, o el 3, han sido almacenados con el 1.
Aunque en este ejemplo los colores seguían un orden de más claro a más oscuro, esto no
siempre tiene porqué ser así, la información que tenemos realmente de cada píxel es el
color que utilizar de su paleta. Es decir, si quisiéramos que la imagen tuviera el mismo
patrón, pero con otro orden en los colores solamente habría que editar la paleta, además
en esta podemos repetir colores si queremos.
A la hora de configurar una paleta pondremos en orden los diferentes colores a utilizar. Si
tomamos como ejemplo la paleta de la figura 9.7.5., cuyos colores son 11, 10, 01 y 00,
deberemos almacenar en el byte donde irá la paleta el valor %11100100.
Figura 9.7.5. Proceso de obtención de los bytes de un tile.
Página 35
En el caso de la Game Boy, que juega con una escala monocromática de colores, contamos
con 3 registros para almacenar paletas a partir de la dirección $FF47. En el primer registro
podremos almacenar la paleta que va a utilizar el fondo y la ventana, los demás dos
registros serán las diferentes 2 paletas que podrán utilizar los sprites.
Con la Game Boy Color, al poder trabajar con los componentes RGB del color la cosa
cambia. Los tiles siguen almacenando el número del color de la paleta a utilizar, pero las
paletas pasan de ocupar 1 byte a ocupar 2 bytes, es decir 16 bits. Dentro de estos 16 bits
encontramos los componentes RGB del color.
Dentro de los 16 bits que ocupa cada color, cada componente toma 5 bits dejando un bit
sin utilizar. Al solamente contar con 5 bits por color, no podemos tomar 255 valores
diferentes en cada componente como estamos acostumbrados actualmente. En cambio,
podemos tomar 32 valores diferentes por componente, lo que nos deja en total 32.768
colores, aunque simultáneamente en la pantalla solamente podremos mostrar 56 colores.
A la hora de asignarles colores a los sprites, tendremos acceso a más paletas que en el
modo monocromático de la Game Boy, podremos utilizar 8 paletas diferentes para los
sprites, en comparación con las 2 paletas que teníamos antes.
Figura 9.7.6. Estructura de los colores en GBC.
Página 36
10. Juegos
Durante el tiempo que la Game Boy estuvo en el mercado, Nintendo sacó muchos juegos
divertidos y de alta calidad. En este punto vamos a analizar dos de estos juegos, para que
no se haga repetitivo he decidido coger dos juegos muy diferentes entre sí, tanto en
mecánicas, como en estilo de juego. El primer juego será el Tetris, y luego continuaremos
con Pokémon Plata.
10.1. Tetris
Juego sacado al mercado el año 1989, como una
versión portable del juego de Alexey Pajitnov. Es
de los pocos juegos de Game Boy que no
necesita memoria externa, el juego cabe a la
perfección en los 32kB de la consola. Este juego
disparó las ventas de Game Boy, llegando a crear
un pack donde podías conseguir la consola junto
este juego.
El juego es muy adictivo, pese a tener una
mecánica muy simple. El juego cuenta con
diferentes piezas llamadas tetrominós, estas
piezas están formadas por todas las posibilidades que salen al juntar 4 bloques del mismo
tamaño. Las piezas tienen una zona por la que irán cayendo hasta llegar al fondo o tocar
otra pieza, el juego consiste en ir completando filas sin que las piezas lleguen a tocar la
zona superior. Cada vez que completamos una fila, esta se elimina convirtiéndose en
puntos para el jugador.
A medida que vamos jugando y consiguiendo puntos
el nivel de dificultad del juego sube haciendo que
las piezas cada vez caigan a mayor velocidad.
Aunque los primeros niveles no suponen un gran reto,
los últimos solo son aptos para los más hábiles.
El Tetris cuenta con dos modos diferentes de juego,
en el primero de estos el final lo marca la habilidad
del jugador, ya que se terminará en cuanto no haya
sitio para la siguiente pieza y en función de las líneas
completadas tendremos más o menos puntos, en
Figura 10.1.1. Portada del juego Tetris.
Figura 10.1.2. Partida Tetris.
Página 37
cambio, en el segundo modo empezaremos con muchos bloques puestos de forma
aleatoria y el jugador deberá limpiar 25 líneas para ganar.
El juego recompensa a los jugadores más
hábiles con diferentes animaciones al
acabar un nivel. En el primer modo de juego
dependiendo de los puntos (mínimo
100.000) podremos ver el lanzamiento de
una nave diferente. En el segundo modo al
completar ciertos niveles nos aparecerá una
animación diferente, por ejemplo, al
completar el último nivel aparecer la
lanzadera espacial de la URSS Buran.
Cuando jugamos la partida de Tetris, en
pantalla solamente hay 8 sprites, estos son
los 4 bloques de la pieza que está cayendo, y los 4 bloques de la siguiente pieza. Como
no llegan a los 10 sprites no tienen por qué preocuparse del límite de sprites en horizontal.
Una vez la pieza cae, esta pasa a formar parte del fondo al igual que los demás
elementos.
Al no tener muchos elementos visibles diferentes durante la partida, el juego no va
cambiando los tiles que tiene cargados, como hacen muchos de los demás juegos de
Game Boy, de hecho, solamente cambia de tiles para mostrar las diferentes animaciones
que tiene.
Fue el primer juego compatible con el cable link que
permite conectar dos Game Boy. Cuando dos
consolas se conectan, el juego les permite jugar en
el modo dos jugadores. En este modo, los
jugadores representan a Mario y Luigi, y se
enfrentan a ver quién es el primero en completar 30
líneas. Como siempre, si no hay sitio para la
siguiente ficha, hemos perdido. El multijugador trae
2 nuevas características, la primera de ellas es que
podemos ver la altura de la zona del
contrincante, la segunda es que si hacemos algún combo le enviaremos a la zona inferior
del contrincante una hilera de bloques para molestar. La partida la gana el primer jugador
en ganar 4 partidas.
Figura 10.1.3. Animación victoria Tetris.
Figura 10.1.4. Multijugador Tetris.
Página 38
10.2. Pokémon Plata
Segunda entrega de la saga Pokémon sacada al
mercado el año 1999. Este juego sí que utiliza
memoria externa para funcionar, de hecho,
ocupa nada más y nada menos que 2.048 kB. La
diferencia es muy grande a la hora de compararlo
con el Tetris que solamente ocupa que 32kB. El
gran peso del juego está justificado por todo el
contenido (textos, mapas, tiles, etc.) que
contiene el juego.
Todo el mundo conoce o ha escuchado hablar
sobre el mundo de pokémon, aun así, voy a dar
un pequeño resumen sobre el funcionamiento del
juego. La mecánica del juego se centra en
batallas entre pokémons, donde haremos luchar a nuestro equipo, contra el equipo del
contrincante, haciendo uso de sus habilidades singulares. Estos pokémons habitan en la
naturaleza del juego, y los podemos encontrar por el mundo, para que se unan a nuestro
equipo. Desde el inicio del juego nuestra misión será completar la liga pokémon, pero
para eso antes deberemos obtener las diferentes medallas del mundo. Durante este
proceso, el juego nos plantea diferentes misiones que deberemos ir completando,
siguiendo con la historia.
Como había dicho al inicio, el gran peso del juego está justificado con el contenido que
tiene, de hecho, el juego cuenta con 251 pokémons diferentes, cada uno con su set de
tiles. Nada más que con los tiles que se muestran en pantalla cuando combaten, cada
pokémon necesita 85 tiles diferentes, lo que supone 1360 bytes por pokémon. Solamente
los tiles de los pokémons ocupan 10 veces la memoria disponible en la Game Boy. Al igual
que los pokémons, cada ataque, cada personaje, cada elemento del fondo, textos, mapas,
objetos, etc. tiene que estar almacenado, haciendo que poco a poco alcance a los 2 MB
que ocupa el juego.
El juego salió para Game Boy y Game Boy Color, por lo que todos los tiles están creados
de forma que son agradables a la vista en cualquiera de los dos modos. En la figura
10.2.2. se puede ver como aun quitando los colores tan significativos del juego este no
pierde calidad.
Figura 10.2.1. Portada del juego
Pokémon Plata.
Página 39
El mapa del nivel es muy grande en
comparación con el buffer del fondo que
recordemos que almacenaba hasta 32x32 tiles.
Para solucionar este problema, muchos juegos
utilizan la misma técnica, van editando el
buffer en función del movimiento del personaje,
de esta forma no se encuentran limitados y
pueden hacer los mapas con total libertad. En
este caso, cuando el personaje se mueve,
solamente se carga el extremo del área visible
en que se ha movido el jugador.
Otra de las técnicas que utiliza pokémon para
hacer uso de una gran serie de tiles durante el juego, es ir intercambiándolos en función
del momento. Por ejemplo, cuando hablamos con alguno de los NPCs del juego, el jugador
no puede moverse, es entonces cuando en el juego sobrescriben los tiles correspondientes
a la animación del movimiento e insertan los tiles de texto, al acabar de hablar se vuelven
a cargar los tiles de las animaciones. Lo mismo ocurre con los ataques a la hora del
combate pokémon, el juego solo almacena los tiles del último ataque que se ha realizado,
cada ataque, sobrescribe los tiles del anterior, de esta forma ahorran mucha memoria.
Figura 10.2.3. Buffer de fondo con
zonas editadas.
Figura 10.2.2. Comparación de pantalla monocromática y color.
Página 40
11. Herramientas de Desarrollo
El mundo tecnológico ha cambiado mucho en poco tiempo, y con él, también lo han hecho
las herramientas que se gastan para el desarrollo de videojuegos. Por esto, en este punto
voy a intentar exponer ambas partes, y de esta forma observar el gran cambio que ha
habido. Para finalizar, expondré las herramientas que existen hoy en día para desarrollar
un juego de Game Boy, en particular, las que he utilizado para el desarrollo del juego.
11.1. Herramientas Predecesoras
La forma de trabajo de los desarrolladores de juegos de antaño era muy diferente a la
actual. Los programadores cogían papel y boli, y diseñaban en estos sus funciones, ya
fuera en BASIC o en ensamblador, una tarea que hoy en día casi ni se plantea a la hora de
ponerse a programar. Una vez tenían la función que querían probar, solamente quedaba
pasarla a la consola y comprobar que todo funcionará correctamente.
De la misma forma, la gente encargada del diseño de niveles y del aspecto gráfico,
tampoco contaban con herramientas especiales para agilizar su trabajo, cogían una hoja
de papel y mostraban en ella lo que tenían en mente. De esta forma se pueden ver hoy en
día los esquemas que montaban en hojas de papel, para organizar y mostrar el mapa del
juego en su totalidad.
El videojuego se probaba directamente sobre la máquina que los iba a ejecutar, y como
podéis imaginar, esto llevaba mucho tiempo entre encender la consola, cargar el juego y
revisar que todo funcionará correctamente. Estamos hablando de tiempos de 8 a 15
minutos, suponiendo que el juego cargará sin problemas a la primera.
Figura 11.1.1. Mapa del juego Fantasy World Dizzy.
Página 41
En el año 1987 apareció una nueva herramienta llamada PDS (Programmers Development
System), esta herramienta permitía programar el juego directamente sobre un ordenador
de 16 bits, transmitiendo el juego ya compilado a la consola, además de permitir alterar la
memoria en tiempo de ejecución.
En el caso de la Game Boy, Nintendo sacó una serie de kits de desarrollo denominados
Wide-Boy, pensados para que la consola pudiera pintar sus gráficos directamente en la
pantalla de un televisor, ayudando a que los desarrolladores no tuvieran que dejarse la
vista en la pantalla de la consola. Estos kits conectaban la Game Boy a otra consola como
la NES, SNES o la N64, permitiendo mostrar la pantalla de la Game Boy directamente
sobre la televisión.
Figura 11.1.2. Pantalla de carga del juego Astro Marine Corps. Este
contaba con un minijuego en la pantalla de carga debido al gran tiempo
que tardaba.
Figura 11.1.3. Kit de GBC diseñado para conectarse a un N64.
Página 42
11.2. Herramientas Actuales
Actualmente, la gente ya no se sienta delante de una mesa con papel y boli para programar,
prefieren hacer uso de editores de código que facilitan el trabajo. Estos editores se
encargan de buscar posibles errores en el código, además de contar con una interfaz que
facilita la búsqueda de funciones entre los archivos. Y esto solamente son dos ejemplos de
las miles de cosas que pueden llegar a aportar.
Con el tiempo, también han salido diferentes entornos de programación, como
Construct2 o Unity. Estos entornos proveen al usuario de todas las cosas básicas para
hacer un videojuego, y las simplifican para que sea muy simple hacer uso de ellas.
Por ejemplo, Construct2, un entorno diseñado especialmente para juegos 2D, presenta
una interfaz para sus scripts que permite a cualquier usuario añadir lógica a su juego a
través de acciones ya predefinidas en el entorno, de forma que cualquier usuario puede
entender y añadir lógica al juego. Este sistema también ayuda a prototipar, al poder montar
un prototipo de tu juego en muy poco tiempo.
Por otro lado, tenemos Unity, uno de los entornos más famosos actualmente junto con
Unreal Engine. Estos entornos están diseñados principalmente para juegos con un aspecto
3D, aunque también cuentan con herramientas para juegos 2D. Unity cuenta por detrás su
propio motor gráfico, físico y de red, además de una interfaz que facilita mucho la edición
de los objetos y el entorno.
Respecto al mundo de los gráficos, también han sacado muchas herramientas que agilizan
la creación de contenidos. Para la creación de modelos 3D existe el programa ZBrush el
cual agiliza mucho la edición de los modelos al poder trabajar con ellos como si de arcilla
se tratase. Además, para su texturizado, existen miles de herramientas que permiten pintar
directamente sobre el modelo, y así ir viendo en tiempo real el resultado.
Figura 11.2.1. Script de Construct2.
Página 44
11.3. Herramientas Usadas
Con las nuevas tecnologías, muchas personas amantes de los juegos retro se han
preocupado por sacar nuevas herramientas que facilitan el desarrollo de estos juegos.
Herramientas que ayudan y agilizan los diferentes procesos, como la creación de Sprites,
el diseño de niveles, la creación de la música, etc. Sin estas herramientas el proceso de
desarrollo sería mucho más tedioso de lo que ya es, ya que la gran mayoría de estas
herramientas no solamente ofrece una interfaz fácil de utilizar, además al exportar los
datos, los tenemos directamente en ensamblador, ahorrándonos el valioso tiempo que
tardaríamos en traducir todos los datos.
Existen muchas más herramientas en internet que las que voy a exponer en este punto,
cada una con sus pros y sus contras. En mi caso, me he centrado en buscar herramientas
que fueran fáciles de usar, y no costará mucho tiempo llegar a dominarlas, para agilizar el
proceso de desarrollo del juego.
Tiles - GBTD (Game Boy Tile Designer)
Aunque no soy un buen artista, me he preocupado por encontrar una herramienta que me
ayudara a crear los diferentes tiles del juego. La herramienta que acabe utilizando fue
GBTD (GameBoy Tile Designer).
Esta herramienta nos permite diseñar tiles de diferentes tamaños, como 8x8, 8x16 y
16x16. También podemos pintar con los colores de pantalla de las diferentes Game Boy.
Todas estas opciones nos permiten ver cómo quedará nuestro tile dentro del juego, además
de contar con una pantalla donde veremos cómo queda en el caso de que se vaya a repetir
múltiples veces.
GBTD cuenta con diferentes utilidades que
permiten pintar y editar el tile fácilmente. La
primera de estas es el lápiz, con el que
podremos pintar con el color seleccionado el
pixel que queramos, para zonas más grandes
podremos utilizar el cubo de pintura, el cual
rellenará todos los píxeles que haya del mismo
color. También permite mover todo el tile en una
dirección, invertirlo en un eje o rotarlo, estas
herramientas son muy útiles pues permiten
hacer grandes cambios con solamente un clic.
Figura 11.3.1. Interfaz GBTD.
Página 45
Además de todas las utilidades con las que cuenta GBTD para visualizar y editar los tiles,
también tiene un gran sistema para exportar los datos, que nos permite hacerlo con el
formato que utilizan varios compiladores de Game Boy. Como extra, nos permite exportarlo
también cómo binario, por si queremos editar a mano el archivo o bien pasarlo a un
programa de compresión.
Diseño de niveles - GBMB (Game Boy Map Builder)
La herramienta que elegí para el diseño de los niveles está muy relacionada con la anterior,
de hecho, fueron realizadas por la misma persona. Esta herramienta es la GBMB (Game
Boy Map Builder).
GBMB permite crear un mapa de hasta 1024 x 1024 tiles, en los cuales podremos pintar
cualquiera de los tiles que hayamos desarrollado en GBTD. Esto es una de las cosas que
más útil me ha parecido, el poder coger directamente el archivo donde tenía todos los tiles
guardados, cargarlo en este programa, y poder utilizarlos para rellenar el mapa a mi gusto.
Al igual que GBTD, esta herramienta cuenta con utilidades que nos permiten pintar y editar
el mapa. Con el lápiz podremos pintar el tile que tengamos seleccionado de la lista, con el
cubo podremos rellenar grandes superficies con el mismo tile. Para editar el mapa
contamos con cuatro utilidades que nos permiten añadir y quitar columnas, de esta forma
podemos ajustar el tamaño del mapa a nuestro gusto.
GBMB permite definir todas las propiedades que uno quiera, pudiendo luego exportarlas
en el formato y tamaño que se desee. Además de crearlas, tiene un formato de
visualización donde podremos ver visualmente las propiedades de cada uno de los tiles de
nuestro mapa.
Al igual que GBDT, contiene un buen
sistema para exportar los mapas, que
nos permite editar la información que
se va a exportar de cada tile y los bytes
que queremos que ocupe cada dato de
información. Con esto, podemos
exportar solamente el número de tile de
cada bloque o si lo preferimos podemos
exportar todas las propiedades de
los tiles en el formato que nosotros
elijamos. También nos permite exportarlo con el formato que utilizan una serie de
compiladores o en binario si vamos a querer postprocesar los datos.
Figura 11.3.2. Interfaz GBMB.
Página 46
Editor - Sublime Text 3
Para la creación y edición del código, decidí usar Sublime Text. He usado este editor de
texto durante varios años y nunca me ha dado ningún problema, todo lo contrario, su gran
abanico de comandos me ha sido muy útil para navegar entre los documentos y llegar
rápidamente al archivo que quería revisar.
Uno de los aspectos que más me gusta de sublime, es que a la derecha contiene un slider
que muestra una copia del archivo actual en pequeño. Esto permite al usuario moverse con
mucha facilidad por el documento, ya que en todo momento sabe en qué parte se encuentra
y que tiene a los lados.
Sublime permite a los usuarios crear paquetes que añaden funcionalidades al editor, estos
paquetes pueden ser descargados por otros usuarios permitiendo que cada uno tenga la
configuración que más le guste, y mejor le venga para cada escenario de trabajo.
En mi caso, instale el paquete z80Asm, este paquete se encarga pintar cada palabra del
código en función de cuál sea su cometido. Por ejemplo, todos los comentarios son
pintados de gris, mientras que las instrucciones z80 son pintadas de blanco. De esta forma,
podemos saber cuál es la función de cada parte del código con solamente echarle un
vistazo por encima.
Figura 11.3.3. Interfaz Sublime.
Página 47
Ensamblador - RGBDS (Rednex Game Boy Development
System)
El juego va a estar escrito en lenguaje ensamblador, un lenguaje de programación a bajo
nivel. Por esto no vamos a utilizar un compilador, como haríamos al programar con BASIC
o con C, lenguajes de programación de alto nivel. En cambio, vamos a utilizar un
ensamblador.
Un compilador, se encarga de traducir a código máquina el programa fuente de un
lenguaje de programación de alto nivel. Al igual, el ensamblador traduce las instrucciones
del lenguaje ensamblador en código máquina. La mayor diferencia entre ambos es que el
ensamblador hace la traducción 1 a 1, es decir, tenemos total control sobre el código
máquina resultante de la traducción, mientras que en un compilador no sabemos a ciencia
cierta cuál será el código máquina resultante. Esto hace que, en la gran mayoría de los
casos, el código en lenguaje ensamblador esté más optimizado que el resultante de un
lenguaje de alto nivel.
El programa que vamos a utilizar para conseguir el ejecutable final del juego va a ser el
RGBDS (Rednex Game Boy Development System), este se compone de tres elementos
diferentes:
● El rgbasm (ensamblador) se encarga de pasar a código máquina cada uno de los
archivos que componen el juego.
● El rgblink (linker) se encarga de unir todos los archivos generados por el rgbasm
en un mismo archivo de Game Boy.
● El rgbfix (comprobación) se encarga de comprobar que el header del juego está
correctamente y de alterar algunos de sus valores si se requiere.
RGBDS cuenta con una página web donde podemos consultar todas las opciones que
ofrece el ensamblador, como la creación de macros o variables que se usan en tiempo de
compilación.
Para automatizar el proceso de ensamblado y linkado del juego, he realizado un makefile
que se encarga de procesar todos los archivos que se encuentren en la carpeta src del
juego, de esta forma no tengo que preocuparme por crear o eliminar archivos, sé que todos
intervendrán en el ensamblado.
Página 48
Emulador - BGB
Una vez ya tenemos el juego ensamblado y linkado en un mismo archivo de Game Boy,
nos queda probar si los cambios que hemos realizado funcionan correctamente. Para esto
podemos hacer dos cosas, una de ellas es introducir el juego en un flash card y probarlo
directamente sobre el hardware, es decir, la Game Boy. La otra opción es probarlo en un
emulador.
Un emulador es un programa que imita el hardware de cierta consola o máquina a partir
del software. Esto permite ejecutar programas y poder ver una imitación de cómo
funcionan en la máquina objetivo. Aunque son bastante fieles, los emuladores
difícilmente consiguen imitar al 100% la máquina objetivo, esto es debido a que hay
aspectos como el propio desgaste del hardware que no se pueden imitar, por eso siempre
se aconseja hacer alguna prueba directamente sobre el hardware.
Para probar el juego, estuve mirando dos emuladores diferentes, el BGB y no$GMB, al
final me decanté por BGB debido a que la versión gratuita de no$GMB no permitía emular
juegos para Game Boy Color. BGB no ponía ninguna pega a la hora de emular juegos de
Game Boy Color, además de estar preparado para funcionar correctamente en Linux
haciendo uso de Wine.
BGB es un emulador de Game Boy y Game Boy Color que tiene muchas características,
tanto para mejorar la experiencia de juego como para depurarlos en busca de errores.
Permite alterar las características del emulador para hacerlo funcionar como más nos
guste, siempre teniendo la opción de volver a la versión por defecto. Entre otras, esto nos
permite cambiar los colores de la pantalla, la velocidad del reproductor de sonido o los
controles.
BGB cuenta principalmente con dos herramientas para depurar el código y comprobar que
todo funcione correctamente, el debugger y el VRAM Viewer. Además, cuenta con una
pequeña herramienta que nos muestra la información de la cabecera del juego, para
comprobar que todo está correctamente.
Página 49
En este ejemplo, podemos ver los diferentes campos de información que el programa
extrae a través de su cabecera. Por el nombre podemos saber que se trata de un juego
de Pokémon, aunque no es fácil saber a cuál se refiere de todos. También sabemos que el
juego hacía uso de la tecnología RTC al necesitar llevar un temporizador integrado en la
tarjeta, además de que ocupa 2 MB de memoria ROM y 32 KB de RAM. Para finalizar,
también sabemos que soporta tanto Game Boy Color como Super Game Boy.
El VRAM viewer nos enseña toda la información relacionada con el aspecto gráfico del
juego: el mapa que se está pintando por pantalla, los tiles que hay almacenados en
memoria, los atributos de cada sprite y las paletas que se están usando. Podremos
acceder a esta información a través de las cuatro pestañas que tiene la herramienta.
En la primera de las pestañas tenemos el BG map, donde podremos ver de forma gráfica
los datos almacenados en la memoria reservada para el mapa. Esta herramienta se
encarga de transformar los valores hexadecimales almacenados en el tile correspondiente
de la memoria, de esta forma podemos ver directamente como se verá por la pantalla de
la Game Boy. Además, nos muestra la información de cada tile del fondo, su posición, el
número de tile, y sus atributos. En el caso de que estemos usando toda la memoria RAM
reservada para los mapas, podremos cambiar de mapa con tan solo un clic, al igual que
los tiles, podremos cambiar la dirección de los tiles que se están usando.
Figura 11.3.4. Información del juego Pokémon Plata.
Página 50
La segunda pestaña está reservada para los Tiles, en esta pestaña podremos ver
gráficamente todos los tiles que hay almacenados en la memoria. De cada tile podemos
saber su número, la dirección en la que se encuentre y la paleta que va a utilizar. Además,
muestra en color los tiles que se están utilizando actualmente, está información es muy útil
a la hora de saber qué tiles son necesarios en cada momento.
En la tercera pestaña podemos ver la información de los 40 sprites que soporta la Game
Boy. Podemos saber su posición, el número de tile que están utilizando y los atributos
que tienen. Como todos los atributos de cada Sprite están comprimidos en un mismo byte
de información, a la derecha podremos ver cada uno de estos atributos por separado.
Además de tener una pequeña interfaz que nos dice visualmente dónde está cada uno de
los sprites.
Figura 11.3.6. Pestaña Tiles.
Figura 11.3.5. Pestaña BG map.
Página 51
En la última de las pestañas, tenemos todas las paletas que están cargadas en memoria,
cada una con su id. Al hacer clic en un color nos aparecerán sus valores de rojo, verde y
azul a la derecha, pudiendo alterar estos valores en tiempo de ejecución. Esto nos
permite probar nuevas paletas directamente sobre los tiles del juego sin utilizar
herramientas de terceros.
Una vez hemos visto todas las opciones que nos ofrece el VRAM viewer, vamos a pasar a
ver el debugger. La herramienta principal a la hora de comprobar que todo funciona
correctamente y buscar los errores por el código. El debugger está separado en cuatro
partes.
Figura 11.3.7. Pestaña OAM.
Figura 11.3.8. Pestaña de Paletas.
Página 52
En la zona superior izquierda podemos ver todas las instrucciones del código, además
de tener marcado en azul la instrucción por la que nos encontramos actualmente. En esta
zona además podemos poner diferentes breakpoints, que pausan el juego al llegar a
estos. Esto es muy útil a la hora de seguir el código a partir de cierto sitio, ya que además
pulsando F7 podremos seguir el código instrucción a instrucción hasta encontrar el
error.
En la zona inferior izquierda tenemos el estado de todos los bytes que conforman la
memoria de la Game Boy. Junto con la primera zona, nos ayuda a revisar cómo se van
alterando los datos de la memoria conforme vamos avanzando en el código, pudiendo
revisar si algún valor en específico no es el que esperábamos, o si la lectura de un byte
está bloqueada por la consola. Ambas zonas, tanto la superior como inferior, cuentan con
la opción de ir directamente a un byte de memoria ahorrándonos el tiempo de su búsqueda.
En la zona superior derecha podemos ver el estado de cada uno de los registros del
microprocesador, además de tener cada uno de los flags por separado. Al igual que la zona
anterior, sirve para corroborar que el valor de estos registros es el correspondiente al pasar por
una sección del código. También podemos alterar los registros y flags, para hacer pruebas
al código o comprobar si ciertos algoritmos funcionan correctamente.
Figura 11.3.9. Ventana Debugger.
Página 53
Para finalizar, en la zona inferior derecha tenemos la pila. En esta zona podemos ver todos los
registros, de forma ordenada, que hay en la pila, y así ver cómo se va editando con las
instrucciones PUSH, POP, RET y CALL. Esto es muy útil en casos de que el juego deje de
funcionar por completo, ya que lo más seguro es que hayamos editado mal la pila, añadiendo
o quitando un elemento que no deberíamos.
Página 54
12. Diseño del Juego
12.1. Galahad Escape!
Galahad Escape! es un juego de acción-aventura para Game Boy, donde encarnamos
el personaje de Galahad, un caballero del reino de Casebit, y le ayudamos a escapar de
la cueva en la que ha acabado. Para conseguir escapar deberá usar las utilidades de los
compañeros que irá encontrando en su interior.
12.2. Historia
[Introducción]
Galahad siempre había sido uno de los mejores caballeros del reino de Casebit. Siempre
velaba por la seguridad de todo el mundo sin preocuparse de su nivel social o ideologías.
Un buen día, paseando por el bosque de Pasuon vio como unos maleantes echaban a un
pobre niño por una gruta muy profunda. Galahad se lanzó contra los atacantes, pero estos
huyeron rápidamente, fue entonces cuando saltó a la gruta de cabeza para intentar salvar
al chaval.
[Gameplay]
Al llegar al fondo de la gruta, Galahad, vio al joven que estaba inconsciente por la caída,
rodeado por un grupo de animales que no había visto en su vida. Estas bestias tenían todo
el aspecto de ir a atacar al joven, por lo que se lanzó a atacarlos con la intención de salvar
la vida del pobre niño.
Cuando derrota a las bestias, el niño despierta y le da las gracias. Además, le cuenta que
el es un mensajero de un reino vecino y que había venido a informar al rey de un posible
ataque, pero el enemigo se había enterado de su misión y le había atacado para qye no
pudiera contar nada.
El mensajero le cuenta a Galahad, que se ha roto la pierna al caer y que no cree que pueda
salir de esa gruta en ese estado, por lo que le encarga a él la misión de contarle al rey la
importante noticia.
Avanzando por el camino que sale de la sala donde había caído, llega hasta el pueblo de
Ganom. Allí se encuentra un reducido grupo de gente que había nacido en la gruta,
descendientes de un grupo de exploradores que entró en la gruta para estudiarla y nunca
consiguió salir.
Página 55
Por el camino se encuentra con Bors, y Perceval, dos habitantes de la gruta que le
ayudarán a completar todos los retos que se les planteen.
[Final]
Después de pasar por múltiples problemas, con la ayuda de Bors y Perceval, Galahad
consigue encontrar la salida de la gruta. Dependiendo del tiempo de juego, se verá cómo
llega a advertir al rey, o si al salir ya está todo destruido por los enemigos.
12.3. Gameplay
El juego transcurre en el interior de la cueva, desde el punto en el que nos tiramos para
salvar al niño hasta el momento en el que conseguimos salir de esta para dar el mensaje
al rey de Casebit. Durante este tiempo, el jugador se dedicará a explorar la gran cueva en
la que se encuentra en busca de la salida, superando todos los retos que se le vayan
planteando desde el inicio.
Al inicio del juego, no será capaz de acceder a todos los caminos posibles, poco a poco a
medida que vaya conociendo a los demás personajes, irá desbloqueando nuevas
habilidades que le permitirán pasar por caminos que anteriormente no podía pasar. De esta
forma la exploración del mapa no se deja al libre albedrío en su totalidad.
Al inicio del juego solamente se tendrá a Galahad como personaje. Los demás personajes
se desbloquearán al hablar con ellos. Los iniciales serán fáciles de conseguir para tener
el equipo básico de personajes, pero para algunos de los demás habrá que superar ciertos
retos para conseguirlos.
A la vez que va explorando la cueva en la que se encuentra, tendrá que luchar contra
diversos enemigos y jefes finales que tendrá que derrotar haciendo uso de los diferentes
ataques de sus personajes. Al poder controlar a tres diferentes personajes, cada uno aporta
un estilo un poco diferente a los otros, además de que el jugador deberá controlar la vida
restante de cada uno de los personajes a los que controla a la vez para adecuarse a la
batalla.
Junto a los enemigos, puede que también encontremos algún tipo de trampas que
bloqueen ciertos caminos de una mazmorra. Estos se superarán haciendo uso de las
utilidades de algunos personajes en particular. Estas trampas no siempre será obligatorio
pasarlas para continuar adelante, alguna de estas lo único que harán será bloquear atajos
para una salida o bien dar acceso a objetos.
El juego considerará que el jugador a muerto o perdido en cuanto todos los personajes
Página 56
que tiene en su equipo hayan muerto, es decir, su nivel de vida sea 0. En este caso el
jugador volverá al pueblo central de la cueva, Ganom. El jugador aparecerá en la iglesia
de este con todos sus personajes sanados. Es en esta iglesia donde el jugador podrá volver
cuando quiera para sanar la vida de todos sus personajes.
En Ganom además el jugador podrá interactuar con los diferentes NPCs que se podrá
encontrar allí. Todos los personajes que haya conseguido se encontrarán por el pueblo
para poder editar su equipo a su estilo de juego. En la posada podrá guardar la partida
para continuar más adelante. Y como ya se ha dicho, en la iglesia se podrá recuperar la
salud de los personajes. A parte de estos, el pueblo contará con sus habitantes que al
interactuar con ellos pueden o bien contarte partes de su historia y sobre Ganom, o dar
alguna pista sobre donde ir o cosas que podría encontrarse el jugador por el camino por la
cueva.
12.4. Personajes
Guerrero: El guerrero cuenta con 5 corazones de vida, pero por contra es uno de los
personajes con menos ataque, solamente realiza 1 punto de daño al atacar. El guerrero
cuenta como ataque básico con la estocada, y como utilidad cuenta con la protección
direccional.
Arquero: El arquero es uno de los personajes más equilibrados en temas de salud y
ataque, al contar con 4 corazones de vida, y realizar 2 puntos de daño al atacar. Como
ataque básico el arquero cuenta con la flecha de fuego, y como utilidad tiene la flecha
eléctrica.
Figura 12.4.1. Guerrero.
Página 57
Minero: El minero cuenta con 3 corazones de vida, y realiza 2 puntos de daño al atacar.
Su bajo nivel de vida se debe a la utilidad del personaje, que utilizada en el momento
correcto puede llegar a realizar mucho daño. El minero cuenta como ataque básico picar
oro, y su utilidad es carga explosiva.
12.5. Ataques
Estocada: Golpe básico del guerrero que hace el daño del personaje en la dirección en la
que esté mirando. Este golpe tendrá un tiempo de espera entre ataque y ataque para no
poder abusar de este.
Flecha de fuego: Golpe básico del arquero que hace el daño del personaje. Al atacar lanza
un proyectil en la dirección en la que esté mirando el personaje, este proyectil no se
destruirá hasta que o bien colisione contra un enemigo, un objeto del entorno o llegue a su
distancia máxima. Este ataque tendrá un tiempo de espera muy pequeño, pero por contra
solamente podrá lanzar un proyectil a la vez, tendrá que esperarse a que este se destruya
para lanzar otro.
Picar oro: Golpe básico del minero que hace el daño del personaje en la dirección en la
que esté mirando. Este golpe tendrá un tiempo de espera entre ataque y ataque para no
poder abusar de este.
Figura 12.4.2. Arquero.
Figura 12.4.3. Minero.
Página 58
12.6. Utilidades
Protección direccional: Utilidad del guerrero. Esta utilidad le permite al guerrero poner un
escudo en la dirección en la que esté mirando que bloquea todo tipo de daño y proyectiles
en esa dirección. Mientras se esté utilizando la protección direccional el guerrero no podrá
hacer uso de la estocada.
Flecha eléctrica: Utilidad del arquero. Esta utilidad cuenta también como ataque
secundario. La flecha eléctrica funciona igual que la flecha de fuego con la diferencia que
realiza un punto menos de daño, además es capaz de activar interruptores gracias a la
carga eléctrica con la que cuenta.
Carga explosiva: Utilidad del minero. Cuando el minero hace uso de esta utilidad deja en
el suelo una carga explosiva la cual tarda unos segundos en explotar, y cuando esta explota
realiza 4 puntos de daño en las cuatro direcciones, además de poder romper ciertas rocas
marcadas en el mapa.
12.7. Enemigos
Dentro del juego contaremos con diferentes tipos de enemigos dependiendo de la zona en
la que nos podamos encontrar de la caverna, aunque en gran medida la poca diferencia
que pueda haber entre ellos sea el aspecto visual y los valores diferentes de sus variables.
Los enemigos tendrán como variables principales al igual que los personajes el daño por
ataque y los puntos de vida, además de estas variables se les añadirá la velocidad de
movimiento.
De entre todos los enemigos que pueda haber podemos diferenciarlos en varios grupos
dependiendo de las características que más resaltan de estos.
Escupidera: Este grupo de enemigos se caracteriza por poder atacar a distancia.
Cuentan con solamente 1 punto de daño y 2 corazones de vida. Este grupo de enemigos
no suele moverse, puesto a que suelen estar en lugares estratégicos donde bloquear el
movimiento del jugador y proteger ciertas zonas.
Gigantes: Este grupo de enemigos se caracteriza por su aguante y ataque. Cuentan con
3 puntos de daño y 4 corazones de vida, por contra son los enemigos con menor
movimiento y más tiempo de espera entre ataque y ataque, para así dar tiempo al jugador
a contraatacar. Este tipo de enemigo suele seguir un patrón de movimiento, que solamente
lo deja en el caso de que el jugador se acerque mucho a ellos.
Página 59
Raptor: Este grupo de enemigos se caracteriza por su alta velocidad. Cuentan con 2
puntos de daño y 1 corazón de vida. Mueren con cualquier golpe, pero son unos enemigos
muy veloces que intentarán abalanzarse contra el jugador nada más lo vean. Suelen seguir
un pequeño patrón de movimiento para asegurarse de ver al jugador si pasa.
Bestia: Este grupo de enemigos no destaca por nada en concreto, suelen estar bastante
equilibrados en todas sus variables. Cuentan con 2 puntos de daño y 3 corazones de vida.
Este grupo de enemigos suele seguir un patrón bastante amplio de movimiento, además
que al igual del raptor al ver al enemigo dejan su patrón para moverse hacía él e intentar
matarlo.
Figura 12.7.1. Gigante.
Figura 12.7.2. Raptor.
Figura 12.7.3. Bestia.
Página 60
Explosivo: Este grupo de enemigos se caracteriza por explotar al morir haciendo 1 punto
de daño en las cuatro direcciones. Cuentan con 1 punto de daño y 1 corazón de vida. Este
grupo sigue un patrón muy pequeño y solo se moverá hacia el jugador si lo ve en línea
recta, una vez deje de verlo se quedará quieto. En el enfrentamiento a este tipo de
enemigos se aconseja atacar a distancia o simplemente no matarlo.
12.8. Jefes finales
Los jefes finales cambiarán un poco el estilo de juego planteando cada uno un puzle
diferente para derrotarlo. Estos puzles involucran la utilización de las utilidades de los
personajes.
Dragón: Este será el primer jefe que veremos en el juego, y la idea es que nos enseñe la
importancia que tienen las utilidades, además hay que tener en cuenta que solamente
contaremos con el guerrero y el arquero. Cada cierto tiempo el dragón lanzará una serie
de bolas de fuego que serán casi imposible de superar a no ser que utilicemos la utilidad
del guerrero para bloquearlas. Además, el dragón estará separado del jugador por un río
de lava, obligando al jugador a atacar con el arquero cuando vea un hueco. El dragón
contará con 15 corazones de vida y sus bolas de fuego causan 1 punto de daño.
Gólem Lanza Rocas: El gólem vivirá en una zona con caminos estrechos que pueden ser
bloqueados con rocas. Al enfrentarnos al gólem, él intentará matarnos haciendo caer rocas
del cielo. Cuando las rocas caen al suelo y no nos dan, estas bloquean el camino para
llegar hasta el gólem, por lo que tendremos que hacer uso de la utilidad del minero para
destruir estar rocas. Cuando consigamos hacer daño al gólem este saltará hasta otra zona
y bloqueará algunos caminos que llevan hasta él y volverá con su patrón de movimientos.
El gólem contará con 8 corazones de vida, con la idea de que le maten con 4 ataques, las
rocas al caer infligen 2 puntos de daño. Antes de caer la zona se marcará para que el
jugador sepa que va a caer una roca en ese lugar.
Figura 12.8.1. Dragón.
Página 61
Cazador Oscuro: El cazador lo encontraremos en una zona parecida a una arena de
batalla. Este enemigo funcionará por oleadas, en cada oleada el cazador hará aparecer
una serie de enemigos para que el jugador los derrote. Una vez derrotados terminará la
oleada. No podremos hacer daño al cazador hasta que termine la oleada, y solamente
podremos realizarle un punto de daño por ronda, en total habrá que sobrevivir 4 rondas
para eliminarlo. Para que se note que no se puede dañar al cazador durante la oleada su
sprite cambiará.
12.9. Diagrama del juego
Nada más empezar el juego, se mostrará al jugador una animación donde se explicará
muy por encima la primera parte de la historia. Se mostrará como Galahad, es uno de los
caballeros de Casebit, y que al ir a salvar a un pobre chico se cae en la gruta.
En esta sala, empezaremos con una pequeña batalla contra unos monstruos para que el
jugador entienda cómo funciona el sistema de batalla del juego. Al salir de esta,
encontraremos el escudo de Galahad, y un camino por el cual es imposible pasar sin
utilizar su utilidad, de esta forma enseñaremos al jugador a usar sus utilidades.
Siguiendo el camino llegará hasta el pueblo de Ganom, en la entrada del pueblo habrá un
NPC que le explicará al jugador las cosas que puede hacer en el pueblo.
Ya en el pueblo empezaremos con lo que vendrá a ser el bucle del juego. El jugador tendrá
diferentes caminos para ir, pero no podrá ir por todos desde un inicio. El jugador tendrá que
conseguir nuevos personajes para poder pasar por los caminos con sus utilidades o bien
realizar algún evento que desbloquee el camino.
A medida que vaya completando caminos irá descubriendo nuevos tipos de enemigos que
intentarán evitar que avance. Por estos caminos podrá encontrar pociones que curen a sus
personajes. Al final de la gran mayoría de caminos habrá un jefe al que tendrán que
Figura 12.8.2. Golem.
Página 62
derrotar. Una vez derrotado este jefe, el jugador encontrará lo que había venido a buscar.
Este bucle de completar caminos para desbloquear la entrada de otros seguirá hasta el
final del juego donde el jugador desbloqueará el camino que lleva hasta la salida del
camino. Es en este punto donde dependiendo del tiempo que haya tardado el jugador en
completar el juego podrá ver una de las dos animaciones disponibles.
En el caso de que haya completado el juego a tiempo, podrá ver como Galahad va a tiempo
a avisar al rey del ataque que iba a haber, en caso contrario de que el jugador no haya
completado el juego a tiempo al salir verá cómo todo está destrozado y hecho pedazos por
su tardanza.
12.10. Interfaz
El jugador contará en la zona inferior de la pantalla con un pequeño HUD donde podrá ver
toda la información relevante de los personajes que lleve en el momento. Esta zona
inferior estará separada en tres partes diferentes, una por personaje. Cada zona mostrará
la información de cada personaje de forma ordenada. En la zona del medio podremos ver
la información del personaje que llevamos actualmente, en la derecha del siguiente
personaje y ya en la izquierda del último. En cuanto cambiemos de personaje el HUD se
actualizará ordenando correctamente los personajes.
De cada personaje podremos ver su cara para saber de qué personaje estamos viendo la
información, y ponernos en contexto para saber el orden de los cambios. También
podremos ver la vida que le queda a cada personaje y su daño base.
.
Figura 12.10.1. Diseño del HUD.
Página 63
12.11. Controles
Los controles que se usarán en el juego serán los siguientes:
● En la cruceta tenemos el movimiento básico del personaje. Al no estar limitado el
movimiento a solamente 1 eje a la vez, el jugador podrá dar a diferentes partes de
la cruceta simultáneamente para moverse.
● En el botón A tenemos el botón de interactuar. Este servirá tanto como para atacar
al encontrarnos delante de algún enemigo, como para interactuar con los NPCs o
el entorno.
● En el botón B tenemos el botón de utilidad. Al dar al botón el jugador utilizará la
utilidad del personaje que esté utilizando en ese momento.
● Con el botón Select podemos ir alternando de personaje entre los que tengamos
en el equipo.
● Con el botón Start abrimos el menú de las opciones de juego, donde podemos
cambiar las diferentes opciones que proporciona el juego.
Figura 12.11.1. Controles del juego.
Página 64
13. Desarrollo
Una vez visto el diseño, ya tenemos una idea preconcebida de lo que va a ser el juego
Galahad Scape!, pero aún queda ver si al final del desarrollo, el juego acaba
aproximándose a la idea que tenemos ahora mismo al haber leído el documento de diseño.
Es en este punto donde vamos a contar como ha ido funcionando el desarrollo del juego,
desde sus inicios, hasta el producto que se ha acabado convirtiendo, explicando además
cada uno de los puntos importantes con los que cuenta el juego.
13.1. Planificación Galahad Scape!, como cualquier otro juego, no se realizó de la noche a la mañana, por
detrás hay muchas horas de trabajo repartidas por todo el juego. Es por eso, que al afrontar
un proyecto lo más seguro e inteligente es hacer una primera planificación del proyecto
en la que se explique cómo se va a trabajar y cuánto tiempo se asigna a cada tarea. A
medida que vayamos trabajando y avanzando en el proyecto podremos ir editando esta
planificación en función de cómo avance el trabajo.
En mi caso, he decidido llevar a cabo una planificación basada en iteraciones. En este
estilo de planificación, se reparten las tareas a realizar en diferentes iteraciones,
poniéndose una meta a realizar en cada una de las iteraciones. Una vez se finaliza una
iteración se ajustan las demás en función del resultado de la iteración actual, se mueven
tareas de una iteración a otra, se ajustan tiempos de duración e incluso se puede llegar a
desechar alguna tarea por falta de tiempo. Como ya habréis supuesto el hecho de que al
final de cada iteración sepamos si las tareas van avanzando correctamente o no, nos
permite prever futuros problemas y ajustar las iteraciones para que se adapten lo mejor
posible al nivel de trabajo actual.
He decidido agrupar las iteraciones en tres grupos muy importantes: las iteraciones de
aprendizaje, las de desarrollo del núcleo y las de añadir funcionalidades.
• Durante las iteraciones de aprendizaje me he dedicado sobre todo a recopilar
información sobre el funcionamiento de la Game Boy, y Game Boy Color, además
de realizar una serie de tutoriales en los que poco a poco iba empezando a
comprender el funcionamiento de la consola y todos sus secretos. Sin estas
primeras iteraciones no hubiera sido capaz luego de trabajar con la fluidez que
tenía, al ya saber cómo funcionaban las cosas y no tener que hacer muchas
búsquedas para solucionar mis problemas.
Página 65
• En las iteraciones del núcleo me propuse desarrollar el núcleo del juego, es decir,
programar todas las mecánicas necesarias para poder jugar a la versión más básica
del juego sin que este pierda su esencia. Esta ha sido la fase más duradera de
todas al tener que dejar las bases del juego lo mejor posible y sin errores para no
tener que corregir partes del código durante las iteraciones siguientes. Aun así,
tengo que decir, que he tenido que editar el código mucho más de lo que me hubiera
gustado al no haber preparado el código para el alto número de elementos con los
que he llegado a trabajar.
• Por último, cada iteración de añadir funcionalidades tenía una meta muy clara y
era dejar siempre un producto terminado. Es decir, partir desde la primera versión
y añadir funcionalidades sin que ninguna se quedé colgando de un hilo, todas
hechas correctamente de forma de que al acabar la iteración se pudiera volver a
jugar al juego. Aunque ha habido mucho trabajo duro por detrás, estas han sido las
iteraciones más entretenidas para mí por su alta versatilidad entre las tareas, desde
crear un nuevo jefe final, hasta diseñar un nivel con su mapa, trampas y enemigos.
A lo largo de los años he usado varias herramientas para hacer el seguimiento de un trabajo
y poder saber en qué estado se encuentran las tareas del proyecto. Algunas de estas
herramientas son muy simples, otras muy complejas, pero en el punto medio tenemos
Trello, la herramienta que he utilizado para el seguimiento del proyecto.
Trello es una herramienta online que ofrece el software necesario para la administración
de proyectos, que funciona en pc, iOS y Android. A diferencia de algunas herramientas de
este estilo, Trello permite al usuario crear todas las listas de tareas que al usuario le
parezcan necesarias, pudiendo personalizar al gusto el tablero de planificación. Además,
si te gustaría añadir cierto campo o funcionalidad a tus tareas, Trello cuenta con una alta
gama de mejoras que se pueden instalar justamente para esto.
Página 66
13.2. Iteración 1: Primeros pasos A la hora de empezar a programar el juego, no podemos hacerlo de cualquier forma, este
desarrollo conlleva un trabajo que cuanto mejor tengamos aprendidas las bases más rápido
se realizará el desarrollo. Es por esto que siempre al empezar un proyecto en un campo
nuevo debemos documentarnos bien y probar a hacer algún test para empezar. En mi
caso me decante por seguir una serie de tutoriales que explicaban el funcionamiento de
cada una de las partes de la Game Boy por separado y luego iban combinándolas poco a
poco. Sin estos tutoriales el proceso de desarrollo se hubiera convertido en un proceso
mucho más tedioso al tener que consultar constantemente el funcionamiento de las cosas.
Lo primero que aprendemos es que todo tiene un lugar predeterminado en la Game Boy,
no podemos empezar el código como más nos guste y por la dirección que nos parezca
más oportuna, sino que la Game Boy empieza su ejecución en el registro $0100, además
de que hay que insertar la cabecera correspondiente en el registro $0104, como vimos en
el punto ‘9.5. Organización de la ROM’.
En esta primera lección, aprendemos a cómo tratar con la memoria RAM y ROM, además
de inicializar la memoria de la Game Boy de forma que al ejecutar el programa se pueda
ver en el fondo el logo de Nintendo rodeado del tile que hemos diseñado. Aunque pueda
Figura 13.2.1. Cabecera de la ROM.
Página 67
parecer un comienzo un poco flojo, el tutorial plantea todas bases sobre la edición de la
memoria y deja parte del camino trabajado para los siguientes tutoriales.
Poco a poco vamos realizando cosas que van pareciéndose más a un juego, de hecho, en
los siguientes dos tutoriales aprendemos a utilizar los Sprites del juego y a almacenar el
estado de los botones de la consola. Con todo lo aprendido hasta este punto, y con un
poco de práctica en programación ya se puede sacar un juego adelante. En los siguientes
tutoriales se van centrando en cosas más específicas, como el sistema de ventanas de
Game Boy, utilizar el sistema de banking de la consola, como generar sonidos, realizar un
temporizador y adaptar el juego para Game Boy Color.
Una vez llegados al punto de haber aprendido como funciona cada una de las partes de la
Game Boy por separado, cuando me planteaba un problema se me ocurrían un par de
aproximaciones de cómo podría llegar a solucionarlo, mientras que al empezar con el
proyecto la mayoría de estos problemas que me planteaba me parecían retos imposibles
de superar con mis conocimientos. Es por esto, que como he dicho al iniciar el punto, a la
hora de empezar con un nuevo proyecto hay que dedicar cierto tiempo para aprender y
practicar los conceptos necesarios, además de que de seguro que al acabar este proceso
y recuerdes el inicio, te darás cuenta del gran cambio que has realizado.
Página 68
13.3. Iteración 2: Núcleo del juego
Una vez ya con los conocimientos básicos que he podido captar a lo largo de la iteración
de aprendizaje ya puedo centrarme en el núcleo del juego, en lo que se va a volver el
motor básico que lo hará funcionar. Este núcleo se reparte en muchos apartados
diferentes, por eso en este punto voy a explicar cómo ha funcionado el desarrollo de
cada uno de ellos, intentando remarcar aquellos apartados más interesantes y que resaltan
por su funcionamiento y cambios durante el desarrollo.
Es un hecho que cuando desarrollé el núcleo del juego lo hice con ejemplos a baja escala
para comprobar que todo funcionará correctamente. Al no hacer uso de muchos elementos
desde un principio hay muchos aspectos desarrollados que se han visto alterados en otras
iteraciones.
13.3.1. Input
Uno de los aspectos más importantes para que el juego sea jugable, es que el usuario
pueda interactuar directamente con el juego haciendo uso del hardware a su
disposición, en el caso de la Game Boy los diferentes botones que tiene y la cruceta de
cuatro direcciones. Para esto, hace falta la creación de un sistema que pueda leer el estado
de cada uno de los botones, el sistema de input.
Bit n Función Bit = 0
Bit 5 Leer el estado de los botones Seleccionado
Bit 4 Leer el estado de la cruceta Seleccionado
Bit 3 (Lectura) Estado del botón Start o Abajo Pulsado
Bit 2 (Lectura) Estado del botón Select o Arriba Pulsado
Bit 1 (Lectura) Estado del botón B o Izquierda Pulsado
Bit 0 (Lectura) Estado del botón A o Derecha Pulsado
Tabla 13.3.1. Funcionalidades del registro de input ($FF00).
Para poder leer el estado de los botones y la cruceta de la Game Boy contamos con un
registro que se encarga de ofrecernos todos estos datos, el registro $FF00. Aunque pueda
parecer que al haber 8 botones, cada bit nos vaya a dar el estado de un botón, la
información que nos da el registro no funciona de esta forma, su funcionamiento real es un
poco más complejo.
Página 69
El registro cuenta con 4 bits de lectura, lo que nos permite recibir el estado de los botones
o de la cruceta. Para cambiar el grupo de elementos de lectura contamos con el cuarto y
quinto bit del registro, estos bits nos permiten seleccionar el grupo de elementos a leer.
Después de cambiar el valor de cualquiera de estos registros hay que esperar unos ciclos
para que la consola pueda leer el valor y escribir los datos correspondientes en el registro.
Al hacer el sistema de input podemos o bien separarlo en partes de lectura y escritura,
o bien, leerlo todo a la vez haciendo que la consola se espere unos ciclos para evitar leer
datos incorrectos. En mi caso me he decantado por la opción de hacer toda la lectura de
los diferentes inputs de una vez para tener el código más ordenado y no tener que
preocuparme por modificar más adelante las diferentes llamadas que tendría en el caso de
separar el sistema de input.
Con este registro solamente podemos saber si el botón está siendo pulsado o si está suelto,
no es como las librerías más actuales que contemplan cuatro estados diferentes para cada
botón, si el botón se acaba de pulsar, si se está pulsando, si se acaba de soltar y si no está
siendo pulsado.
Uno de los problemas que he tenido con el sistema de input es que, aunque para la cruceta
me vale con la información que suministra el registro $FF00, para los otros cuatro botones
necesito saber si se acaban de pulsar, por esto he tenido que modificar el sistema de
input de forma que pueda conseguir también esta información.
Para conseguir obtener la información sobre que botones se acaban de pulsar lo primero
de todo es almacenar el estado de los botones del frame anterior para tener algunos valores
con los que comparar. Al realizar una operación XOR entre los valores del frame actual y
el anterior, solamente se quedan a 1 los valores de los botones que han sufrido un cambio,
el problema es que con esta operación se marcan tanto los botones que se acaban de
pulsar como los que se acaban de soltar. Es por esto que el valor que sale de la operación
XOR la comparo con el estado de los botones del frame actual, de esta forma soy capaz
de saber que botones han sido pulsados justamente en este frame.
Figura 13.3.1. Método de obtención de las teclas recién pulsadas.
Página 70
13.3.2. Gráficos
Otro de los pilares más importantes de un juego es el sistema gráfico, todo tiene que
pintarse y procesarse de forma correcta para que el usuario pueda disfrutar y entender las
diferentes escenas que plantea el juego. Además, en el caso de la Game Boy, no actualizar
correctamente los valores de los Sprites y el fondo puede llegar a generar errores
visuales. Hay algunos errores que pueden pasar desapercibidos por el usuario, pero hay
otros que pueden llegar a fastidiar la jugabilidad del juego, como elementos del fondo con
tiles incorrectos, o la aparición de tearing en algunos elementos.
Como ya he dicho en puntos anteriores, los elementos visuales no pueden actualizarse
cuando a uno mejor le venga, las actualizaciones de los Sprites y los demás elementos
como el fondo deben hacerse durante el periodo de V-Blank. Este periodo tampoco dura
mucho tiempo, por lo que también deberemos hacer el menor número de operaciones
posibles, por lo que a la hora de tratar con Sprites, tendremos una copia que podremos
editar en cualquier momento de sus atributos (OAM), de esta forma en un principio
solamente tendremos que copiar y pegar los valores de un sitio al otro.
Figura 13.3.2. Error visual provocado por una mala
actualización de los Sprites.
Página 71
La Game Boy permite utilizar sprites de dos tamaños diferentes, de 8x8 o de 8x16. En
un inicio los sprites del juego iban a ocupar solamente 8x8 píxeles, pero más tarde al probar
el juego en la propia consola observe que los elementos se veían demasiado pequeños,
por lo que opte por hacerlos de 16x16 píxeles. En este caso podría haber optado por montar
los objetos con 4 sprites de 8x8 o con 2 sprites de 8x16, como el número de sprites que
soporta la Game Boy está limitado a 40 opte por esta segunda opción al necesitar menos
sprites. El hecho de utilizar dos o más sprites para un mismo objeto dificulta un poco las
cosas, ya no nos vale solamente con copiar y pegar los valores, ahora tenemos editar un
poco la posición de los demás sprites respecto al primero.
Para ahorrar memoria y no tener que crear un código especial para cada tipo de elemento
que iba a haber en el juego, lo primero que hice fue crear una estructura de variables que
iban a seguir todos los elementos gráficos, de esta forma solamente tenía que hacer una
serie de métodos generales que pintasen los sprites a partir de la estructura creada. Esta
estructura consta de 5 variables, 4 de las cuales son los propios atributos del sprite, esta
última variable es un byte con atributos que me han ido haciendo falta a lo largo de
desarrollo del juego. En el apartado gráfico hay un flag que nos advierte si el sprite está en
modo espejo horizontal, ya que en este caso aparte de activar el propio flag con el que
cuenta cada sprite tenemos que intercambiar la posición de los dos sprites que componen
el elemento final.
Los métodos creados para el sistema gráfico del juego son bastante básicos que ya se
componen del método para borrar un sprite y reiniciarlo, y el método para pintar sprites,
que permite tanto pintar normal como en modo espejo leyendo el valor del flag
correspondiente. Para ahorrar memoria hice que tanto el método de pintar normal como el
de pintar en modo espejo hicieran uso del mismo método de pasar valores al sprite, lo único
que pasando valores diferentes por parámetros.
Figura 13.3.3. Imagen formada por dos
sprites de 8x16.
Página 72
Aunque llegados hasta aquí puede que ya tenemos todo lo necesario para pintar sprites
esto no es del todo cierto. Aunque la Game Boy es capaz de usar 40 sprites diferentes no
significa que durante el tiempo que dura el V-Blank de tiempo a copiar los valores de los
40 sprites, ni mucho menos. Por lo que decidí actualizar los valores de los sprites cada
dos frames, es decir, en un frame actualizaba los valores de la mitad de sprites y en el
siguiente frame actualizaba los valores de la otra mitad.
Página 73
13.3.3. Scroll
A la hora de editar los registros que controlan el scroll de la pantalla visible de la Game Boy
debemos tener en cuenta que editar estos valores supondrá un cambio visual en toda la
pantalla, por lo que al igual que los sprites, deben editarse dentro del periodo de V-
Blank. Al ser un elemento muy grande, el error visual en este caso es más notable, y por
lo tanto precisa de solución, por suerte podemos aplicar la misma técnica que con los
sprites, es decir, tener una variable de copia para cada valor.
Una vez solucionado este problema con la actualización de los valores, aún nos queda otro
que tiene que ver en cómo se relacionan la pantalla visible y el fondo. Cuando editamos
los valores del scroll estamos cambiando la posición de la pantalla visible respecto al fondo,
el problema reside en que la posición de los sprites respecto a la pantalla visible no
cambia, y entonces su posición con el fondo se descuadra.
En este caso, al editar el scroll de la pantalla, deberemos realizar el movimiento contrario
sobre todos los sprites que se estén usando, de esta forma conseguimos mantenerlos en
el sitio. Esta operación se puede realizar de forma muy sencilla gracias a que el fondo mide
32x32 tiles, o lo que es lo mismo 256x256 píxeles, es decir, el valor máximo que puede
almacenar un byte. Gracias a esto no tenemos que hacer transformaciones ni operaciones
complejas sobre los valores, nos vale con una simple suma de valores.
Figura 13.3.4. Comparativa posición Sprite con diferentes valores de Scroll.
Página 74
13.3.4. Físicas
La estructura de variables creada en un inicio para poder pintar correctamente los Sprites
durante el V-Blank tiene más funcionalidades que esa, ya que el problema no reside
solamente en escribir datos en los atributos de los sprites, tampoco se pueden leer los
datos correctamente si no estamos dentro de este periodo de tiempo, en caso de intentarlo
fuera de tiempo siempre leeremos el valor $FF. Por esto tener a mano siempre una copia
de los valores nos sirve para hacer todo tipo de cálculos en los que se pueda ver
involucrada la posición del objeto, como se da en el caso de las colisiones.
El sistema de físicas plantea dos tipos distintos de colisiones, por una parte, tenemos las
posibles colisiones que puedan tener los objetos con el mapa, y por otra la propia
colisión que pueda haber entre los distintos objetos. Ambos tipos se calculan de forma
muy diferente
Colisiones con el fondo
A la hora de calcular las colisiones con el fondo no miro a ver en que tile se está moviendo
el objeto, principalmente porque al igual la información de los sprites, los datos sobre los
tiles del fondo no se pueden acceder en cualquier momento, además de que tendríamos
una lista muy larga comparando con cada tile diferente, por lo que cada zona tiene su
propio mapa de metadatos. Los mapas de metadatos tienen el mismo tamaño que la zona
que están representando y además contiene información sobre todos los tiles del mapa,
esta información nos dice si el bloque es colisionable o no, o si tiene una funcionalidad
especial.
Página 75
Solamente con la posición del objeto no podemos hacer ninguna comprobación con el
fondo, principalmente porque se mueven en entornos diferentes. A la hora de pintar sprites
por pantalla, su posición se pone respecto a la pantalla visible, en vez de hacerlo sobre el
fondo, haciendo que saber la posición de un objeto en el fondo sea un poco dificil. Para
hacer la conversión de pantalla visible a coordenadas del fondo tendremos que restarle a
la posición del jugador los valores del scroll.
Figura 13.3.5. Metadatos de un mapa.
Figura 13.3.6. Error que pueda dar no tratar la posición.
Página 76
El siguiente problema con el que nos encontramos es que cada byte de información del
fondo y el mapa de metadatos corresponde a un tile, mientras que nosotros tenemos
la posición del objeto en píxeles. Por suerte la conversión de píxeles a tiles es bastante
simple, solamente tenemos que dividir la posición X e Y entre 8 y ya la tendremos.
Ahora que ya tenemos la posición del objeto respecto al fondo y medida en tiles, ya solo
nos falta una transformación más, esto es debido a que la forma de representación de los
datos de la Game Boy y cualquier otro elemento electrónico no es matricial, si no que están
representados de forma lineal, uno detrás de otro. La fórmula que hay que seguir para
conseguir la dirección es la siguiente:
Los objetos del juego no son solamente puntos por el mapa, es decir ocupan un
espacio, esto hace que cuando tengamos que comprobar la posición que tenemos
almacenada en memoria, que equivale a la esquina superior del sprite, además tenemos
que comparar con las otras tres esquinas. De esta forma podremos saber a ciencia
cierta si el sprite esta colisionando con algún tile solido del fondo. A modo de optimización,
y como solo se utiliza esta comprobación cuando algún objeto va a intentar moverse,
solamente se hace la comprobación en aquellas esquinas que siguen el movimiento, es
decir, si el personaje se mueve hacia arriba se comprueban las dos esquinas superiores.
De esta forma reducimos a la mitad los cálculos que tiene que hacer el algoritmo.
Colisiones entre sprites
Por otra parte, tenemos las colisiones entre los propios sprites, donde comprobamos
que dos o más sprites no compartan el mismo espacio. A diferencia de las colisiones con
el fondo, esta vez no tenemos que realizar ninguna transformación de los valores que
obtenemos de cada sprite, lo único es que vamos a trabajar con muchos más elementos al
tener que comparar con todos los sprites del juego.
Lo primero de todo es conseguir la posición de los dos sprites que vamos a comparar, el
mayor reto a la hora de comprobar si están utilizando el mismo espacio es no editar los
registros en los que se encuentra la posición del primer sprite, pues estos valores van a
hacer falta para seguir comparando con los demás elementos.
Figura 13.3.7. Conversión de posición a dirección de memoria.
Página 77
Ya teniendo almacenadas las posiciones de los dos sprites en registros diferentes, tenemos
que realizar dos comparaciones para saber si los sprites comparten espacio. La primera
comparación trabaja con el eje vertical, y comprueba si los sprites comparten el mismo
espacio en este eje, en el caso de que no lo estén ya sabemos que no hay posible colisión
entre los dos sprites, en el caso de que sí que haya esta superposición realizamos la misma
operación, pero con el eje horizontal. En este caso, si ocurre una superposición significará
que los dos sprites están colisionando entre ellos.
Figura 13.3.8. La superposición en ambos ejes es signo de colisión.
Figura 13.3.9. La superposición en solamente un eje no es signo de colisión.
Página 78
Existen dos métodos diferentes que comprueban la colisión entre sprites, cada método
devuelve un tipo de dato diferente. El método más básico, simplemente devuelve si ha
habido colisión o no entre los sprites, mientras que el segundo método, en el caso de
haber colisión devuelve la dirección del objeto con el que ha colisionado, esta función
es muy útil a la hora de hacer daño a enemigos, o intentar interactuar con elementos del
juego.
Durante el tiempo de desarrollo, el modo en el que se hacia la comprobación para cada
sprite ha ido cambiando según crecía el número de elementos con el que se iba a
comparar para intentar optimizar lo máximo posible el código. Al inicio de todo, cada sprite
contaba con su carga y su llamada al método para hacer la comparación, lo que suponía
que, añadir un elemento nuevo al juego costase 6 bytes. Más adelante, empezábamos
añadiendo todos los elementos a la pila, y luego, ir recuperándolos uno a uno para hacer
la llamada al método, de esta forma se reducía el coste de los elementos a solamente 4
bytes, pero aún se puede optimizar más. En la opción final, ya contamos con arrays de
memoria en los que están almacenados todos los sprites y el método solamente tiene que
ir recorriendo este array, mientras ejecuta la llamada al método para comprobar la colisión,
de esta forma conseguimos optimizar lo máximo posible el tamaño de cada elemento a
solamente 2 bytes, que equivalen a la propia dirección del elemento. Esta misma
transformación la han sufrido más partes del código al sufrir el mismo problema.
Página 79
13.3.5. Sonido
Aunque fue el último sistema en crearse, no por eso dejar de ser importante, de hecho, hoy
en día todos los juegos deben tener un buen sistema de sonido para que puedan contarse
como acabados, ya que, en el caso de no tenerlo, este factor afecta muy negativamente a
los juegos.
Para realizar el sistema de sonido, principalmente me ayude de una librería externa
realizada por AntonioND, la cual es capaz de reproducir canciones en la Game Boy.
Claramente, tiene limitaciones, aun así, es de lo mejor que he encontrado y es muy fácil e
intuitiva de usar. Además, cuenta con una herramienta que permite transformar
archivos .mod a bytes de forma que puedan añadirse estás canciones al juego. Ya con la
librería, lo primero que hice fue hacer una pequeña fachada para facilitarme su uso, y
además adaptarla a mi gusto personal.
Por defecto, la canción que reproduce esta librería utiliza los cuatro canales de la
Game Boy, haciendo imposible que se reproduzca cualquier tipo de efecto sonoro. Es por
esto, que limité su uso a solamente 3 canales, y así por el cuarto ya podía reproducir
estos efectos. Aunque todos los canales permiten reproducir sonido, cada canal de la
Game Boy permite hacer cosas diferentes:
• El primer canal permite hacer sonar diferentes tonos, además de poder hacer un
portamento al cambiar.
• El segundo canal funciona al igual que el primero, pero sin la característica del
portamento.
• En el tercer canal podemos programar una onda jugando con los valores de
frecuencia y forma. Este, puede hacer sonar un tono, pero no podemos editar su
envolvente como en los canales anteriores.
• El cuarto canal permite reproducir ruido blanco. Aunque estamos hablando de
valores aleatorios, podemos editar el rango de estos valores consiguiendo
diferentes sonidos.
Página 80
Decidí dejar los tres primeros canales a la música, porque estos dan muchos más juegos,
además que solamente con las opciones que permite el cuarto canal ya se pueden realizar
muchos efectos sonoros.
El método encargado de hacer sonar los efectos sonoros está muy simplificado, ya que se
encarga de leer el valor que recibe, y cargar el efecto sonoro. Finalizando, con la activación
del cuarto canal para que este emita el sonido.
Figura 13.3.10. Componentes de la envolvente de los dos primeros canales.
Página 81
13.3.6. Tipos de objetos
La estructura de variables que creamos en un principio para tener control sobre cuando
actualizar los sprites se ha convertido al final en la forma básica que tienen todos los
elementos del juego. Ya sean de mayor complejidad o muy básicas, todos los elementos
comparten esta misma estructura base.
Al compartir la estructura base hay funciones que se han podido generalizar, y hacer
que cualquier tipo de objeto pueda hacer uso de ellas. Por lo general estás funciones suelen
llegar a ser más complejas de entender y desarrollar, debido a que todos los valores que
se usan deben ser enviados a través de los registros propios del microprocesador y
tenemos que procurar que estos valores perduren hasta que se hayan utilizado y no se
vuelva a necesitar su uso.
En mi caso empecé por generalizar el método encargado del movimiento, de forma que
cualquier elemento pudiera hacer uso de él sin ningún problema. Como en un principio el
código solamente iba a usarlo el propio jugador para moverse, había muchas partes del
código en el que se cargaban variables específicas del jugador, el trabajo en este caso fue
adaptar este mismo código para que pudiera usarlo cualquier elemento. Pase de tener un
método al cual no hacía falta ningún parámetro, a otro al cual había que pasarle tanto la
dirección del objeto, como la velocidad a la que queremos que se mueva. El método, a
partir de la dirección del objeto saca su posición a la que le suma su velocidad, y
simplemente con esto ya tenemos la nueva posición del objeto, pero antes de almacenar
este nuevo valor tenemos que comprobar que no haya colisión tanto con el fondo, como
con los sprites. Como el sistema de colisiones estaba creado a partir de la estructura básica
de los objetos no hubo que hacer muchos cambios en esta parte, el único problema fue
que las colisiones con el fondo dependían del objeto, por lo que antes de utilizar esta
función hay que alterar la variable que se encarga de saber con qué tipo de elemento está
trabajando.
A parte del movimiento, poco a poco, según se iban necesitando han ido surgiendo nuevos
métodos de carácter común. Hay métodos que simplemente se dedican a devolver algunos
de los valores del objeto, como su posición, mientras que al igual que el movimiento
tenemos métodos un poco más complejos, como por ejemplo el usado para comprobar si
dos elementos diferentes están a un rango predeterminado.
Página 82
Como ya he dicho, sin hacer mucho hincapié en el tema, el juego mueve diferentes tipos
de objetos, cada uno con una funcionalidad diferente. Por una parte, tenemos al propio
player, el cual, al ser el elemento con el que va a interactuar el jugador toma un papel más
importante en comparación con los demás. Luego contamos con elementos como los
enemigos, NPCs, proyectiles y los ataques, que como he dicho reiteradamente
mantienen está misma estructura base. Para finalizar, tenemos a los props, los cuales al
no utilizar sprites de forma visible no hace falta que hagan uso de esta estructura.
Figura 13.3.11. Diferentes objetos del juego.
Página 83
13.3.7. Player
El player, al ser el elemento con el cual el jugador va a poder interactuar con el juego, es
el elemento que más lógica tiene y por lo tanto el más complejo de todos. Dentro de las
posibles acciones que puede llevar a cabo nos encontramos con el movimiento, el ataque
básico, interactuar con los NPCs, y poder cambiar de personaje.
Movimiento
El método de movimiento del player fue una de las primeras cosas que se hicieron, además
de ser de una de las que más ha cambiado durante el tiempo. Al inicio, como ya he contado,
toda la lógica del movimiento se encontraba dentro del player, aunque durante el desarrollo,
parte del método se acabó generalizando. Digo parte del método, porque al tratarse del
objeto principal del juego, este siempre debe encontrarse visible en la pantalla, y por lo
tanto mover la pantalla visible en consecuencia. Y es aquí donde reside el problema, en
conseguir un movimiento que permita ver en todo momento tanto al player como a los
demás elementos importantes.
Durante las primeras iteraciones, el player siempre estaba en el centro de la pantalla visible,
y era esta la que se iba moviendo. En este caso se tenía una gran visibilidad del mapa,
además de poder saber en todo momento donde estaba el player, el mayor problema que
tenía esta técnica era cuando nos acercábamos a los bordes del mapa, ya que como el
fondo es cíclico se podía ver parte del extremo contrario. Tampoco se podía dejar una pared
grande a los bordes porque en ese caso ocupábamos casi todo el espacio del mapa, por
lo que opte por cambiar la forma del movimiento. En esta segunda aproximación, el
personaje sí que podía moverse por la pantalla visible libremente y cuando estaba cerca
del borde, la pantalla se movía. En este caso, al reducir al mínimo la parte que se podía
ver del otro extremo sí que era viable dejar solamente un tile como pared. El mayor
problema que tenía esta aproximación es que la mayor parte del tiempo, como había que
acercarse mucho al borde para mover la pantalla visible, no eras capaz de saber que
podías encontrarte. Al final, después de probar muchas veces el juego, y realizar muchas
pruebas llegué a un resultado que fue de mi agrado. Esta vez junte las mejores partes de
cada una de las opciones anteriores, en esta versión el player siempre que puede está
en el centro, solamente cambia cuando nos aproximamos a los bordes del fondo donde el
scroll está limitado para que nunca pueda verse el contenido del borde contrario. De esta
última forma el player siempre es visible y además se puede ver lo máximo posible
del mapa.
Página 84
Personajes
Hasta ahora a lo largo del documento hemos utilizado varias veces la palabra personaje
sin llegar a explicar detenidamente a que nos estamos refiriendo. Dentro del juego,
realmente estamos controlado a uno de los personajes que hay en nuestro equipo.
Aunque al inicio empezamos solamente teniendo a un personaje en nuestro equipo, que
vendría a ser el protagonista, poco a poco nuestro equipo irá creciendo hasta el máximo
de tres. Al controlar a un personaje obtenemos sus valores de vida y daño, además de
funciones de ataque y de utilidad.
Aunque la mayoría de la lógica del sistema encargado de cambiar y añadir personajes la
podemos encontrar dentro del propio player, es en el objeto personaje donde se controlan
todas las variables, y se pueden inicializar sus valores en función del tipo de personaje
que sea. Aun así, el player contiene una copia de los valores del personaje que está
controlando para poder acceder más fácilmente a los valores.
A la hora de cambiar el personaje que se está controlando, hay que llevar una serie de
comprobaciones antes, no se puede realizar el cambio así como si nada. Lo primero y más
básico es comprobar que realmente haya más personajes con los que hacer el cambio, ya
que no siempre vamos a tener al máximo el equipo. Seguidamente se comprueba que los
personajes no estén muertos, es decir, su vida sea superior a 0. Como el player solo cuenta
con una copia del personaje actual esta comprobación hay que hacerla directamente con
la variable vida del personaje. Una vez ya hemos hecho las comprobaciones y vamos a
llevar a cabo un cambio de personaje viene la parte más interesante del código, cuando
cambiamos de personaje no cambiamos simplemente las variables del player, además
rotamos las direcciones de los personajes, es decir, el personaje 2 pasará a ocupar la
dirección del 1, el personaje 1 la del 3, y el personaje 3 la del 2. De esta forma solamente
rota las referencias a los personajes, sin tener que llegar a tocar la información de estos. A
la hora de rotar la información se puede hacer en dos direcciones distintas en función de
si queremos que se pongan en primer lugar los últimos valores, o viceversa, que los
primeros valores se pongan al final, por la forma en que tenía ordenada el código decidí
que los nuevos valores se fueran al final, además, por temas de complejidad el código
empieza la rotación desde el último valor.
Página 85
Por otra parte, tenemos el método para añadir nuevos personajes al grupo, que es muy
más simple. Lo primero, como es lógico, comprobamos si hay un hueco vacío en el equipo,
y en el caso de hacerlo inicializamos ese personaje con los valores predeterminados. En
el caso de no haber un hueco libre alteramos los valores del primer personaje.
Ataque
Debido a que cada personaje contaba con un tipo diferente de ataque, decidí separar toda
la lógica de los ataques del jugador a otro objeto, y así tenerlo todo más accesible y fácil
de consultar. Lo más complejo del ataque es su creación, ya que el update se centra en
hacer comprobaciones de colisiones, y actualizar su posición. Como he dicho, la creación
es el paso más importante, ya que tenemos que inicializar todos los valores
correctamente en función del tipo de ataque y su dirección. El primero de los problemas
que tiene la inicialización es que cada dirección que puede tomar un ataque supone unos
valores de tile y atributos diferentes, ya que por ejemplo atacar a la derecha supondrá tener
que utilizar el modo espejo horizontal, además, que al pasar por parámetros un ataque
vertical tendremos que cambiar de tile. Este proceso, podría realizarse de forma muy fácil
si separamos el código de cada dirección, pero estaría repetido el mismo código cuatro
veces con algunos valores diferentes, ocupando un espacio que podríamos gastar para
otras cosas, es por eso que opte por otra opción. Adaptar el código, de forma que
independientemente de la dirección que pueda tomar el ataque, se pueda ejecutar la
misma parte del código. Para esto, hace falta separar las direcciones vertical y horizontal
en dos parámetros diferentes donde sabemos si el valor es positivo, negativo o nulo. Al
poder tratar cada dirección de una forma distinta las cosas se facilitan mucho, además de
poderse usar el mismo código para las comprobaciones de ambas direcciones.
Figura 13.3.12. Proceso de cambio de personaje en memoria.
Página 86
13.3.8. Enemigo
Al igual que el player, los enemigos son un tipo de objetos con mucha lógica y
complejidad. Este objeto en particular me ha planteado muchos retos durante su desarrollo
que he podido resolver con soluciones de lo más diversas e interesantes. Al tener que
trabajar con el comportamiento de los enemigos, nos encontramos en que no solamente
desarrollar el algoritmo es problemático, también lo es la parte en la que se diseña este
comportamiento, ya que, si desde un principio no lo tenemos muy claro, habrá muchas
partes del código que en futuro puedan darnos más de un problema y nos obliguen a
replantearnos el código.
Existen varios tipos de enemigos diferentes, aunque podemos dividirlos entre los cuerpos
a cuerpo y los a distancia. Mientras que de enemigos a distancia solo existe un tipo, de
cuerpo a cuerpo tenemos a la bestia, al raptor y al gigante. Por la forma en que está
diseñado el objeto enemigo, no hace falta añadir nuevas variables dependiendo del tipo,
además que los enemigos de un mismo tipo comparten también la lógica. Todo esto es
gracias a los diferentes sistemas que forman el comportamiento de los enemigos.
Rutina de movimiento
Hay muchas formas de desarrollar el movimiento artificial que va a seguir un tipo de objeto
determinado. Por ejemplo, podrías hacer un algoritmo que hiciera que el objeto se moviera
siguiendo unos puntos, o también de forma pseudoaleatoria teniendo en cuenta la posición
de los demás objetos, pero estos no son nuestro caso. Partiendo de la idea que de los
enemigos siguieran siempre un camino se me ocurrió que el propio camino podría estar
almacenado en los enemigos en forma de rutinas de movimiento. Las rutinas están
formadas por diferentes pasos que forman el camino en su totalidad, en estos pasos se
tiene almacenada la información sobre cuantos píxeles hay que moverse y en qué
dirección, de forma que simplemente con seguir los pasos los enemigos siguen patrones
de movimiento.
A la hora de llevar esta idea al código la idea se complica, sobre todo por el número de
variables nuevas que hay que tratar solamente para implemente este sistema. Hace falta
guardarse el inicio de la rutina que se está siguiendo, y el paso por el que vamos, luego,
hace falta una copia de los valores de cuantos movimientos quedan y la dirección en la que
tiene que moverse.
En cada iteración el enemigo intenta moverse en la dirección que tiene almacenada de la
rutina de movimiento, haciendo las comprobaciones pertinentes de colisiones.
Seguidamente procede a comprobar si tiene que cambiar de paso, y en el caso de ser así
Página 87
actualiza la variable, teniendo en cuenta de que es cíclica, y copia los valores del siguiente
paso. Aunque no lo parece, el hecho de que, aunque no consiga moverse, se altere la
variable del número de movimientos restantes del paso, es muy útil, gracias a esto, los
enemigos siempre tienden a seguir la rutina de movimiento, aunque haya objetos sólidos
que se lo impidan.
Como los enemigos a distancia funcionan a modo de torretas que intentan bloquear el
paso a un camino, no se mueven. En un principio al no moverse estás variables usadas
para las rutinas de movimiento se estaban malgastando, así que les di una funcionalidad
diferente. En este caso, cada paso indica el tiempo entre ataques y la dirección en la que
van a atacar. De esta forma, además, como la lógica por dentro para cambiar de paso es
la misma que con el movimiento no hay que generar código nuevo.
Perseguir
Mientras que los enemigos se están moviendo según su rutina, puede ser que el player se
acerque demasiado a ellos, en este caso su comportamiento cambiará y empezarán a
seguir al player mientras que no se salga del rango. En sí, explicar el método por encima
es simple, pero por detrás hay muchas comprobaciones y cálculos internos que vamos a
ver a continuación debido a que son de interés.
Lo primero de todo, es comprobar si el jugador está dentro de su rango de visión para
empezar a perseguirlo. Para esto, lo primero que hay que conseguir es la distancia que
hay entre el enemigo y el jugador. Al que, igual que para otros cálculos donde estaba
involucrada la dirección, tomaremos cada eje por separado y así solamente tendremos que
restar una posición a la otra para calcular la distancia que hay entre los dos elementos. Ya
con esta distancia tenemos que ver si está dentro del rango. Para saber si un elemento
está a rango tenemos tres opciones diferentes.
Página 88
Por una parte, podemos comparar la distancia de cada eje con el rango por separado
consiguiendo esta forma cuadrada del área de visión. Si en cambio, queremos que el área
se parezca más a la forma de un rombo, deberemos hacer la comparación con el rango
sumando el valor de ambos ejes. Ya, por último, si queremos que el área se lo mayor
parecido a un círculo que se pueda podemos calcular las dos opciones anteriores o hacer
la comparación con la hipotenusa de los dos valores. El problema de esta última opción es
que al tener que elevar los valores al cuadrado consumiría muchos ciclos, además de que
al estar trabajando con valores muy grandes lo más seguro es que tocase trabajar con
registros de 16 bits, ya que todas las demás opciones permiten trabajar solamente con 8
bits.
Una vez ya sabemos si el enemigo está a rango del player, este debe moverse hacia él.
Para este cálculo volvemos a hacer uso de los valores de distancia que hemos gastado
antes. Estos valores, tenemos que transformarlos en 0, -1 o +1, en función de si el valor es
positivo o negativo. Ya con estos valores, se los sumamos a la posición del enemigo y
llamamos al método de movimiento. Es cierto, que es fácil de engañar a este sistema
poniéndose delante de un objeto sólido, aun así, como el rango de visión de los enemigos
tampoco es muy grande no se da mucho este caso, además de que calcular el camino más
rápido de un punto a otro supondría un gran coste computacional que no nos podemos
permitir.
Figura 13.3.12. Tipos de rango de visión de los enemigos.
Página 89
Atacar
Aunque el sistema de ataque no es muy complejo, sí que es interesante de analizar cómo
se soluciona el problema que plantean los ataques. Al haber muchos enemigos, no hay
suficientes sprites para que cada enemigo pueda mostrar su ataque, tal y como hace el
player. Es por eso que opte por hacer que los enemigos tuvieran que estar muy cerca para
poder atacar al player, y sin mostrar ningún sprite al atacar, ya que al acercarse cuerpo
a cuerpo sería muy fácil sobrepasar el límite que tiene la game boy de 10 sprites en
horizontal como máximo.
Ya a la hora de empezar a atacar, hay que hacer nuevamente una comprobación para
saber si el player está a rango de ataque. En el caso de que este a rango el enemigo
cambia su update de movimiento por el de ataque, en el que esperará un cierto tiempo,
dependiendo de su tipo, hasta completar el ataque. Si al completar el ataque el player está
cerca, este recibirá el daño del ataque, en caso contrario conseguirá escaparse.
Figura 13.3.13. Rango de visión y ataque.
Página 90
Optimización
Al principio del desarrollo, el comportamiento de los enemigos se probaba en un mapa que
solamente contaba con dos enemigos, por lo que con todos los cálculos que tenía que
hacer, el juego funcionaba a una velocidad decente. Más adelante, durante la creación
de mapas para el juego se iban requiriendo cada vez más enemigos, hasta el punto de
necesitar 7 enemigos en un mismo mapa. Al hacer funcionar el juego, este ya no
funcionaba a una velocidad decente, de hecho, la baja velocidad hacía que no fuera ni
jugable. Tuve que implementar un método de optimización para que, aun habiendo
muchos enemigos en un mismo mapa, el juego se pudiera jugar correctamente.
La técnica consiste en comprobar antes de que se haga el update si el enemigo está
dentro de un área un poco mayor a la pantalla visible de la game boy, en el caso de que no
esté dentro de la pantalla salimos del método sin hacer su update. De esta forma solamente
se están actualizando aquellos enemigos los cuales se pueden ver en pantalla, mientras
que todos los demás están a la espera de que la sección visible del mapa cambie para
poder proceder con su update.
Página 91
13.3.9. NPCs
La palabra NPC viene del inglés, Non Player Character. tal y como su nombre indica, los
NPCs son aquellos personajes que nos podemos encontrar por el juego, y que podemos
interactuar con ellos, es decir, realizar algún tipo de acción. Para saber el tipo de acción
que van a llevar a cabo, los NPCs cuentan con dos variables nuevas, una que indica la
acción que van a realizar, y la otra que la otra sirve para tener un valor de entrada para la
acción. Por ejemplo, en el caso de querer hablar con el jugador, la primera variable se
encargaría de almacenar el método encargado de mostrar al jugador texto, mientras que
esta segunda variable almacena la dirección del texto a mostrar.
Como cada NPC tiene una acción diferente a realizar, hace falta que estos tengan una
variable con el método a ejecutar. Solamente con este dato ya se podría haber programado
toda la lógica de los NPCs, ya que cada uno tendría su método con su código a ejecutar,
el problema vendría a que habría mucho código parecido que se podría juntar de alguna
forma. De aquí viene la segunda variable con la que cuentan los NPCs, el parámetro que
se le va a enviar al método. De esta forma, todos los NPCs que al interactuar con ellos te
dan cierta información comparten el mismo método a ejecutar, mientras que el parámetro
que se pasa es diferente. Esto ayuda a reducir un poco el tamaño del juego, aunque aún
hay muchos NPCs que tienen su propio método de interactuar, debido a que son casos
muy especiales, como por ejemplo el NPC encargado de abrir el camino de un lugar
específico o el que se mueve de un mapa a otro al interactuar con él.
Página 92
13.3.10. Props
Aunque casi todos los elementos del juego utilizan la misma estructura base, siempre hay
alguno que rompe esta regla. Al inicio, los props utilizaban la misma estructura que todos
los demás elementos, pero más adelante, con el crecimiento del juego hubo que cambiar
su funcionamiento, y este caso es muy especial, porque no estamos hablando de un
cambio ínfimo, aunque su funcionalidad era la misma, toda la lógica interior del elemento
sufrió un cambio, y esto es en lo que nos vamos a centrar en este punto.
Dentro del juego, los props sirven para poder bloquear caminos. En un principio al contar
con el sistema de colisiones que permite controlar si dos sprites están ocupando el mismo
espacio, la primera aproximación de todas fue hacer que los props utilizarán un sprite y por
lo tanto tuvieran la estructura básica. Este formato de prop era muy simple de programar,
ya que simplemente suponía poner un sprite, el cual no iba a moverse, en un punto
determinado. Hasta que no estuvo todo programado y funcionando correctamente no me
di cuenta de lo que iba a suponer que los props utilizaran sprites, ya que como siempre,
contamos con la limitación de 40 sprites en total y solamente 10 en horizontal. Había zonas
del mapa en las que solamente los props ya llegaban al límite de los 10 sprites en
horizontal, evitando que otro elemento pudiera ocupar la misma fila o se empezarían a ver
errores visuales.
La primera aproximación no había funcionado y por lo tanto había que desecharla, ya que
las limitaciones a las que nos enfrentábamos eran muy grandes. Desarrollé una segunda
aproximación teniendo en cuenta que los props no iban a moverse, y que buscan
mimetizarse con el fondo. En esta versión, ya no se crean sprites, de hecho, se hace todo
lo contrario, se añaden al mapa. A la hora de crear un prop, lo primero de todo es tener ya
cargado el mapa visual, junto con sus metadatos, ya que se vamos a sobrescribir estos
datos para añadir al prop. A partir de su posición, y realizando una serie de cálculos y
transformaciones, al igual que con las colisiones, podemos sacar la dirección de memoria
donde tiene que crearse el prop. El problema, es que si lo añadimos sin más en esta
dirección de memoria borraremos la información que ya hay, es por eso, que los props
cuentan con una serie de variables que les permite almacenar los datos que había en la
dirección de memoria que ocupan al crearse, de esta forma no perdemos los datos. Como
se puede suponer, a la hora de borrar el prop, es volver a calcular la dirección de memoria,
y entonces almacenar de vuelta los valores que tenemos guardados en las variables del
prop. Con esta aproximación, además, no tenemos que ir actualizando la posición de los
props con el movimiento del scroll, como tocaría en el caso de usar sprites, ahora
solamente tenemos que gestionar la creación y destrucción de estos elementos.
Página 93
13.3.11. Carga de Mapas
El juego tiene muchos mapas, y cada uno de estos cuenta con un número diferente de
elementos, y con valores únicos, haciendo que no podamos cargar los mapas de cualquier
forma, primero de todo tenemos que crear una estructura que vayan a seguir todos los
mapas para realizar un código que funcione concorde a la estructura que se haya diseñado.
La estructura que he diseñado permite crear el número de elementos que uno quiera, y
poniéndoles los valores requeridos. Lo primero de todo, son las direcciones del mapa visual
y de metadatos que se van a usar, al ponerlos dentro de la estructura nos permite utilizar
el mismo mapa visual para distintas zonas si así se requiere. Luego, tenemos una lista de
mapas con el que conecta el actual, en esta lista se tiene el mapa al que se va a ir, y el
spawner que se va a utilizar, el concepto de spawner lo veremos en este mismo punto. A
la hora de hacer jefes finales, la carga de mapas es muy diferente, por lo que hay un byte
expresamente puesto, para utilizar un tipo de cargador diferente. Después ya tenemos a
todos los elementos del juego con sus variables correspondientes, todos los elementos con
un byte para saber si esta activado o no, de esta forma es fácil editar si un elemento va a
estar en un mapa o no.
• NPC: Posición X, Posición Y, Número de Sprite, Método de interactuar, Información
para interactuar.
• Enemigo: Posición X, Posición Y, Número de Sprite, Tipo de enemigo, Rutina de
movimiento.
• Prop: Posición X, Posición Y, Número de Sprite, Metadatos del prop.
• Spawner: Scroll Y, Scroll X, Posición Y, Posición X.
Aun no se ha hablado sobre los spawners, y son un elemento muy importante de la carga
de mapas. Los spawners, marcan donde se va a poner el jugador al cargar el mapa. Por
cada punto donde puede aparecer hay un spawner diferente. Con las variables de scroll
ajustamos la posición de la pantalla visible, y seguidamente sobre esta pantalla ajustamos
la posición del player.
Página 94
El desarrollo del método para cargar el mapa fue muy difícil, ya que había que procurar en
todo momento no editar el registro HL, ya que en este se almacenaba la dirección por la
que iba la carga del mapa, y luego al cargar los diferentes elementos, tampoco se podía
editar el registro DE. Con un poco de ingenio, y adaptar un par de métodos con las
limitaciones que teníamos el método salió adelante. Pero aún no estaba completo, ya que,
al ser un método muy costoso, y que cambiaba completamente la pantalla no se podía
llamar en cualquier momento. Es por esto, que cree un método que preparaba todos los
valores para la carga del mapa con el menor coste posible y sin editar ningún elemento
visible, además de cambiar el método de update del juego, para que al siguiente frame se
cambiará el mapa. De esta forma, tenía un método que se preocupaba de apagar la
pantalla, cargar todos los datos, y luego volver a encenderla y continuar con el curso normal
del juego.
Figura 13.3.14. Estructura final de los niveles.
Página 95
13.3.12. Preparación Mapas
Como ya he explicado en puntos anteriores, la creación de los mapas se hace en su gran
mayoría a través de las herramientas GBTD y GBMB, aun así, hay un largo proceso entre
el archivo que conseguimos de estas herramientas y el que luego está dentro del
juego. Como este proceso suponía pasar horas realizando tareas mecánicas que se
podían automatizar, decidí desarrollar una herramienta propia en C++ que me ayudará en
este proceso.
Lo más esencial que hace la herramienta, es crear los metadatos del mapa a través de
la información de los tiles del mapa visual. Para hacer esta transformación, hay que
configurar un archivo donde ponemos las diferentes equivalencias. Para realizar tanto esta
función como las demás que explicaré a continuación, había que conseguir hacer un buen
tratamiento del archivo, para así conseguir la información de todos los bytes del mapa. En
este caso solamente nos dedicamos a intercambiar unos valores por otros, pero la
herramienta cuenta con otras herramientas para pasar el archivo de texto a uno binario
y viceversa, transformar un archivo binario al formato que gasta el ensamblador.
Como los mapas de por sí ocupan mucho espacio, decidí que lo mejor iba a ser
comprimirlos dentro del juego, y ya descomprimirlos cuando hiciera falta utilizarlos. La
diferencia de espacio es abismal, pasan de ocupar 1024 bytes fijos a llegar a ocupar
solamente unos 150 bytes. Para comprimir y descomprimir, utilizo el método 2 de RNC
(Rob Northen Compression), el cual es una variante del LZSS y de la codificación
Huffman. Para procesar el archivo hace falta que esté en binario, de ahí la funcionalidad
que he explicado para convertir el archivo a binario, y luego para volverle a dar el formato
de ensamblador. Por suerte también encontré, en una página de desarrolladores, el código
en ensamblador para descomprimir los datos utilizando este mismo método. Con este
código soy capaz de rellenar la información del mapa a partir de los datos comprimidos, sin
tener que ocupar el gran espacio que supondría no tenerlo comprimido.
Figura 13.3.15. Preparación de los mapas.
Página 96
13.3.13. Ventana
A parte del fondo, donde podemos mostrar los mapas, también contamos con otro elemento
propio de la Game Boy que nos permite editar los elementos que se muestran por pantalla.
Este elemento es la ventana, y en el caso del propio del juego se usa tanto para pintar el
HUD, como para mostrar el texto por pantalla, aunque se le pueden dar tantos usos
como permita la imaginación. Al igual que el fondo, la ventana tiene reservada 1024 bytes
de memoria que corresponden a los 32 x 32 tiles que puede almacenar, aunque realmente,
por el uso que se le suele dar a este elemento lo más seguro es que no se utilicen todos
los bytes reservados.
La característica más importante de la ventana es que se puede posicionar en cualquier
punto de la pantalla visible de forma permanente, aunque esta se vaya moviendo al hacer
uso de sus variables de scroll. La ventana al igual que la pantalla visible cuenta dos
variables para ajustar su posición X e Y, los valores de estas variables ajustarán la posición
de la esquina izquierda superior de la ventana. Por ejemplo, si tenemos un 0 en ambas
variables, la ventana se pintará siempre desde la esquina izquierda superior de la pantalla
tapando el fondo.
Aunque para la zona inferior y derecha de la pantalla visible es muy fácil ajustar la
ventana, ya que solo hay que cambiar las dos variables pertinentes, para los otros dos
extremos no es tan fácil, y esto junto con la poca información que se puede encontrar sobre
el tema, ha hecho que la gente acabe por generalizar el código para los casos más difíciles,
ya que estos permiten poner la ventana en cualquier punto. En mi juego, la ventana
solamente ocupa la zona inferior de la pantalla, pero como he explicado, al buscar
Figura 13.3.16. Diferentes usos de la ventana. Izquierda: Pokémon Rojo. Derecha:
The Legend of Zelda: Link’s Awakening.
Página 97
información sobre como posicionarla solo encontré ejemplos sobre el caso difícil, de hecho,
hasta que no profundice más en los registros de la Game Boy no me di cuenta de que era
mucho más simple de como yo tenía. En esta aproximación más difícil, había que hacer un
control de las interrupciones H-Blank, para activar y desactivar la ventana de forma que se
podía poner en cualquier sitio jugando con los valores del método. Este método era muy
costoso, se ejecutaba cada vez que el LCD llegaba al lado derecho de la pantalla,
pausando la ejecución que se estaba llevando a cabo, además, al tratar con interrupciones
es muy fácil provocar un error que acabe rompiendo el juego.
Pintar Texto
La primera funcionalidad que le di a la ventana fue la de poder pintar texto. Por pintar
texto, no solamente nos referíamos a ser capaces de pintar letras en la ventana, además,
estas se van mostrando poco a poco hasta llegar al límite de la ventana, esperando a
que el jugador pulse un botón para borrar lo que había y continuar pintando la frase. Aunque
las letras se van mostrando poco a poco, si se pulsa el botón A se pintan todas las letras
posibles de una vez, sin tener que esperar el tiempo que tardan en aparecer todas.
El primer “truco”, si se puede llamar así, que hay a la hora de pintar el texto, es la
organización que tienen los tiles de los caracteres. Estos no están puestos de cualquier
forma, están en las direcciones exactas para optimizar y simplificar todo el código, sus
valores ASCII concuerdan con sus números de tile. De esta forma, a la hora de almacenar
una cadena de caracteres, simplemente tenemos que poner la frase, y el ensamblador al
procesar el archivo almacenará en cada byte el valor ASCII correspondiente, y así cada
carácter concuerda con su número de tile. Al almacenar de esta forma los tiles, nos
ahorramos hacer transformaciones y operaciones sobre los valores para sacar su tile
correspondiente, optimizando el código.
Página 98
Por otro lado, a la hora de mostrar el texto lo primero que hago, al igual que al cargar un
mapa, es cambiar el método que se encarga de actualizar el juego. Principalmente lo
hago porque así es más fácil controlar el juego, además que me permite ir cambiando las
funcionalidades del input de la consola. Otro valor a recalcar hay que cambiar el método,
es que no quería que los demás elementos del juego se actualizarán mientras se está
hablando, es decir, quería que el juego se paralizase. Como ya dije, no se puede cambiar
el método en cualquier momento, por lo que hice una función que se encarga de hacer
todos los preparativos, además de guardar la dirección de memoria donde está
almacenado el texto que se va a pintar.
Como os podréis imaginar, para el texto no utilizamos todos los valores ASCII, dejándonos
una serie de valores vacíos. Aprovechando estos valores que se quedan sin tener una
funcionalidad clara, he cogido dos de estos y les he programado una cierta lógica.
• El valor 0, el cual sirve para saber cuándo se ha acabado de pintar todo el texto
por completo
• El valor 1, este sirve para dejar de pintar texto en la ventana actual, de forma que
para pintar los siguientes caracteres hay que borrar la ventana y continuar pintando
desde arriba, como haríamos de forma normal cuando la ventana llega a su límite.
Figura 13.3.17. Memoria de vídeo con todos los caracteres.
Página 99
Como la variable de inicio de la frase se puede editar, una vez la ventana llega a su fin, o
el código lee el carácter 1, alteramos este valor poniendo la dirección del ultimo valor leído.
De esta forma podemos siempre ejecutar el mismo código sin preocuparnos. Además, de
forma interna, hay otra variable que marca cuál es el siguiente valor para leer desde está
dirección.
HUD
Como ya he dicho, a parte del texto también se muestra en esta ventana el HUD del juego.
En este, se pueden ver las estadísticas básicas del grupo de personajes que se tenga,
es decir, su ataque y su vida. Como solamente cuento con dos tiles de altura, tampoco me
plantee poner muchas más variables de cada personaje, como podría ser mencionar el tipo
de ataque, en cambio, opte por hacer el HUD lo más simple y minimalista posible.
La función para pintar el HUD se divide en dos partes, por una tenemos los diferentes
adornos que hay en el HUD, y por otra ya tenemos toda la información de los
personajes. Este método está separado en dos partes para no tener que repintar aquellos
elementos que son fijos y no cambian durante el juego. Solamente se pintan cuando se
finaliza una conversación o se carga un mapa, ya que estas operaciones sustituyen los
valores que se encuentran en la ventana.
Los métodos permiten pintar la información de cada personaje cambiando los valores que
reciben, haciendo que el método ocupe mucho menos espacio, y facilitando la edición del
aspecto visual. Luego, la gran parte del código simplemente es poner los tiles en sus
lugares correspondientes, lo más complicado es pintar los datos de cada personaje sin
perder su dirección de memoria
Figura 13.3.18. Uso de los caracteres en el juego.
Figura 13.3.19. HUD del juego.
Página 100
13.3.14. Color
Aunque partimos de un juego pensado para la primera de las Game Boy, podemos
aprovechar la compatibilidad que había entre la Game Boy y Game Boy Color para
aprovechar la mayor característica de esta segunda, poder pintar con diferentes colores en
vez de la paleta de verdes clásica. Aunque en un principio pueda asustar tener que hacer
un código que funcione en ambas consolas, y además que se visualice de forma diferente,
realmente no es tan complicado gracias al funcionamiento interno de la Game Boy.
Lo primero, y más esencial que hay que hacer es editar un valor de la cabecera, el registro
$143 con el valor $80. Este registro marca si el juego es compatible con la Game Boy Color
o no, de esta forma la consola pueda hacer las comprobaciones pertinentes.
Aunque en la Game Boy Color cada tile del fondo puede utilizar una paleta diferente, esta
parte no es compatible con ambas versiones, aunque sí que podremos personalizar una
paleta para el fondo. Con los sprites, como se puede observar en sus atributos, sí que se
le puede asignar una paleta diferente a cada elemento.
Pero para asignar las paletas, lo primero que hay que hacer es crearlas. Por suerte, el
emulador BGB cuenta con un menú donde podemos editar en cualquier momento cada
una de las paletas cambiando sus valores y probando a ver qué resultado nos gusta más.
A la hora de añadirlas por código no es muy complicado, la Game Boy cuenta con un
registro donde podemos poner una a una las paletas, y la consola va moviendo los valores
a los lugares determinados para que todos queden almacenados correctamente.
Figura 13.3.20. Comparación versión monocromática y color.
Página 101
13.4. Iteración 3: Dragón Una vez terminada la segunda iteración, con la finalización del núcleo del juego, y varias
zonas jugables donde se podían probar todas las mecánicas decidí empezar a planear y
diseñar lo que vendría siendo la tercera iteración. Al ya tener enemigos básicos que no
añadían mucha dificultad al juego, decidí empezar en esta iteración a diseñar el primero de
los jefes finales que estaba descrito en el documento de diseño del juego, en este caso, el
dragón.
En esta iteración me encargue tanto de la creación y diseño del jefe, como en la creación
de todos los pasos que eran necesarios seguir hasta llegar a este.
• Poder aceptar la misión.
• Crear el nuevo camino.
• Añadir al arquero al equipo.
• Batalla con el jefe.
13.4.1. Reutilización de la memoria
Antes de empezar con el nuevo jefe, algo que ya tenía muy claro que iba a hacer, era
reutilizar los espacios de memoria que utilizan los demás elementos del juego. Pensé
en esto porque en el momento en el que transcurre la batalla final no hay enemigos, no hay
NPCs, es decir, no hay ningún elemento básico del juego que vaya a utilizar su espacio de
memoria.
Es cierto que el ahorro de memoria que se hace es mínimo, ya que en total solamente
ahorre 42 bytes de memoria RAM, y reutilice 14 atributos de Sprites. Pero esta era una
técnica que quería utilizar y ver si era viable utilizarla en el juego. Después de utilizarla y
comprobar que todo funcionaba correctamente, pienso que es una buena técnica, que,
aunque en este caso el ahorro es mínimo puede llegar a marcar la diferencia en algunos
casos, además que no añade mucha complejidad a la hora de programar, en mi caso
solamente tuve que crearme una serie de constantes en las que tenían la dirección de
memoria a reutilizar, de esta forma podía usar el nombre de las constantes y así evitar el
posible lio de nombres.
Página 102
Al no tener quejas de la técnica, en la quinta iteración, con la creación del nuevo jefe volví
a utilizar esta técnica ahorrando 52 bytes de memoria. Poco a poco el ahorro de memoria
se va notando, aunque es cierto que este ahorro es más útil al hablar de memoria ROM,
que es la que más fácilmente se acaba llenando.
Figura 13.3.21. Cantidad de memoria reutilizada para el dragón.
Página 103
13.4.2. Funcionamiento del dragón
Como quería que la batalla contra el dragón fuera lo más fluida posible, lo primero que
hice fue hacer su propio bucle de actualización del juego, de esta forma no hay que
actualizar elementos innecesarios y el juego va todo lo fluido que podría ir, además al tener
su propio bucle de actualización me permite organizar las cosas de la forma más óptima
posible. De hecho, al solamente tener que actualizar el jugador, el dragón y sus bolas de
fuego, el cambio entre el bucle normal y el personalizado se nota mucho.
Además, para que siempre se vean todos los elementos que están involucrados en la
pelea, he limitado el espacio de juego al espacio de la pantalla de la Game Boy. En un
inicio cuando el personaje se mueve, la pantalla se mueve con él, pero en las batallas está
limitado de forma que no se mueve. Esto lo hice desde el movimiento del personaje, donde
hay una serie de variables que marcan cuales son los límites del scroll, de esta forma puedo
cambiar estos límites en función del momento y del tamaño del mapa.
De forma resumida el jefe final consta de dos cabezas que se van moviendo de izquierda
a derecha mientras que van lanzando bolas de fuego contra el jugador. Este no puede
golpear cuerpo a cuerpo a las cabezas, así que tiene que utilizar al arquero para conseguir
golpearlas a distancia con sus flechas. Una vez debilitada una de las cabezas, la otra
empieza a disparar con una mayor frecuencia hasta que el jugador la derrota. A
continuación, voy a centrarme en explicar las partes más relevantes del jefe.
Movimiento
Aunque en un inicio pueda parecer que el movimiento de las cabezas del dragón es
aleatorio realmente ambas están haciendo uso de una lista de movimientos, donde esta
especificado cuanto tienen que moverse y en qué dirección. El secreto de esta lista está
en que ambas cabezas utilizan la misma, es decir, en el caso de que la primera tome el
elemento 5 de la lista, la siguiente de las dos cabezas en coger un elemento se quedará
con el sexto. De esta forma conseguimos que, con una lista de pocos elementos, las dos
cabezas se repartan estos de una forma no ordenada, pudiendo dar lugar a nuevas
combinaciones.
Para coger un nuevo elemento de la lista, no siempre hay que esperar a que llegue a 0 el
contador que tienen, en el caso de que lleguen a una pared que les impide el paso también
cogerán elementos de la lista hasta que su movimiento sea posible. Esto ayuda con la
repartición no ordenada de la lista. Por otra parte, cuando una de las cabezas muere, la
otra pasa a tener la lista para ella sola, por lo que el patrón de movimiento que ha seguido
Página 104
hasta el momento cambia, haciendo que por unos momentos la batalla se dificulte hasta
que el jugador vuelva a memorizar el nuevo patrón.
Además, como desde un principio mi mayor objetivo es que la batalla fuera lo más fluida
posible realice un nuevo algoritmo de movimiento para estas. Esto es debido a que su
movimiento está mucho más limitado que el de los demás elementos, por lo que hay
muchas comprobaciones que podía ahorrarme. De hecho, en la versión final el movimiento
de las cabezas solamente se preocupa de mover en horizontal y comprobar que no
choquen con las paredes horizontales, nada en comparación con el algoritmo que utiliza el
jugador o los enemigos.
Ataque
Para el ataque del dragón utilice una técnica parecida a la del movimiento. Al igual que
este, utilizo una lista donde esta almacenado el tiempo que hay entre ataque y ataque.
Cuando el contador llega a cero y hay que realizar un ataque, se consulta un registro
donde hay almacenado una serie de unos y ceros que determinan que cabeza es la
que va a realizar el disparo. Cada cabeza tiene un valor asignado, uno o cero, y a cada
ataque el registro rota de forma que siguen un patrón de ocho ataques, uno por cada valor
que hay en un byte. En el momento de que una de las cabezas muera, da igual el valor
que haya en este registro, la cabeza restante realizará todos los ataques.
Aunque ya tenía creado el elemento proyectil, el cual tiene una lógica parecida a la de las
bolas de fuego, decidí crear la propia clase por si en algún momento quería añadirles más
lógica. Gracias a esto, pude implementar fácilmente un algoritmo que se encargaba de
comprobar si había bolas de fuego disponibles para el disparo, y en el caso de haberlas
disparar una.
Figura 13.3.22. Rango de movimiento del dragón.
Página 105
13.4.3. Cambio de objetos
Como he ido diciendo, durante esta iteración cree nuevos elementos haciendo uso de la
reutilización de memoria, y con esto surgió un nuevo problema. Los elementos con los
que se comprobaban las colisiones estaban fijos y no podían cambiar por lo que al
reutilizar su memoria el algoritmo encargado de hacer la comprobación leía valores que no
eran y daba falsos positivos. Para este problema se me ocurrieron diferentes soluciones.
• La primera de estas era escribir un algoritmo diferente para cada situación, e ir
cambiando cual se usaba en función del momento. Esta solución implicaba escribir
el mismo código muchas veces, además había muchas partes del código que iba a
tocar cambiar para hacer que todo funcionará a la perfección.
• La segunda solución era intentar ajustar los nuevos elementos con los que ya
habían creados, de forma que los bytes que se utilizaban para la comprobación de
colisión coincidieran, y así no tener que tocar el código. Esta solución era muy poco
práctica, y además implicaba dificultar mucho la reutilización de memoria. De
hecho, con esta solución no hubiera sido capaz de reutilizar la memoria de todos
los elementos que utiliza el dragón.
• Finalmente, se me ocurrió una nueva solución, y es la que llegué a implementar. En
esta, preparo el código para que solamente compruebe los elementos que
haya en una lista, de forma que pueda cambiar los elementos de la lista en
cualquier momento. De esta forma, en vez de tener un algoritmo para cada situación
solamente tengo listas diferentes que puedo ir cambiando en función del momento.
De esta forma no se repite código, y puedo cambiar los elementos al gusto.
Página 107
13.5. Iteración 4: Preparación Sprites A la hora de planear la cuarta iteración, tuve en cuenta que coincidía justamente en una
etapa en la que no tenía mucho tiempo diario para trabajar, es por esto por lo que después
de meditarlo decidí dedicar este tiempo en realizar muchas tareas pequeñas que me iban
a ahorrar tiempo más adelante. Para ser más precisos en preparar todo el sistema
gráfico de forma de que cuando tuviera todos los tiles hechos, pudiera añadirlos sin ningún
contratiempo.
Una vez ya tenía las tareas planeadas, y sabía cómo iba a hacerlo todo, decidí pedirle a
un buen amigo el cual tiene un gran talento dibujando si me podía ayudar con el aspecto
visual del juego. De esta forma una vez tuviera todas las tareas hechas, podría añadir los
nuevos tiles al juego y darle un aspecto más acabado.
A continuación, voy a explicar los cambios más relevantes que hice, ya que, aunque hice
muchas tareas la gran mayoría eran muy básicas y sin nada importante para explicar, como
por ejemplo hacer que los sprites y los mapas utilicen grupos diferentes de tiles.
13.5.1. Tiles Reservados
Como el juego iba a tener diferentes personajes que se podían ir cambiando a gusto del
jugador decidí empezar por reservar algunos tiles a estos personajes, de forma que en
vez de tener en todo momento los tiles de todos los personajes solamente se tengan
almacenados aquellos personajes que se están usando. Así no importa el número de
personajes que exista en el juego, siempre se va a tener en memoria los tiles necesarios.
Empecé por los tiles del personaje que el jugador controla. En un principio dependiendo
del personaje se utilizaba un número de tile diferente, lo que suponía tener en memoria a
todos los personajes que se estaban usando. Como solamente se ve uno de estos, decidí
hacer que siempre se gaste el mismo número de tile, y en cuanto el jugador cambia de
personaje se cargan los nuevos tiles en el espacio reservado para esté.
Por otra parte, había que hacer algo parecido con las cabezas del HUD, pero a medida
que hacía este arreglo empezaron a surgir problemas. El mayor problema venía al cambiar
de personajes, ya que, aunque la referencia a los personajes cambiaba los tiles no, así que
en un principio era difícil saber que tile correspondía a cada personaje. Aunque me costó
tiempo, la solución era mucho más simple de lo que parecía, debía comparar la referencia
a cada personaje para así saber cuál estaba primero, segundo y tercero en memoria, ya
que los tiles de estos también estaban ordenados del mismo modo.
Página 108
Después de reservar espacio para el cuerpo visual del personaje, las cabezas del HUD y
los elementos del texto me quedaban 144 tiles libres para utilizar en el fondo. Saber este
valor es muy importante, ya que nos pone un límite a la hora de diseñar visualmente los
mapas. Sobrepasar este valor sería un desastre, ya que supondría que hay que volver a
diseñar el mapa de forma de que no exceda el valor, y como no iba a diseñar yo los mapas,
me hacía el valor para que desde un inicio mi compañero conociera el límite.
Página 109
13.5.2. Pack de Tiles
En un inicio todos los mapas utilizaban los mismos tiles para su fondo. Pero claro, utilizar
solamente 144 tiles para todos los mapas del juego iba a ser un desastre debido a que hay
lugares muy diferentes entre ellos, no va a ser lo mismo las zonas que son integras de
cueva, como el pueblo, o los interiores de las casas, por eso decidí hacer un sistema de
paquetes de forma que cada mapa pudiera utilizar un grupo de tiles diferente, y así poder
personalizar cada mapa.
Por suerte ya había realizado el cargador de mapas, por lo que solamente tuve que añadir
un par de variables para el pack de tiles que iba a utilizar el mapa. En estos packs se
encuentra tanto la lista de tiles que usarán los sprites como la lista de tiles del fondo, de
esta forma se pueden crear nuevos packs combinando estos dos elementos. De esta forma
una vez estuvieran hechos todos los tiles iba a ser un momento añadirlos al juego.
Página 110
13.5.3. Transiciones
Como desde un inicio las transiciones entre mapas quedaban muy bruscas decidí
implementar los efectos de fundido y apagado, en los que o bien los colores iban
cambiando poco a poco para realizar el fundido a blanco, o, todo lo contrario, desde blanco
van cambiando hasta conseguir tener la paleta que utiliza el juego.
Una de las partes más complicadas, es que el cambio de paletas tenía que estar
sincronizada, es decir, esperar siempre la misma cantidad de tiempo. Para esto decidí
utilizar el temporizador que tenía ya integrado. El método que diseñé es capaz de esperar
desde 20 milisegundos hasta un segundo entero. Al entrar en él, almacena el segundo y
los microsegundos en los que está y espera en un bucle hasta que pasa el tiempo
predeterminado. De esta forma soy capaz de controlar mejor el tiempo que va a estar en
espera, ya que hacer uso de un registro a modo de contador hubiera supuesto muchas
pruebas hasta llegar al valor correcto.
El cambio de paletas en el modo monocromático de la Game Boy fue muy simple de
programar, ya que solamente tenemos que preocuparnos de 4 colores diferentes. En
cambio, para el modo a color al tener que preocuparnos de hasta 32.000 colores es mucho
más complicado, no podemos simplemente poner las transiciones de cada color como en
el caso monocromático. Para solucionar este problema, encontré un algoritmo que se
encarga de aumentar el brillo de un color.
Para facilitar el trabajo, empieza pasando cada paleta de los 2 bytes que utiliza a 3, uno
para cada componente del color. Es más simple trabajar cada color por separado. Una vez
tenemos los colores por separado simplemente tenemos que sumarle un valor, y procurar
que no excedan el máximo, ya que en ese caso se verán casi negros. Una vez alterados
los colores solamente tenemos que volver a dejarlos en 2 bytes. Entender estos
algoritmos fue muy complejo, ya que en su gran mayoría utilizan instrucciones de rotación,
y no estaba nada acostumbrado a su uso, por lo que me toco estudiar estas a fondo.
Página 111
Finalmente, para hacer la transición solo aplico más o menos veces el algoritmo de brillo.
Es decir, para dejar el color casi blanco aplico el algoritmo cuatro veces, mientras que para
dejar los colores casi intactos solamente lo aplico una vez. De esta forma hago la transición
de una forma muy sencilla.
Figura 13.3.24. Transición de apagado.
Página 112
13.6. Iteración 5: Golem Una vez terminadas las tareas que tenía preparadas para la iteración anterior, y empecé a
tener un poco más de tiempo para trabajar en el proyecto empecé con la nueva iteración.
Para esta había pensado añadir un nuevo personaje al juego, además de un nuevo jefe
final. Decidí decantarme por hacer al minero, tal y como tenía explicado en el documento
de diseño, y al Golem, de esta forma podía empezar a introducir la mecánica de poder
destruir algunos bloques para liberar caminos.
Ha diferencia de la iteración anterior que duro poco más de dos semanas, para esta que
era mucho más compleja calculé por encima que me iba a llevar aproximadamente entre 3
semanas o un poco más si me retrasaba con alguna tarea, ya que la realización del jefe
anterior me había costado debido a que había que pensar nuevas mecánicas y diseñar
más a fondo el funcionamiento del jefe final.
13.6.1. Nuevo personaje: Minero
Empecé a trabajar por el nuevo personaje, porque a mí vista de entre todas las tareas que
tenía esta era de las más simples por hacer, además de que seguramente el
funcionamiento del Golem iba a cambiar en función de lo bien que quedará el personaje,
ya que en el caso de no haber conseguido destruir bloques lo más seguro es que me
hubiera decantado por realizar otro personaje de los diseñados, por suerte conseguí
realizar la tarea a tiempo.
En su gran mayoría pude reutilizar funcionalidades de los ataques anteriores cambiando
un par de valores. De hecho, la única diferencia que hay en un principio entre el ataque del
guerrero y el del minero es el tiempo de espera entre ataque y ataque, sin contar con la
funcionalidad de poder destruir bloques que esta aplicada al ataque del minero.
Para la destrucción de los bloques tenía que hacerla desde 0, ya que, aunque sí que tenía
el método para borrarlos no había ninguno que se encarga de comprobar colisiones con
los props. Esto era debido a que como ya he mencionado en puntos anteriores la colisión
de los props se hacía directamente editando los metadatos del mapa.
Igualmente, gracias a que los props igualmente hacían uso de la estructura básica
creada para los objetos podía hacer uso de las funciones encargadas de comprobar las
colisiones entre sprites. De esta forma comprobando con cada uno de los bloques que eran
destructibles podía saber cuál de todos había sido golpeado. Aunque parezca muy básico,
seguir una misma estructura de datos a lo largo del proyecto es muy útil y me ha ayudado
muchas veces a ahorrarme muchas horas de trabajo.
Página 113
13.6.2. Funcionamiento del Golem
Al igual que con el dragón he decidido separar la explicación del funcionamiento del Golem
en los diferentes aspectos que lo componen, y así poder explicar cada uno más a fondo, y
más en este caso en el que creo que cada una de las partes ha sido complicada de hacer.
Movimiento
El movimiento del Golem es el punto que más tiempo me ha llevado entre todos ya que me
ha costado mucho conseguir que sea de mi agrado, aunque aún pienso que le queda
alguna vuelta de tuerca para que este perfecto. Esto es debido a que el movimiento lo
componen diferentes submódulos, y hacer que la fusión de todos quede bien es muy difícil.
Como el movimiento del Golem es unidireccional, guarda en un registro la dirección que
está siguiendo. Como contamos con cuatro direcciones diferentes podemos almacenar el
mismo valor 2 veces en el registro, de esta forma para cambiar de dirección podemos
utilizar las instrucciones de rotación.
Para mover al Golem hago uso del algoritmo básico de movimiento que desarrollé para
todos los tipos de objetos, y en el caso de que tenga una colisión con el mapa o el jugador,
cambio la dirección de movimiento rotando el registro. De esta forma el Golem puede entrar
en un ciclo de movimiento sin llegar a pararse en ningún momento.
A parte del movimiento simple, el Golem va comprobando a cada momento si se
encuentra en un cruce. No hace falta comprobar la dirección de cada cruce, puesto que
todos comparten las mismas dos características. La posición Y de los cruces termina
en %XXX11000, mientras que la posición X termina en %XXX10000. Con esta información
Figura 13.3.25. Las 4 diferentes direcciones del Golem. La diferencia
entre cada una solamente es una instrucción de rotación.
Página 114
con solamente dos comprobaciones podemos saber si se encuentra en cualquiera de los
cruces.
En el caso de que se encuentre en un cruce, busca cual sería la dirección que más le
alejaría del jugador, y en el caso de que esa dirección este libre la toma, en caso contrario
sigue con su movimiento. Para darle un poco de ayuda al jugador, y hacer el movimiento
lo más fluido posible, el Golem no puede volver por donde ha pasado, ya que esto haría
muy difícil la tarea de alcanzarlo. En los cruces se da otro caso en el cual el Golem tiene
delante al jugador, por lo que cualquiera de los dos lados le alejaría lo mismo del jugador.
Para estos casos el Golem tiene programado tomar el lado que más le aleje de las paredes
exteriores, ya que en estas es más fácil atraparlo.
Para que fuera más difícil perseguir al Golem hice que este fuera soltando props a su
paso. De esta forma el jugador no puede perseguirlo directamente y tiene que buscar
nuevos caminos libres. Como era posible que el Golem se queda atrapado por sus propios
bloques hice que si era necesario este pudiera eliminarlos y seguir con su paso. El sistema
necesitaba hacer uso de muchos props, y en un principio el juego solamente estaba
preparado para usar 6, es por eso, que haciendo uso de la reutilización de memoria cree 7
nuevos props que el Golem puede hacer aparecer.
Ataque
Hasta este punto aún no hemos visto ninguna forma en la que el Golem es capaz de
golpear al jugador, ya que de hecho ni lanza proyectiles ni es capaz de hacer daño cuerpo
a cuerpo, ya que esto supondría tener que acercarse, y queremos todo lo contrario. Es
entonces donde entra un nuevo elemento al juego, diseñado solamente para esta batalla.
Las estalagmitas.
Estas aparecen cuando el contador que tiene integrado el Golem llega a 0, y se reinicia
cuando finalmente se ha conseguido crear una, ya que no se pueden crear estalagmitas
encima de otros elementos. Inicialmente las estalagmitas solamente son un agujero en
el suelo, por el que el Golem puedo pasar por encima, pero después de cierto tiempo, o
cuando el jugador intenta pasar aparece la estalagmita de su interior dañándole.
De esta forma el Golem consigue tanto dificultar el movimiento del jugador como dañarlo.
Estas estalagmitas nunca aparecen al lado del jugador, de hecho, está programado para
que como mínimo haya un cruce de diferencia, aun así, en el caso de que el jugador no
esté atento y se mueva muy rápido es muy fácil que acabe activando alguna de estas.
Página 115
Diferentes Fases
Al igual que el jefe del dragón, esté también tiene diferentes fases de dificultad, pero en
este caso están mucho más marcadas. En la fase inicial el Golem casi que no pone
estalagmitas que puedan dañar o bloquear el paso del jugador, haciendo que sea más fácil
acabar dañándole.
En el caso de que el jugador consiga dañar al Golem, cambiamos de fase. Al cambiar de
fase la posición del Golem y del jugador se cambia a los valores iniciales de la batalla,
además de realizar pequeños cambios al mapa, y aumentar la frecuencia con la que el
Golem pone estas estalagmitas.
En cada una de las fases el mapa cambia un poco, poniendo bloques que no pueden ser
destruidos por el jugador. Esto se hace para que cada fase sea un poco diferente, y de
lugar a nuevas formas para intentar acorralar al Golem. Para realizar estos cambios, cada
fase no tiene un mapa diferente, si no que todos utilizan el mismo, teniendo casa fase un
set de props diferentes. Por la forma en que estaba hecho el cargador de niveles fue muy
simple realizar esta tarea.
Figura 13.3.26. Agujero y estalagmita.
Página 117
13.6.3. Fin del juego
Al ya tener dos jefes finales, con sus correspondientes caminos, decidí hacer el bucle del
juego, es decir, poder jugar siguiendo un orden en las misiones, y una vez terminado el
juego poder volver a empezar.
Realizar el bucle del juego fue bastante simple, ya que todos los elementos que me hacían
falta ya habían sido diseñados con el núcleo, además, si quería volver a empezar la partida,
solamente tenía que llamar al registro $0100 que es donde se encuentra el inicio de
ejecución del juego. De esta forma todos los valores se inicializaban a sus datos iniciales.
Y como en el caso de la Game Boy no deja ni editar la ROM, ni tener datos almacenados
inicialmente en la RAM, no iba a tener ningún problema.
Para darle un seguimiento a las misiones, hice que uno de los NPCs del juego fuera
abriendo el paso a los diferentes caminos, y de esta forma, cuando se derrotaba a uno de
los jefes cambiaba desde el código la misión que este daba. Para el final del juego también
cree su propio bucle de actualización, y ahí hice que hasta que no se pulsará la tecla A no
se volviera a iniciar el juego, mostrando la pantalla de GAME OVER.
Figura 13.3.28. Pantalla de Game Over.
Página 118
13.7. Iteración 6: Animación inicial
Ya con todo el ciclo del juego realizado, y habiendo añadido los nuevos elementos visuales
al juego, solamente faltaba añadirle algún tipo de imagen inicial a modo de presentación.
Es por esto qué decidí dedicar lo que sería la última iteración para realizar una pequeña
animación que resumiera de la mejor forma la historia inicial del juego.
Lo primero de todo fue diseñar esta animación en papel.Hice un pequeño boceto de lo
que se vería, además de explicar cómo tenía pensado llevar a cabo la animación. De esta
forma una vez estuvieran todas las piezas hechas el proceso de programación sería mucho
más simple.
Como no me sobraba memoria ROM y quería darle mi propio toque personal a la
animación, decidí realizar yo los dibujos y de esta forma conseguir optimizarlos de la mejor
forma posible. Para optimizar las imágenes, me dedique sobre todo a intentar que el mayor
número de tiles de la imagen se repitiera, y de esta forma tener que almacenar un
número menor de datos. Finalmente, también acabé comprimiendo los datos para que
ocuparán lo menos posible, aun así, cada animación ocupa de media 1500 bytes. Un valor
bastante significativo.
La animación al completo se compone de 4 partes diferentes, en el momento en el que se
pulsa cualquiera de los botones o la animación finaliza salimos de esta para ir hasta el
menú inicial del juego. Cada animación se compone de los mismos elementos, una función
de inicialización, y otra de actualización, de esta forma cuando una animación termina
solamente tiene que llamar al método de inicialización de la siguiente y cambiar el método
del que se está haciendo update.
Página 119
Figura 13.3.29. Pequeños cambios realizados a una imagen para ahorra 400kB. La
imagen inferior marca las pequeñas diferencias entre las dos imágenes.
Página 120
13.7.1. Animación 1: Vista general del reino
En esta primera animación, quería que el jugador pudiera echar un vistazo general al
reino, además de aprovechar la imagen para el menú inicial. Para esto realice una imagen
donde se podía ver el castillo, el bosque y la ciudad, lo que vienen siendo los elementos
más relevantes del reino. Además, inserte un par de adornos que decoraban el cielo junto
con el nombre del juego, de esta forma la parte superior de la pantalla no se quedaba vacía.
La animación de esta parte es la más simple de todas. Después de un pequeño tiempo, el
scroll lateral de la pantalla va moviéndose hasta llegar a mostrar toda la imagen. Una
vez la imagen se muestra cambiamos a la siguiente animación.
En los cambios de animación, al tener que cargar muchos datos se apaga la pantalla. Para
que la transición entre animación y animación no sea muy brusca hice uso de los efectos
de fundido y apagado desarrollados en la cuarta iteración.
Figura 13.3.30. Animación 1: Vista general de reino.
Página 121
13.7.2. Animación 2: Bosque
Después del vistazo general al reino, hice la animación del bosque. En esta animación se
puede ver al personaje corriendo a través del bosque. Esta animación tiene un efecto
especial, y es que el bosque y el personaje se mueven por separado, además de que el
personaje pasa por detrás del fondo.
Este efecto se consigue montando con sprites el personaje, ya que estos contienen una
opción para que se pinten por detrás del fondo. Además, como quedaba un poco vacío,
inserte múltiples cambios de paleta para expresar el estrés y la velocidad del personaje.
Figura 13.3.31. Animación 2: El bosque.
Página 122
13.7.3. Animación 3: Golpe
Según la historia del juego, ahora mismo debería estar la batalla que tiene el personaje
contra los bandidos, pero esta parte la resumí poniendo solamente el golpe. Haber puesto
parte del combate hubiera supuesto un gran gasto de memoria, además de que al diseñarla
no encontraba la forma de hacer que quedará bien.
En esta animación, primero de todo se ve el bocadillo de la onomatopeya, para que
seguidamente aparezca la palabra. Para realizar esto no hago uso de dos paquetes
diferentes de tiles, ni edito el mapa visual. Lo que hago es cargar del color del fondo los
tiles correspondientes a la palabra, de esta forma cuando cargo los tiles correspondientes
a la palabra estos aparecen directamente en la pantalla. Es decir, aunque todo el bocadillo
se vea igual, por dentro cada parte del mapa hace referencia a los tiles que más adelante
rellenará la palabra.
Figura 13.3.32. Animación 3: El golpe.
Página 123
13.7.4. Animación 4: Caída a la cueva
En la última parte de la animación se muestra como el jugador se cae a la grieta. Para la
caída quería hacer un efecto de profundidad, a medida que el jugador iba cayendo en la
cueva pretendía que se fuera haciendo más y más pequeño.
La Game Boy no tiene ninguna función que permita realizar un escalado a los sprites de
forma automática, así que decidí hacer una versión simple de esta idea. Hice el mismo
personaje con diferentes tamaños, de esta forma solamente tenía que cambiar los
sprites que estaba usando.
Pero que el cambio de sprites no quedará muy brusco, realice el efecto de fundido, pero
esta vez solamente con la paleta de los sprites. De esta forma el fondo siempre se ve, y
queda un efecto que en mi opinión queda bastante bien.
Figura 13.3.33. Animación 4: La caída.
Página 124
13.7.5. Press Start
Como ya he dicho, al pulsar cualquiera de los botones o bien visualizando toda la animación
nos movemos hasta lo que viene siendo el menú inicial. En este menú vuelvo a utilizar la
imagen de la primera animación, centrándola en la zona donde hay más elementos.
Como ver solamente una imagen estática tampoco quedaba muy bien, decidí añadir la
frase Press Start con sprites. De esta forma podía añadir un parpadeo a estas que dieran
el efecto de que el juego no se ha quedado parado y sigue funcionando. Además, como
dice el texto para empezar con el juego hay que pulsar el botón Start de la consola. Una
vez se pulsa el botón podemos empezar a disfrutar del juego.
Figura 13.3.34. Pantalla Press Start.
Página 125
13.8. Cambios Ya con el juego finalizado, si echamos la vista atrás podemos ver todas las cosas que han
cambiado desde la idea inicial que estaba detallada en el documento de diseño del juego.
Hay todo tipo de cambios, desde algunos con mayor relevancia hasta otros en los que solo
se ven alterados pequeñas zonas visuales.
A mi parecer, el cambio más relevante que ha habido en el juego es la eliminación del botón
de utilidad, ya que en un principio está iba a ser una de las características fuertes del juego
junto con el cambio de personajes. En un inicio, al utilizar los controles del teclado con el
emulador no era capaz de percibir si la combinación de teclas que utilizaba era buena o
mala, una vez probé el juego directamente sobre una Game Boy me di cuenta de que
utilizar el botón Select para cambiar de personaje era muy poco accesible. Por esto decidí
quitar las utilidades, pero a su vez, hice que algunos ataques tuvieran el comportamiento
de su utilidad, por ejemplo, el ataque del minero es capaz de romper piedras.
El aspecto visual del juego también se ha visto afectado. En un principio el poblado, según
la historia, iba a ser como un pequeño poblado medio destruido formado por un grupo de
supervivientes, y al final acabó pareciendo un pueblo normal. Este cambio se realizó debido
a que era muy difícil que estéticamente quedará bien realizar un poblado con estas
características con la baja resolución de la Game Boy, al menos con la experiencia que
teníamos en este tipo de arte.
Dentro del grupo de enemigos, aunque la mayor parte de ellos sí que sale en el juego, hay
dos que no. El enemigo Escupidera sí que está programado y tiene todo su set de
funcionalidades, pero debido a que al final no llegaba a encajar bien con el diseño de los
mapas acabé por no utilizarlo. Por otra parte, debido a su complejidad no se pudo realizar
el enemigo Explosivo, aunque pienso que este sí que hubiera encajado bastante bien con
el diseño de los mapas.
Finalmente, hay una serie de cosas que me hubiera podido haber acabado y así haber
dejado un juego con un aspecto más acabado:
• La música del juego. Aun teniendo todo el sistema de música solamente hay una
canción disponible para escuchar.
• El último jefe del juego. El desarrollo de un jefe es de las cosas que más tiempo
lleva, ya que hay que pensar y equilibrar las funcionalidades que va a tener.
• Mejoras de memoria. Hay partes del juego que se podrían mejorar ahorrando
memoria, además de darle más fluidez.
Página 126
14. Conclusiones
Durante el inicio del curso, con el desarrollo de un juego para el computador Amstrad CPC
464, empecé a pillarle el gusto a programar en lenguaje ensamblador. El hecho de poder
realizar un juego con un set de instrucciones y registros limitado me parecía algo
interesante, además de que había múltiples formas de abordar cada problema, pero
siempre había que buscar la más óptima para ahorrar la mayor cantidad de bytes posible.
Esto me llevo a embarcarme a un trabajo de este tipo. Y una vez acabo el trabajo puedo
afirmar en que no me equivoque en mi elección. Durante los meses que he dedicado al
desarrollo del juego he aprendido una gran serie de nuevos conocimientos:
• Planificación y distribución de las tareas. A lo largo de todo el proyecto, he tenido
que ir planificándome todas las tareas que iba a tener que realizar, junto con el
tiempo estimado para de esta forma poder seguir un seguimiento del mismo y ver
si voy bien de tiempo. El inicio fue un poco caótico, ya que había muchas tareas por
realizar, y pocas veces me acercaba al tiempo que me había marcado, pero poco a
poco fui prediciendo mejor los tiempos, lo que me ayudo a poder concretar unas
fechas para el final de las últimas iteraciones.
• Adaptarme al entorno. Empezar a realizar el juego fue un proceso muy lento, tenía
que aprender el funcionamiento de la Game Boy. No fue hasta haber acabado el
núcleo del juego que no pude afirmar que me había adaptado a este nuevo entorno
de desarrollo, ya que dejé de revisar constantemente aspectos técnicos de la Game
Boy. Además, este aspecto se pudo notar también en que una vez adaptado al
entorno el desarrollo mejoro drásticamente.
• Aprender ensamblador. Aunque ya tenía algunos conocimientos del juego que
había realizado al inicio de curso para el Amstrad CPC, durante la realización de
este trabajo he mejorado notablemente mi conocimiento en este lenguaje. Aun no
soy un experto ya que como en todo me quedan muchas cosas por aprender, pero
puedo afirmar que esta experiencia me ha puesto por encima de muchas personas.
• No tener miedo a probar cosas nuevas. A la hora de desarrollar juegos estoy más
cómodo en estilos de juego que me permitan realizar solamente un mapa. Pero
para este trabajo, al empezar de cero decidí probar realizar juego con diferentes
mapas y fases. Aunque en un inicio no estaba muy seguro de cómo iba a realizarlo,
creo que el resultado final quedo bastante bien, aunque supuso muchas horas de
preparación y planificación.
Página 127
• Acabar un producto. Al inicio del curso me pareció una tarea casi imposible
realizar el juego de inicio a fin, incluso llegué al punto de plantearme realizar una
pequeña demo donde se pudieran ver todas las mecánicas, pero a medida que iba
realizando el nucleó del juego vi que podía realizar un producto bastante bueno con
mi nivel. Si es cierto que ha habido dos puntos por terminar, aun así, estoy muy
contento con el resultado final.
Es aquí donde creo que realmente se puede ver la evolución que he hecho como
programador y como estudiante. Al entrar en la carrera no se me hubiera ocurrido empezar
un proyecto de este tipo por mí mismo, empezar proyectos de los cuales tenía poca idea
de cómo iban a funcionar me daba miedo. A lo largo de estos cuatros años que he estado
en la universidad he podido empezar varios proyectos grupales en los que siempre se
incentivaba justamente este aspecto.
Todo ese trabajo duro, y conocimientos aprendidos han desembocado en parte en este
trabajo, junto con todos los futuros proyectos que realizaré durante mi vida. Y espero en un
futuro haber mejorado lo suficiente para poder mirar a atrás y sentirme igual de realizado
que con este trabajo.
Página 128
15. Bibliografía y referencias
AntonioND. (2009). GBT PLAYER from https://github.com/AntonioND/gbt-player
Cinabrium. (2018, August 30). Atari Lynx from https://es.wikipedia.org/wiki/Atari_Lynx
David Pello. (n.d.). Tutorial de ensamblador from wiki.ladecadence.net/
doku.php?id=tutorial_de_ensamblador
Devkits. (n.d.). Nintendo Game Boy Color Wide-Boy N64 Version, from http://devkits.
hand http://devkits.handheldmuseum.com/GBC_Wideboy.htm
Devkits. (n.d.). Nintendo Game Boy Wide-Boy (Original FamiCom versión) from
http://devkits.handheldmuseum.com/GB_Wideboy.htm
Devrs. (1999, August 28). Gameboy Tile Designer from http://www.devrs.com/gb/
hmgd/gbtd.html
Devrs. (1999, October 2). Gameboy Map Builder from http://www.devrs.com/gb/
Hmgd/gbmb.html
Gbdev. (n.d.). CPU Comparision with Z80 from http://gbdev.gg8.se/wiki/articles/
CPU_Comparision_with_Z80
Gbdev. (n.d.). Video Display from http://gbdev.gg8.se/wiki/articles/Video_Display
Jeff Frohwein. (2000, February 29). Fade Out v1.0 from http://www.devrs.com/gb/files/
fade.txt
Joseaperez. (2018, August 11) Sega Game Gear from https://es.wikipedia.org/wiki/
Sega_Game_Gear
Retroinvaders. (2011, August 12). GameBoy emulador: Diferencias entre el Zilog Z80 y
el procesador Sharp LR35902 from https://retroinvaders.com/es/18509/diferencias-entre-
el-zilog-z80-y-el-procesador-sharp-lr35902
Página 129
Rvbelzen. (n.d.). The z80 Instruction Set from http://rvbelzen.tripod.com/z80prgtemp/
z80prg04.htm
Natesh Narain. (2016, September 09). Gameboy LCD Controller from
https://nnarain.github.io/2016/09/09/Gameboy-LCD-Controller.html
Nintendo. (n.d.). Game Boy. Datos técnicos from https://www.nintendo.es/Atencion-al-
cliente/Game-Boy-Pocket-Color/Informacion-del-producto/Datos-tecnicos/Datos-tecnicos-
619585.html
Nintendo. (n.d.). Take a look behind-the-scenes with design documents from The Legend
of Zelda! from https://www.nintendo.co.uk/News/2016/December/Take-a-look-behind-the-
scenes-with-design-documents-from-The-Legend-of-Zelda--
1169414.html?utm_medium=social&utm_source=twitter&utm_campaign=Zelda&utm_cont
ent=illustrations
Teknoplof. (2011, October 11). Cómo se hacían los videojuegos en los años ochenta
from http://www.teknoplof.com/2011/10/11/como-se-hacian-los-videojuegos-en-los-anos-
ochenta/
Tirodvd. (2015, January 27). Coloring Gameboy – Layers from https://tirodvd
.wordpress.com/2015/01/27/coloring-gameboy-layers/
Sabbut. (2018, September 1). Game Boy from https://es.wikipedia.org/wiki/Game_Boy
Speccy. (2010, December 10). Lenguaje Ensamblador del Z80 (I) from https://wiki
.speccy.org/cursos/ensamblador/lenguaje_1
Zilog. (2016). Z80 CPU from www.zilog.com/manage_directlink.php?filepath=docs/z80/
um0080&extn=.pdf