+ All Categories
Home > Documents > 1. Justificación y Objetivosrua.ua.es/.../1/...para_Game_Boy_Galahad_Escape_NAVARRO_MARTI… ·...

1. Justificación y Objetivosrua.ua.es/.../1/...para_Game_Boy_Galahad_Escape_NAVARRO_MARTI… ·...

Date post: 09-Apr-2020
Category:
Upload: others
View: 2 times
Download: 0 times
Share this document with a friend
130
Transcript

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 43

Figura 11.2.2. Interfaz ZBrush.

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 106

Figura 13.3.23. Fase de la batalla contra el dragón.

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 116

Figura 13.3.27. Diferentes fases de la batalla contra el Golem.

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

Página 130

16. Anexo


Recommended