trabajo fin de grado - rua, repositorio institucional de...

185
1 Grado en Ingeniería Multimedia Trabajo Fin de Grado Autor: Alejandro Roca Vande Sype Tutor/es: Francisco José Gallego Durán Septiembre 2019

Upload: others

Post on 12-Sep-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

1

Grado en Ingeniería Multimedia

Trabajo Fin de Grado

Autor:

Alejandro Roca Vande Sype

Tutor/es:

Francisco José Gallego Durán

Septiembre 2019

Page 2: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

2

Page 3: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

3

1. Justificación

Desde el momento que entré en la carrera de Ingeniería Multimedia, sabía desde un principio

cual era mi objetivo: dedicarme en un futuro al desarrollo de videojuegos de manera

profesional. Es por ello que, en el último curso de este grado, realicé el itinerario de Creación

y Entretenimiento Digital el cual consistía en realizar un videojuego durante todo el año

junto con otros compañeros de la carrera.

Dentro de este itinerario, se encontraba la asignatura de Videojuegos I donde los primeros

meses consistía en realizar un videojuego en ensamblador para el ordenador Amstrad CPC.

Fue en esta asignatura, donde empecé a aprender a programar en ensamblador y a

comprender lo que consistía realizar un videojuego con las limitaciones que presentaba este

ordenador de 8 bits: espacio de memoria limitado, uso de un lenguaje de programación que

no proporciona ni ayudas de gestión de memoria ni ninguna función previamente integrada

que realice operaciones o tareas de una manera más sencilla o de forma automática.

Según iba aprendiendo a programar este lenguaje de bajo nivel, me iba dando cuenta de lo

útil que era, ya que es el programador el cual tiene el control en todo momento de todo

aquello que está realizando, ya que debajo de este nivel de programación no hay nada más

que el código máquina en el que el ordenador interpreta las instrucciones que nosotros

escribamos.

Todo esto te permite saber gestionar mucho mejor el espacio de memoria del cual se dispone

y a optimizar de una manera más efectiva los programas y/o funciones que se realizan. Ya

que, en caso de no conseguir optimizar lo máximo posible, con los pocos recursos y espacio

de memoria que por lo general disponen estos ordenadores y consolas antiguas, se

convertiría en algo bastante complejo el proceso de conseguir hacer un videojuego jugable.

Por otra parte, aprender ensamblador me permitió entender mejor el funcionamiento de

algunos conceptos de más alto nivel como son los punteros y así como, muchos otros

aspectos relacionados con el almacenamiento de datos, por lo que lo considero algo esencial

para poder programar de manera mucho más eficiente en lenguajes de más alto nivel como

podría ser C o C++.

Page 4: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

4

Índice

1. Justificación ...................................................................................................................... 3

Índice de Figuras ........................................................................................................................ 7

2. Introducción ................................................................................................................... 10

3. Marco Teórico ................................................................................................................. 12

3.1. Análisis de Videojuegos previos similares ……………………………………………………………..12

4. Objetivos .......................................................................................................................... 26

5. Metodología .................................................................................................................... 28

6. Creación de una ROM básica .................................................................................... 30

6.1. Introducción ………………………………………………………………………………………………………….. 30

6.2. Bancos y ranuras de la ROM …………………………………………………………………………………. 31

6.3. Validación de la ROM …………………………………………………………………………………………… 33

6.4. Gestión de interrupciones ………………………………………………………………………………………. 35

6.5. Inicialización de la pila y el VDP …………………………………………………………………………… 36

6.6. Liberación de la VRAM ………………………………………………………………………………………… 39

7. Diseño del videojuego …………………………………………………………………………………………… 42

7.1. Descripción general ……………………………………………………………………………………………….. 42

7.2. Historia ………………………………………………………………………………………………………………….. 43

7.3. Género y Audiencia ……………………………………………………………………………………………….. 43

7.4. Ámbito …………………………………………………………………………………………………………………… 43

7.5. Jugabilidad …………………………………………………………………………………………………………….. 44

7.6. Mecánicas ………………………………………………………………………………………………………………. 44

7.7. Enemigos ……………………………………………………………………………………………………………….. 48

7.8. Niveles …………………………………………………………………………………………………………………… 50

7.9. Controles ……………………………………………………………………………………………………………….. 52

8. Desarrollo del videojuego …………………………………………………………………………………….. 53

8.1. Prototipo Mágica ……………………………………………………………………………………………………. 53

8.1.1. Código básico para la ROM ……………………………………………………………………………….. 55

8.1.2. Dibujado de un sprite y un fondo por pantalla …………………………………………………….. 58

8.1.3. Lectura de la entrada del jugador por teclado ………………………………………………………. 66

8.1.4. Organización del proyecto y makefile …………………………………………………………………. 70

Page 5: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

5

8.1.5. Dibujado de un sprite y fondo creados desde cero ....................................................... 72

8.1.6. Generalización de las funciones de dibujado ............................................................... 77

8.1.7. Disparo del jugador ...................................................................................................... 80

8.1.8. Colisiones entre sprites ................................................................................................. 86

8.2. Replanificación................................................................................................................. 89

8.3. Demo jugable ................................................................................................................... 91

8.3.1. Colisiones con el mapa ................................................................................................. 91

8.3.2. Gravedad ...................................................................................................................... 95

8.3.3. Control del tiempo mediante VSYNC .......................................................................... 97

8.3.4. Inicialización de los datos .......................................................................................... 100

8.3.5. Cambio de mapas ....................................................................................................... 104

8.3.6. Muerte del jugador ..................................................................................................... 107

8.3.7. IA primer tipo de enemigos ........................................................................................ 109

8.3.8. Recibir daño ............................................................................................................... 117

8.4. Mecánicas extra .............................................................................................................. 118

8.4.1. IA segundo tipo de enemigos ..................................................................................... 118

8.4.2. Puzles ......................................................................................................................... 124

8.4.3. Contador de tiempo .................................................................................................... 126

8.4.4. Menú .......................................................................................................................... 132

8.4.5. Animaciones ............................................................................................................... 134

8.5. Contenido ....................................................................................................................... 136

8.5.1. Búsqueda de materiales .............................................................................................. 137

8.5.2. Diseño de niveles ....................................................................................................... 139

8.5.3. Diseño de la pantalla de muerte y menú ..................................................................... 139

8.6. Reducción del espacio ocupado en ROM ....................................................................... 141

8.6.1. Precisión subpíxel ...................................................................................................... 142

8.6.2. Codificador de mapas ................................................................................................. 144

8.6.3. Aumento del espacio disponible en ROM .................................................................. 148

8.7. Implementación del final del juego ................................................................................ 150

8.7.1. Creación de los niveles finales ................................................................................... 151

8.7.2. Creación de la pantalla de fin del juego ..................................................................... 154

8.8. Mejoras y arreglos .......................................................................................................... 156

8.8.1. Efecto de parpadeo de sprites ..................................................................................... 156

8.8.2. Música ........................................................................................................................ 159

Page 6: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

6

9. Conclusiones................................................................................................................. 161

10. Bibliografía y referencias ......................................................................................... 163

11. Glosario .......................................................................................................................... 167

12. Anexo 1. Detalles técnicos de la Sega Master System .................................. 169

12.1. Información general ………………………………………………………………………………………… 169

12.2. CPU ………………………………………………………………………………………………………………… 171

12.3. Mapa de memoria ……………………………………………………………………………………………. 173

12.4. Sistema básico de entrada-salida (BIOS) …………………………………………………………. 175

12.5. Video Display Processor (VDP) ………………………………………………………………………. 177

Page 7: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

7

Índice de Figuras

Figura 1. Portada del videojuego Alien 3. ....................................................................................... 12

Figura 2. Alien 3 para la SMS. ........................................................................................................ 14

Figura 3. Portada de Ninja Gaiden. ................................................................................................. 16

Figura 4. Ninja Gaiden para la SMS. .............................................................................................. 17

Figura 5. Portada de Alex Kidd in Shinobi World. ......................................................................... 18

Figura 6. Alex Kidd in Shinobi World para la SMS. ...................................................................... 19

Figura 7. Portada Master of Darkness. ............................................................................................ 20

Figura 8 Master of Darkness para la SMS. ..................................................................................... 21

Figura 9. Portada de Zillion. ........................................................................................................... 22

Figura 10. Zillion para la SMS. ....................................................................................................... 23

Figura 11. Portada de Operation C. ................................................................................................. 24

Figura 12. Operation C para la SMS. ................................................................................................ 25

Figura 13. Definición de ranuras. .................................................................................................... 31

Figura 14. Definición de bancos. .................................................................................................... 32

Figura 15. Definición de las ranuras y bancos que contendrá la ROM. .......................................... 33

Figura 16. Definición de la etiqueta SDSC. .................................................................................... 34

Figura 17. Definición de la dirección de memoria en la cual se escribirá el código. ...................... 35

Figura 18. Gestión de interrupciones en la SMS. ............................................................................ 35

Figura 19. Gestión del botón de pausa en la SMS. ......................................................................... 36

Figura 20. Valores iniciales para los registros del VDP. ................................................................ 37

Figura 21. Se envía los valores iniciales del VDP al puerto de control. ......................................... 38

Figura 22. Dirección de VRAM y orden a realizar. ........................................................................ 39

Figura 23. Vaciado de la VRAM. ................................................................................................... 40

Figura 24. Mensaje de ¡Hola Mundo! mostrado por pantalla. ........................................................ 40

Figura 25. Sprite del personaje principal. ....................................................................................... 46

Figura 26. Sprite de vida. ................................................................................................................ 47

Figura 27. Sprite de llave. ............................................................................................................... 47

Figura 28. Sprite del cristal. ............................................................................................................ 47

Figura 29. Sprite de puerta. ............................................................................................................. 48

Figura 30. Sprites del enemigo a melé. ........................................................................................... 49

Figura 31. Sprites del enemigo a distancia. ..................................................................................... 50

Figura 32. Bioma de zona cuevas. .................................................................................................. 50

Figura 33. Bioma de bosques. ......................................................................................................... 51

Figura 34. Niveles creados para el primer bioma. ........................................................................... 51

Figura 35. Niveles creados para el segundo bioma. ........................................................................ 52

Figura 36. Controles de Invasion para la SMS. ............................................................................... 52

Figura 37. Mágica para Amstrad CPC. ........................................................................................... 54

Figura 38. Racer para la SMS. ........................................................................................................ 55

Figura 39. Configuración de las ranuras y bancos para el prototipo. .............................................. 56

Figura 40. Inicialización de los registros del VDP para el prototipo. ............................................. 56

Figura 41. Direcciones de la paleta de colores (CRAM). ............................................................... 58

Figura 42. Cargado de la paleta de colores a los sprites. ................................................................ 59

Figura 43. Valores de la paleta de colores para un Sprite de Sonic usando BMP2TILE. ............... 60

Figura 44. Copia de los datos de los índices de tiles a la VRAM. .................................................. 61

Page 8: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

8

Figura 45. Carga de un tilemap de un mapa en VRAM. ................................................................. 62

Figura 46. Buffer del Sprite Attribute Table. .................................................................................. 63

Figura 47. Representación del SAT de la SMS. .............................................................................. 63

Figura 48. Copia de los índices de tile del Sprite al buffer del SAT. .............................................. 64

Figura 49. Copia del buffer al SAT. ................................................................................................ 66

Figura 50. Sprite y fondo del tutorial dibujados por pantalla. ......................................................... 66

Figura 51. Zona de memoria para la gestión de interrupciones en SMS. ........................................ 67

Figura 52. Información sobre los bits correspondientes al Joystick P1. ......................................... 68

Figura 53. Control del movimiento hacia la derecha del jugador. .................................................. 69

Figura 54. Ejemplo de uso de la directiva”. section” de WLA DX. ............................................... 71

Figura 55. Makefile básico para poder generar un archivo ".sms". ................................................ 72

Figura 56. Tilemap creado con Tiled para representar un nivel de Mágica. ................................... 73

Figura 57. Editor de memoria del emulador MEKA. ...................................................................... 75

Figura 58. Dibujado de Sprites antes y después de resolver el problema con BMP2TILE. ........... 76

Figura 59. Zona de datos del Player mediante “enum”. .................................................................. 78

Figura 60. Función para actualizar la coordenada X del Sprite en el SAT utilizando IX. .............. 79

Figura 61. Función para actualizar la coordenada Y del Sprite en el SAT usando IX. ................... 80

Figura 62. Dibujado de 2 Sprites: Player y bala. ............................................................................. 81

Figura 63. Función de borrado de la bala. ....................................................................................... 83

Figura 64. Movimiento hacia la derecha del Sprite de la Bala........................................................ 84

Figura 65. Actualización de todos los aspectos del Sprite de la bala. ............................................. 85

Figura 66. Comprobación de si la 2º entidad está situada la izquierda de la 1º entidad o no.......... 88

Figura 67. Planificación abril (preproducción). .............................................................................. 90

Figura 68. Calendario Mayo (producción). ..................................................................................... 91

Figura 69. Zona donde se almacena la información relacionado con el tilemap. ........................... 92

Figura 70. División entre 8 del valor almacenado en el registro A. ................................................ 93

Figura 71. Acceso a la fila correspondiente donde se encuentra el tile a obtener del tilemap. ....... 94

Figura 72. Acceso a la columna del tile del tilemap. ...................................................................... 95

Figura 73. Tablas para implementar el salto del jugador. ............................................................... 96

Figura 74. Función para controlar la caída del personaje hasta colisionar con el mapa. ................ 97

Figura 75. Contador de frames y de tiempo mediante VSYNC. ..................................................... 98

Figura 76. Uso del tiempo para controlar la frecuencia de disparo del jugador. ............................. 99

Figura 77. Ejemplo de creación de un struct. ................................................................................ 101

Figura 78. Creación struct game_object player. ............................................................................ 101

Figura 79. Símbolos generados automáticamente con el struct del player. .................................. 102

Figura 80. Ejemplo copia datos con ldir. ...................................................................................... 103

Figura 81. Proceso de cambio de nivel del juego. ......................................................................... 105

Figura 82. Función para borrar los sprites de la pantalla. ............................................................. 106

Figura 83. Función para "matar" todas las entidades del nivel. .................................................... 106

Figura 84. Función para decrementar la vida del jugador una unidad. ......................................... 108

Figura 85. Comprobación del estado del enemigo. ....................................................................... 111

Figura 86. Función para comprobar si hay que eliminar al enemigo o no. ................................... 111

Figura 87. IA enemigos estado reposo. ......................................................................................... 113

Figura 88. Movimiento de los enemigos. ...................................................................................... 115

Figura 89. Update para más de un enemigo. ................................................................................. 116

Page 9: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

9

Figura 90. Función para preparar la bala del enemigo para ser disparada. ................................... 121

Figura 91. Reseteo de la posición de la bala en función de la posición y dirección del enemigo. 122

Figura 92. Función para comprobar la colisión de la bala de los enemigos con el jugador. ......... 123

Figura 93. Función para efectuar el cambio de nivel con las puertas. .......................................... 126

Figura 94. Tilesheet de ejemplo que contiene los números a dibujar. .......................................... 127

Figura 95. Definición de una variable de más de dos bytes con WLADX. .................................. 128

Figura 96. Función para inicializar el reloj de tiempo al valor 300. ............................................. 129

Figura 97. Carga del tile correspondiente del sprite de los números en la posición correcta del

buffer. ............................................................................................................................................. 131

Figura 98. Dibujado del reloj de tiempo y el contador de vidas en Invasion. ............................... 132

Figura 99. Modificación del sprite a mostrar por pantalla mediante los índices de tile. ............... 135

Figura 100. Tilesheet con sprites para el protagonista. ................................................................. 138

Figura 101. Fondo para pantalla de muerte. .................................................................................. 139

Figura 102. Fondo para la pantalla del menú. ............................................................................... 140

Figura 103. Ejemplo estructura fichero que contiene los tiles de los mapas. ................................ 144

Figura 105. Sección encargada de gestionar lo que ocurre cuando se detecta un valor distinto. .. 146

Figura 106. Función para escribir en el fichero de salida. ............................................................ 147

Figura 107. Comparación fichero entrada/salida. Arriba: fichero sin comprimir. Abajo: fichero

comprimido. ................................................................................................................................... 147

Figura 108. Ejemplo de mapa con tiles simétricos creado con Tiled. ........................................... 150

Figura 109. Diseño del nivel 7 que contiene escaleras para dar al exterior. ................................. 151

Figura 110. Uso de la instrucción “jr c” y “jr nc” para hacer comprobaciones de mayor y menor de

un valor........................................................................................................................................... 152

Figura 111. Diseño del nivel final donde se encuentra el portal. .................................................. 153

Figura 112. Cambio de estado de mundo a Fin del Juego. ............................................................ 155

Figura 113. Pantalla de Fin del Juego. .......................................................................................... 155

Figura 114. Borrado de un sprite del SAT. ................................................................................... 157

Figura 115. Función para desplazar al enemigo cuando este recibe daño. ................................... 158

Figura 116. Una de las funciones incluidas en la librería PSGlib. ................................................ 159

Figura 117. Paso de información al puerto $bf del VDP. ............................................................. 180

Page 10: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

10

2. Introducción

Desde la década de los 40, fecha en la cual se creó por primera vez lo que hoy en día se

considera como videojuego, el sector de los videojuegos ha ido creciendo en popularidad

poco a poco, hasta tal punto de que, a día de hoy, los videojuegos son considerados como

uno de los sectores que más beneficios generan, los cuales se ven incrementados año tras

año.

Actualmente, el sector de los videojuegos es mucho más variado y grande a como lo era hace

varias décadas. Hoy en día se puede encontrar multitud de lenguajes de programación, cada

uno con sus ventajas e inconvenientes, y así como, diferentes motores gráficos, consolas,

procesadores etc.

Sin embargo, es durante la década de 1980 cuando la fiebre de los videojuegos creció

considerablemente, en una etapa que se conoce como la edad de oro de los videojuegos.

Todo esto gracias al lanzamiento de juegos como Space Invaders o las máquinas

recreativas que se podían encontrar por cualquier sitio en aquellos tiempos, hicieron que los

ingresos generados por la industria crecieran considerablemente.

Durante esta época, crear un videojuego era un proceso bastante difícil y esto se debía

principalmente por dos motivos: En primer lugar, el acceso a la información no era tan

sencillo a como lo es hoy en día, ya que la principal fuente de información residía en los

libros y no se podía acceder de manera sencilla a Internet para obtener la información que se

necesitaba, además de que el Internet como hoy lo conocemos no existía por aquel entonces.

En segundo lugar, construir un videojuego requería de una habilidad elevada por parte del

programador ya que debía adaptarse a las limitaciones del ordenador o consola para la que

creaba el videojuego. La principal limitación a la que debían hacer frente residía en el

espacio de almacenamiento disponible en la máquina, el cual era bastante reducido y esto

requería que el programador tuviese que aprender a implementar técnicas que le permitiesen

reutilizar partes de código con el objetivo de ahorrar el máximo espacio posible.

Es por esta razón, por lo que he decidido realizar un videojuego para una consola antigua

ya que considero que crear un videojuego para una máquina con tales limitaciones me

permitirá mejorar mis habilidades como programador y así como aprender muchos otros

conceptos que hasta ahora desconocía o no manejaba del todo bien.

Page 11: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

11

El tener tan poco espacio disponible me obliga a tener que aprender a crear un código que

sea reutilizable por diferentes funciones y que ocupe el menor espacio posible. Todo este

trabajo que debo realizar para crear un videojuego en tales condiciones, no tiene otro

resultado que el de perfeccionar y mejorar mis habilidades en lenguajes de programación

que se utilizan hoy en día, de tal manera que pueda conseguir realizar programas o

funciones que sean lo más óptimas posibles.

Por otro lado, también he decidido no utilizar un lenguaje de programación actual, como

podría ser C++, para crear el videojuego en cuestión. Considero mucho más rentable para

mí el utilizar lenguajes de programación de la época que son de más bajo nivel que los

actuales. Esto quiere decir que no habrá nada más por debajo (excepto el código máquina)

de este lenguaje y, por lo tanto, todo lo que realice estará totalmente controlado por mí y no

realizará nada de lo cual yo desconozca. Además, utilizar un lenguaje de bajo nivel me

permite entender de mejor manera conceptos actuales como pueden ser los punteros u otros.

También considero que me resultará mucho más beneficioso el aprender a manejarme con

lenguajes de bajo nivel en aspectos como a la hora de encontrar trabajo en el futuro en

cualquier industria de videojuegos, ya que estoy completamente seguro que tendrá mucho

más valor una persona que ha realizado un juego en lenguaje ensamblador que uno que lo

ha realizado, por ejemplo, en Unity con C++, ya que existirán una mayor cantidad de

personas que sepan trabajar con este último.

El lenguaje que utilizaré para crear el videojuego no es otro que el famoso Z80. Esto es

debido a que, durante esta etapa de los 80 fue también cuando se lanzó el Zilog Z80, un

microprocesador de 8 bits que se popularizó gracias a su uso en ordenadores como Spectrum,

Amstrad CPC u ordenadores de sistema MSX. Al ser el más famoso, es el microprocesador

que tendrán la mayoría de las máquinas de esa época y, por lo tanto, el lenguaje más

asequible a la hora de poder encontrar información con la que poder trabajar.

Finalmente, en cuanto a la consola u ordenador sobre la que crear el videojuego, me he

decantado por la Sega Master System, la cual, aunque no tuviese mucho éxito durante su

época, es una consola con la que aún no había trabajado y tras ver sus especificaciones

técnicas y el catálogo de juegos del cual dispone, decidí que el videojuego del proyecto sería

sobre esta consola de 8 bits.

Page 12: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

12

3. Marco Teórico

En este apartado se va a realizar un pequeño estudio de algunos de los videojuegos antiguos

que han sido creados para la consola SMS o para cualquier otra con características técnicas

similares a esta. Concretamente, se tratarán aquellos videojuegos que pertenezcan al género

de plataforma/acción ya que es el tipo de género que tendrá el videojuego que se pretende

crear en este proyecto.

El objetivo de este análisis o estudio, es el de intentar averiguar qué mecanismos o técnicas

han sido utilizadas para conseguir los distintos aspectos jugables que contienen los

videojuegos que han sido estudiados, de tal manera que, se pueda estimar y decidir qué

características incluir y cuales no en el videojuego del proyecto. Todo esto basándose en

función del coste que suponga realizar dichas mecánicas y el tiempo del cual se dispone para

realizar el proyecto.

3.1. Análisis de Videojuegos previos similares

A continuación, se encuentran todos aquellos videojuegos que, tras realizar una pequeña

revisión de los mismos, se consideran de utilidad o del mismo parecido que el que se

pretende crear en este trabajo:

• Alien 3 – Master System

Juego de plataformas y acción de desplazamiento

lateral basado en la película de Alíen 3 de 1992. El

jugador controla a la protagonista de la película,

Ellen Ripley, mientras avanza por la colonia

Fiorina 161. Su objetivo es el de rescatar a unos

prisioneros que se encuentran en esta prisión

espacial.

Dentro del juego, el jugador puede realizar la

acción de saltar para poder avanzar por los niveles,

disparar para eliminar a los aliens con la

peculiaridad de que todas las armas disponibles

funcionarán si tienen munición, subir por escaleras,

abrir puertas etc.

Figura 1. Portada del videojuego Alien 3.

Fuente: www.gamefaqs.com

Page 13: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

13

El motivo de elección de este juego es debido a que se pueden encontrar diversas

mecánicas que pueden servir de referencia para implementar en el videojuego del

proyecto. La idea es que el protagonista utilice un arma a distancia como herramienta

para eliminar a los enemigos, al igual que ocurre aquí en Alien 3.

Por esta razón, como ambos juegos utilizan un arma a distancia, se puede tomar como

referencia para determinar aspectos como la velocidad y dirección de disparo. En Alien

3, se puede realizar un disparo hacia cualquier dirección, incluyendo en diagonal, y,

además, la velocidad de disparo simula la de una ametralladora real, por lo que la

cadencia de disparo es bastante elevada. Esto no es algo que se desee integrar en el

proyecto, pero sirve para tomarse como referencia. Igualmente, el aspecto de la munición

obliga al jugador a no malgastar los disparos, lo cual es algo curioso que no se ha

observado en ningún de los otros juegos analizados. Sin embargo, este aspecto no se ha

llegado a considerar a introducir en el juego del proyecto, ya que se pretende que el

jugador no tenga que preocuparse de aspectos como la munición restante del arma.

Por otro lado, la mecánica de disparo a distancia, permite asimismo observar el tipo de

enemigos a los que puede hacer frente el jugador y la manera que este debe afrontarlos

para eliminarlos. No obstante, en Alien 3 no se ha llegado a observar una gran variedad

de enemigos, todos utilizan la misma mecánica de moverse a gran velocidad y reducir

esta cuando reciben un disparo del jugador.

Como se ha comentado previamente, en Alien 3 el jugador puede utilizar más de un

arma distinta, lo que permite cambiar la jugabilidad del juego en función del arma que

se esté manipulando. Sin embargo, no es un aspecto que se considere a introducir por el

motivo principal del tiempo disponible para el proyecto. Incluir varias armas resultaría

en crear un diseño de niveles y de enemigos mucho más complejo para que se pueda

observar así la diferencia real de utilizar un arma u otra y que no resulte en un uso

indiferente de cualquiera de ellas.

Finalmente, la mecánica más interesante a observar en Alien3, es el uso de granadas.

Una de las ideas principales del juego a realizar es que el jugador pueda manejar granadas

para poder eliminar a los enemigos. Esta mecánica permitiría añadir una mayor variedad

a la jugabilidad del videojuego y se considera mucho más simple a realizar que añadir

diversas armas donde cada uno de estas debería tener una implementación distinta lo que

deriva en mayor requerimiento de tiempo de desarrollo para realizarlas.

Page 14: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

14

Figura 2. Alien 3 para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Hasta este punto se encontrarían las mecánicas “clave” de Alien 3, es decir, aquellas que

son exclusivas de este juego y no se han llegado a encontrar en los otros analizados.

Además de estas, se encuentran mecánicas que, como se observará posteriormente, son

típicas de los videojuegos de plataformas de esa época. La única diferencia que se podrá

encontrar en estas mecánicas es la forma en la que estas se implementen, pero la idea es

la misma en todos.

Dentro de este apartado, se encuentran mecánicas tales como el reloj de tiempo, el cual

es un contador de tiempo que determina el tiempo restante que le queda al jugador para

terminar el nivel en cuestión. Al finalizar el nivel, se suman los puntos correspondientes

a la puntuación total en función del tiempo que le haya sobrado al jugador. Al comenzar

el siguiente nivel, se realiza un reinicio de dicho contador a su valor inicial.

El apartado del reloj, es un apartado que se va a implementar, pero la manera en la que

se va a realizar será diferente. Se encontrarán diferencias como que el contador mostrará

solo los segundos y que no se reiniciará al acabar el nivel. Sin embargo, el problema aquí

reside en averiguar cómo se realiza realmente esta mecánica.

Page 15: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

15

En función de los conocimientos ya aprendidos sobre los aspectos técnicos de la consola,

se entiende que su funcionamiento reside en el cambio de los sprites que representan los

números, es decir, cada vez que transcurra un segundo se cambiará el sprite del número

que sea necesario al sprite que represente el siguiente número. Aunque por supuesto, está

implementación se basa en mi conocimiento y es probable que en Alien 3 se haya

realizado de una manera distinta, pero considero que la propuesta planteada puede ser

también válida a realizar.

También se puede encontrar el aspecto de la puntuación, que añade al videojuego una

mayor rejugabilidad. La tabla de puntuación brinda al jugador el interés de volver a jugar

al juego con el objetivo de mejorar la puntuación previamente obtenida en la partida

anterior. Esta mecánica se puede implementar de una manera similar que la del contador

de tiempo, con la diferencia de que se realizará el cambio de los sprites necesarios en

función de los puntos que se añadan lo cual complica levemente la implementación de

la mecánica ya que hay que realizar previamente la suma de los puntos obtenidos a la

puntuación total y después asignar los sprites correctos para que muestren el valor

resultante de la suma por pantalla.

Como última mecánica a examinar, se encuentra la de salto, una mecánica común en

cualquier videojuego de plataformas. Para esta mecánica, hay que fijarse en aspectos

como la velocidad del salto o la de caída, con el fin de obtener una representación lo más

idéntica a la real y que el jugador sienta que puede llegar a controlarlo de manera

correcta. Desconozco la manera de cómo se habrá implementado en Alien 3 o en

cualquiera de los otros videojuegos investigados, pero una forma podría ser mediante el

uso de una pequeña tabla de datos que almacene los valores a sumar o restar a la posición

Y del personaje en cuestión. De esta manera, lo único que habrá que realizar es recorrer

los valores e ir sumándolos a su posición hasta que se llegue al último valor.

Page 16: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

16

• Ninja Gaiden – Master System

Ninja Gaiden es un videojuego de plataformas de

scroll lateral y de género de acción. Fue

desarrollado por SIMS y lanzado en 1992 para la

SMS. No es el primer juego de la saga y, por lo

tanto, contiene muchas mecánicas parecidas a sus

versiones predecesoras en NES.

Además, de las mecánicas comunes de juegos de

plataformas como saltar, atacar o trepar, también

se pueden utilizar habilidades especiales para

eliminar a los enemigos, lo cual hace que se

reduzca un contador y si este se encuentra a 0 no

puede volver a usar la habilidad hasta que se

vuelva a recargar con los objetos que encuentra

por el nivel.

El motivo principal por el cual se ha decidido incluir a Ninja Gaiden en los videojuegos

a analizar, es debido a su interfaz. Al contrario que en Alien 3, donde se implementa una

interfaz mucho más simple en la cual nunca se llegan a visualizar al mismo tiempo más

de dos elementos distintos de la misma, en Ninja Gaiden se utiliza una interfaz mucho

más compleja y variada, donde se puede encontrar información de todo tipo. Desde la

habilidad especial disponible en el momento hasta una barra de vida que se utiliza para

cada uno de los enfrentamientos con los jefes finales.

Figura 3. Portada de Ninja Gaiden.

Fuente:

www.wikipedia.org/wiki/Ninja_Gaiden_

(Master_System)

Page 17: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

17

Figura 4. Ninja Gaiden para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Esta interfaz, como se observará posteriormente, es una interfaz que se puede encontrar

en los juegos de plataformas de esa época. Sin embargo, este tipo de interfaz presenta

tanto ventajas como desventajas. Por un lado, es evidente que este tipo de interfaz

proporciona al jugador una mayor información sobre todo lo que ocurre en el juego. Esto

permite que su primera impresión ya sea positiva y que visualmente el juego resulte más

atractivo. Además, se proporciona un mayor “feedback” al jugador, ya que este recibe

mucha más información sobre lo que está ocurriendo dentro del juego.

Sin embargo, al tener una interfaz tan compleja y variada, esto deriva en que se tiene un

menor espacio disponible para el dibujado del resto de sprites que se vayan a utilizar en

el juego. Se desconoce cómo se ha implementado realmente está interfaz, pero se intuye

que todas aquellas partes que se modifican deben ser sprites y por lo tanto debe ocupar

espacio en el SAT (tabla de sprites). Esto deriva en un menor espacio disponible para

poder dibujar al mismo tiempo, por ejemplo, un mayor número de enemigos u objetos.

Se asume que las partes que no varíen, como las letras de “SCR” o “TIME” para el

contador de tiempo, vienen integradas conjuntamente con el dibujado del mapa, por lo

que no ocuparían espacio en el SAT sino en ROM.

Page 18: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

18

Por estos motivos, se considerará utilizar este tipo de interfaz siempre que sea posible

manejar tal cantidad de sprites al mismo tiempo. En caso de que no sea posible, se optará

por una interfaz más sencilla que permita el correcto dibujado de todos los sprites que se

deseen.

• Alex Kidd in Shinobi World – Master System

Videojuego desarrollado por Sega y lanzado en

1990, en el cual el protagonista debe rescatar a su

novia de un malvado ninja. Para ello, el fantasma de

un antiguo ninja se fusiona con él otorgándole

poderes. Gracias a esto, el protagonista utiliza una

espada, la cual sirve no solo para matar a sus

enemigos, si no, también, para romper partes de

paredes y cofres. Dentro de estos cofres, se puede

encontrar desde vida extra hasta otra arma a utilizar

por el jugador.

El único aspecto atractivo de este videojuego y por

el cual se ha decidido analizar, es la mecánica de los

cofres. Como ya se ha explicado, mediante el arma

del protagonista se puede romper los cofres que

aperecen por los niveles para obtener diferentes ventajas. La idea es integrar este aspecto

para el videojuego del proyecto, utilizando esta mecánica de romper cofres para obtener

objetos que mejoren las habilidades del personaje o simplemente proporcionen salud.

Igualmente, se puede utilizar esta mecánica para que proporcione granadas en el caso de

que se haya terminado por integrar el aspecto de granadas.

Desde mi punto de vista, considero que es bastante sencilla de implementar la mecánica

de los cofres. Una forma de implementar podría ser la siguiente:

1. Se dibujan los cofres como sprites, no como parte del fondo de esta manera se

podrán borrar posteriormente.

Figura 5. Portada de Alex Kidd in

Shinobi World.

Fuente: www.wikipedia.org

/wiki/Alex_Kidd_in_Shinobi_World

Page 19: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

19

2. Una vez dibujados, se implementaría una función que comprobase si el arma del

jugador colisiona con el sprite del cofre. Dicha función, solo se llamará cuando

el jugador realice la acción de atacar. En el caso del videojuego del proyecto, al

tratarse de un arma a distancia, se lanzaría la función cuando se realice el disparo

del arma.

3. Si resulta que colisionan ambos sprites, entonces el sprite del cofre dejaría de

dibujarse por pantalla y en su lugar se sustituiría por el sprite de la mejora que se

quiera proporcionar al jugador. Siempre teniendo en cuenta el espacio disponible

en el SAT y que no se solapen ninguno de los valores del SAT del nuevo sprite

aparecido con los valores de los sprites que ya estaban dibujados.

Por otro lado, al final de cada nivel surgen 2 puntuaciones: la puntuación total que lleva

conseguida el jugador en la partida y la puntuación más alta obtenida. La cuestión es

que, como se observará más adelante, hay más juegos que utilizan este sistema de 2

puntuaciones, pero en ningún momento se llega a entender cómo funciona realmente. La

cuestión reside en que no se cree posible que dicha puntuación se almacene una vez que

la consola es apagada. Se desconoce si es posible llegar a conseguir guardar datos del

jugador si la consola se desconecta. La única opción que se considera posible es que la

puntuación haga referencia a partidas anteriores siempre y cuando la consola no se haya

apagado, si no que al terminar el juego se haya reiniciado la partida.

Figura 6. Alex Kidd in Shinobi World para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Page 20: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

20

Finalmente, a diferencia a como ocurría en Ninja Gaiden, se puede observar una interfaz

muy simple, en la cual la única información que se proporciona al jugador es la salud

restante del personaje. En los momentos de enfrentamiento contra un jefe final de nivel,

también aparece la salud del mismo en la pantalla la cual utiliza el mismo formato de

dibujado que la del protagonista.

• Master of Darkness – Master System

Videojuego de plataformas desarrollado por SIMS y

lanzado por Sega en 1993. Es un juego muy parecido

al Castlevania de la NES, donde el jugador entra en el

rol de un psicólogo que trata de derrotar a Drácula, el

cual está detrás de numerosos asesinatos en Londres.

Como ya se ha comentado anteriormente, una de las

principales dudas relacionadas con el juego a realizar

para el proyecto reside en la manera a implementar el

sistema de armas y es por esa razón que varios de los

juegos analizados se deben principalmente por la

forma a que estos implementan este sistema. En el

caso de Master of Darkness, el protagonista jugable

tiene un arma principal, que es un arma a melee, y

un arma secundaria, que es un arma a distancia con

munición limitada.

Sin embargo, lo curioso de este sistema de armas reside en que, durante el transcurso del

juego, el jugador puede llegar a encontrar armas diferentes a utilizar, pero al recoger una

de esas armas esta se sustituye por la que el jugador tenía anteriormente equipada. Esto

implica que el jugador no puede llevar varias armas del mismo tipo, obligándole a elegir

en base de lo que le resulte más útil en cada momento.

Figura 7. Portada Master of Darkness.

Fuente: www.wikipedia.org

/wiki/Master_of_Darkness

Page 21: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

21

Figura 8 Master of Darkness para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Por otro lado, en este videojuego también se hace uso de bombas y se utilizan de una

manera muy parecida a cómo podrían usarse las granadas en el juego del proyecto.

Cuando se lanza una bomba en Master of Darkness, el Sprite ejecuta una especie de

parábola hasta caer al suelo o colisionar con un enemigo, en cuyo caso explota

inmediatamente eliminando al enemigo que este próximo.

Se desconoce actualmente de qué manera se habrá implementado ese movimiento

parabólico. Se intuye que para realizar este movimiento se utilizará una función que haga

mover el sprite de la bomba tanto en X como Y. La velocidad a la que se moverá el sprite

en el eje horizontal será siempre constante, sin embargo, en el eje Y será donde la

velocidad irá variando conforme transcurren los segundos, pasando de una velocidad que

aumentará progresivamente hasta que, trascurridos los segundos necesarios, empezará a

decrementar también progresivamente hasta colisionar con el enemigo o parte del mapa.

Page 22: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

22

Otro de los aspectos a destacar en este videojuego de la SMS, reside en el apartado de la

IA. Si se observa de manera general el funcionamiento de los enemigos, se puede

observar que la mayoría no utiliza una Inteligencia Artificial muy compleja (o al menos

no lo aparenta). Por ejemplo, existe un tipo de enemigo que son los perros, estos siguen

un patrón muy sencillo: se desplazan de un punto a otro durante una cantidad de tiempo

y tras esto, cambian a un patrón de reposo, en el cual se quedan parados durante otro

intervalo de tiempo, el cual al terminar vuelven otra vez al patrón de movimiento y así

sucesivamente. Si colisionan con el jugador durante su movimiento este recibe daño.

También se encuentra otro ejemplo de enemigo que se desplazan de un punto a otro

repetidamente y de vez en cuando realizan un disparo hacia delante. Aparentemente, este

disparo parece aleatorio y no sigue ningún patrón, ya que, tras observarlos varias veces,

suelen disparar sin tener siquiera al jugador dentro de su campo de visión

También se encuentran enemigos fantasmas que se desplazan por el nivel sin colisionar

con el entorno y cuando detectan al jugador atacan desplazándose en una línea recta hacia

la dirección del jugador y si colisionan con él, este recibe daño.

Este IA que se utiliza en Master of Darkness es una IA que aparentemente no parece

demasiado compleja. Prácticamente todos los tipos enemigos se pueden implementar con

una simple máquina de estados la cual cambie permita cambiar el comportamiento del

enemigo cada vez que ocurra cierto aspecto o cada X segundos. Es por ello, por lo que

la IA de los enemigos del videojuego del proyecto se realice de esta forma.

• Zillion – Master System

Videojuego de plataformas desarrollado por Sega que

fue lanzado en 1987. Zillion está ambientado en un

mundo espacial donde existe una fuerza de

mantenimiento de la paz dentro del Sistema Planetario,

la cual desea destruir una base malvada y para ello el

personaje principal, denominado JJ, deberá infiltrarse

en dicha base y recuperar los cinco disquetes para

ingresar así la secuencia de autodestrucción en el

ordenador central de la base. Figura 9. Portada de Zillion.

Fuente: www.wikipedia.org

Page 23: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

23

De todos los videojuegos analizados hasta el momento, Zillion es de los que posee una

de las mecánicas más simples, pero a la vez más divertidas y que ofrecen un

“gameplay” bastante entretenido. En primer lugar, la mecánica de combate, como acabo

de comentar es bastante simple y sencilla, pero sin embargo bastante divertida desde mi

punto de vista. El protagonista puede situarse de pie, agachado o tumbado, y en

cualquiera de las tres formas puede disparar su arma para elimnar a los enemigos o

destruir ciertos objetos del entorno. Además, estas tres posiciones permiten al jugador

esquivar los disparos de los enemigos, los cuales también pueden agacharse para disparar

e incluso disparar hacia arriba por si el jugador se encuentra situado por encima de ellos

lo que da mucho juego.

Figura 10. Zillion para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Es probable que esta mecánica de combate se tome como referencia para ser integrar en

el videojuego del proyecto, ya que como ya se ha comentado varias veces, no se

considera muy compleja y, sin embargo, es bastante divertida.

Page 24: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

24

Otro motivo por el cual Zillion es motivo de análisis en este proyecto, es debido a la

posibilidad de mejorar el arma principal. Al contrario que en juegos anteriores en los

que se podía utilizar más de un arma, aquí solo se dispone de una única arma a distancia,

la cual va siendo mejorada durante el trascurso del juego mediante accesorios que se

pueden encontrar por los niveles, pudiendo así mejorar aspectos como mayor daño o

mayor cadencia de disparo.

Al momento de observar esta mecánica, automáticamente se consideró bastante útil ya

que abre la posibilidad a añadir variedad al “gameplay” del juego y a la vez resulta mucho

más simple de implementar que, por ejemplo, añadir diversas armas que puedan ser

utilizadas. De esta manera, combinando mecánicas como la de los cofres que

proporcionan ayudas al romperlos, una posibilidad podría ser que solo haya un arma

disponible durante todo el juego y que los cofres pudiesen proporcionar mejoras

relacionadas con el arma. Mejoras tales como disparar más rápido o que las balas hagan

más daño, podrían ser alguno de los ejemplos. Además, con esta mecánica si se desea

añadir una mejora nueva al arma en algún momento dado será mucho más simple y

requerirá una menor cantidad de tiempo que el añadir una segunda arma utilizable.

• Operation C – Game Boy

Es un videojuego de Acción desarrollado por Konami

en 1991 para la Game Boy. En Operation C el jugador

toma el control de Bill Rizer, el cual debe destruir una

fuerza enemiga que almacena de manera secreta

alienígenas en su base.

Aunque se trate de un videojuego para una plataforma

distinta que la Sega Master System, el motivo único y

principal por el que está incluido en la lista reside en

su temática.

La idea para la temática del proyecto se basaba en una historia ambientada en la época

de la guerra de vietnam y que el protagonista fuese un soldado con características

parecidas al famoso personaje de Rambo. Características que se parecen mucho a las que

incluye el juego analizado en estos momentos y por el cual se toma como referencia para

coger ideas sobre diseño y sprites.

Figura 11. Portada de Operation C.

Fuente: www.wikipedia.org

/wiki/Operation_C_(video_game)

Page 25: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

25

Figura 12. Operation C para la SMS.

Fuente: www.youtube.com/WorldofLongplays

Page 26: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

26

4. Objetivos

El objetivo principal de este proyecto es el de realizar un videojuego para la consola de 8

bits, la Sega Master System. Además, el lenguaje de programación que se va a utilizar para

conseguir dicho objetivo es el que se utilizaba originalmente para la consola, el ensamblador

Z80.

Por otro lado, existen una serie de objetivos que también se pretenden lograr. En primer

lugar, se explicará el proceso de creación de una ROM básica, de tal manera que sirva

como base para poder crear cualquier videojuego para la SMS y que este sea total y

completamente funcional en una consola real y no solo en los emuladores.

En segundo lugar, como ya se puede observar en el apartado de marco teórico, se realizará

una investigación de videojuegos que se consideren similares con el que se pretende crear

en este proyecto. De ese modo, se analizarán todos aquellos videojuegos ya existentes para

la consola que puedan tener cierto parecido o que tengan mecánicas similares a las que tendrá

el videojuego del trabajo o que se consideren interesantes a incluir. Todo esto con el fin de

realizar una pequeña investigación que permita averiguar los métodos empleados en cada

uno de ellos para conseguir implementar todas aquellas mecánicas que se consideren

interesantes a integrar en el proyecto actual.

En tercer lugar, se explicará, con el mayor detalle posible, todo el proceso de desarrollo

que conlleva realizar un videojuego para la Master System. El objetivo es describir todo

lo que se ha ido efectuando durante el desarrollo y, así como, todos los posibles problemas

que hayan podido surgir y como se han solucionado en el caso de que se haya encontrado la

solución.

Finalmente, se escribirá un pequeño anexo al final de este documento donde se explicará

algunos de los aspectos técnicos de la consola Sega Master System. El objetivo de este

anexo es el de proporcionar mayor información sobre algunos aspectos técnicos de la consola

que se puedan comentar a lo largo de este documento y así facilitar el entendimiento tanto

como a mi persona como a la persona que lea el documento.

Page 27: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

27

De manera resumida, todos los objetivos que se pretenden conseguir son los siguientes:

• Realización de un videojuego que funcione en Sega Master System.

• Mejorar los conocimientos de lenguaje ensamblador, concretamente, el lenguaje

que se utiliza para la CPU de la SMS, el Z80.

• Investigar sobre aspectos técnicos de videojuegos similares: Descubrir como

realizan ciertos aspectos y que técnicas utilizan en algunos videojuegos que tengan

características similares al videojuego del proyecto.

• Detallar el proceso de creación de una ROM básica para Sega Master System:

formato, cuestiones técnicas, herramientas disponibles, problemas encontrados etc.

• Pequeño anexo con detalles técnicos de la consola Sega Master System.

Page 28: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

28

5. Metodología

El tipo de metodología que se ha aplicado a este proyecto está basado en las metodologías

de tipo ágil. Aunque por lo general el uso de este tipo de metodologías suele darse en

proyectos en grupo con el objetivo de trabajar de manera colectiva y obtener el mejor

resultado posible, es necesario aplicar un estilo de metodología ágil (Scrum, Cristal etc.)

ya que me enfrento a un proyecto sobre un tema del cual tengo poco conocimiento previo

del mismo al tratarse de una consola con la que nunca he trabajado anteriormente.

No se ha aplicado un estilo de metodología en concreto, sino que se ha tomado la idea por

la cual se fundamentan todas las metodologías ágiles existentes que, aunque diferentes entre

ellas, todas se basan en seguir un desarrollo iterativo durante el cual se van añadiendo

funcionalidades al producto o proyecto.

Esto quiere decir que el proyecto es dividido en diferentes etapas o iteraciones. Al finalizar

cada iteración se realiza una pequeña revisión de la misma con el objetivo de encontrar fallos

o bugs que hayan podido surgir. Sin embargo, al tratarse de un proyecto del cual se tiene

poco conocimiento previo, este está en constante modificación y lo que se realizó en la

primera etapa puede que no se mantenga igual en iteraciones siguientes debido a que cuanto

más tiempo transcurra trabajando en el proyecto más conocimientos se tendrán sobre el

mismo y se pueden detectar errores que inicialmente no se descubrieron.

Teniendo esto en mente, la planificación del proyecto se ha dividido en diferentes

iteraciones:

1. Fase de Investigación y pruebas: Durante esta etapa, se analizarán los aspectos

técnicos de la consola Master System con el objetivo de aprender sus limitaciones y

puntos fuertes para averiguar así que aspectos podrían ser posibles de implementar y

cuales no dentro del tiempo disponible. Además, durante esta fase también se

realizarán pequeñas pruebas sobre la consola y algún pequeño prototipo para ir

viendo cómo es trabajar con ella y los posibles problemas que se podrían encontrar.

Page 29: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

29

2. Fase de preparación: Una vez terminado la primera fase de investigación, comienza

la fase de preparación de la idea del videojuego. Para ello, primero se hará un

pequeño análisis de los videojuegos creados para la misma plataforma durante la

época, con la intención de obtener ideas que puedan ser aplicadas al proyecto y

averiguar de qué manera podrían haber sido implementadas. Asimismo, una vez

analizados los videojuegos necesarios, se comenzará a realizar un pequeño GDD para

describir en qué consistirá el videojuego a crear y todo lo que se pretende

implementar en él.

3. Fase de desarrollo: Etapa de mayor duración durante la cual se realizará el

videojuego del proyecto mientras, a su vez, se va documentando todo lo realizado en

la memoria.

4. Fase de revisión y finalización: Etapa final que consistirá principalmente en

terminar todos los apartados de la memoria del trabajo que hayan quedado sin

finalizar y de realizar una revisión general del documento. Además, también servirá

para corregir fallos o bugs que puedan haber surgido en el videojuego.

Se trata de una planificación muy genérica, pero al tratarse de un proyecto el cual está en

constante evolución y modificación es posible que esta planificación se vea modificada

durante el transcurso del proyecto. Sin embargo, con esta pequeña planificación ya se puede

tener una idea de cómo se dividirá el trabajo del proyecto y permitirá avanzar de una manera

más gestionada y, por lo tanto, más rápida.

Page 30: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

30

6. Creación de una ROM básica

6.1. Introducción

Si se quiere realizar un videojuego para la Sega Master System, primero es necesario crear

una ROM válida para que el juego pueda funcionar en una consola real. Esta explicación

toma como referencia el tutorial realizado por Maxim en SMSPOWER [23].

En esta sección, se va a explicar el proceso para crear una ROM básica para la SMS que

contenga las instrucciones necesarias para que la ROM funcione de manera correcta.

Algunas de estas instrucciones servirán para aspectos como la inicialización de los registros

del VDP, la configuración de los bancos y las ranuras, para vaciar la VRAM etc. De esta

manera, al finalizar esta sección, se tendrá una pequeña ROM que funcionará en una SMS

y, que, a su vez, servirá como base para poder crear futuros juegos.

Antes de comenzar a explicar el proceso, conviene indicar las herramientas que son

necesarias para poder crear la ROM:

• Un editor de texto para poder escribir todo el código necesario para crear la ROM.

El que se ha utilizado para esta explicación es Sublime Text 3.

• Un ensamblador que permita convertir el fichero escrito en lenguaje ensamblador a

código máquina para que este pueda ser entendido por la consola. El ensamblador

que se va a utilizar para realizar dicho cometido es WLA DX [12], debido a que este

software proporciona multitud de directivas útiles a la hora de programar que

facilitará el trabajo de creación del proyecto.

• Un emulador para poder probar que la ROM funcione correctamente. El emulador

que se ha utilizado para este ejemplo es MEKA [8], aunque también hay otros como

el Emukon que también puede servir.

Además de estas herramientas, en este mismo documento se encuentra disponible un

pequeño anexo con información extra sobre aspectos técnicos de la consola de Sega Master

System que sirven para completar toda información que no sea descrita en las siguientes

líneas. Asimismo, también hay disponible un pequeño glosario con algunos de los términos

que se puedan encontrar a lo largo del documento.

Page 31: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

31

6.2. Bancos y ranuras de la ROM

Lo primero que se debe realizar, es indicar a la ROM como de grande va a ser, es decir, se

debe definir el número de bancos que se desea tener y el tamaño de cada uno de estos. Esto

es obligatorio debido a que es necesario saber con cuantos bancos se va a trabajar en la ROM

para obtener así el tamaño total del cartucho y, además, como se observará posteriormente,

esto permite también escribir el conjunto de instrucciones de código en distintas secciones

que pueden ser intercambiadas en todo momento.

Por otro lado, también es necesario definir el número de ranuras disponibles, es decir, hay

que indicar en cuantas ranuras o bloques va a ser dividido el espacio de direcciones del Z80.

La definición de estas ranuras es necesario debido a que es a donde se van a insertar los

bancos procedentes de la ROM del cartucho donde se tendrá escrito el código del juego.

Dicho todo esto, primero se va a definir el mapa de memoria. Para ello, se debe utilizar la

primera directiva disponible en WLA DX [12], denominada memorymap.

Figura 13. Definición de ranuras.

Fuente: WLA DX [12].

Si se observa la Figura 13, mediante slot se realiza la definición de una ranura y la dirección

de inicio desde donde esta comenzará. Pueden ser definidas hasta 256 ranuras (desde la

ranura 0 hasta la 256). Además, antes de definir la ranura, se especifica el tamaño de la

misma en bytes mediante slotsize.

Page 32: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

32

Por otro lado, también existe la directiva defaultslot. Esta es utilizada para definir la ranura

que se quiera tener por defecto, de manera que, si se define un banco, como se observará

ahora, sin indicarle la ranura en la cual va a ser insertado, entonces será insertado en la ranura

que se haya indicado en defaultslot.

Sabiendo esto, se puede observar que en la Figura 13 se está estableciendo que la SMS

contenga 2 ranuras: La primera empezará en la dirección $0000 y tendrá un tamaño de 8 kb

(2000 bytes) y la segunda ranura empezará en $2000 y tendrá un tamaño de 24 kb (6000

bytes). Además, se establece la ranura 1 como la ranura por defecto.

Una vez definido las ranuras que se van a tener, hay que definir el mapa de bancos de ROM.

Para ello, se utiliza la directiva rombankmap, que permite describir los bancos que va a

contener la ROM de una manera parecida a como se realiza en memorymap.

Figura 14. Definición de bancos.

Fuente: WLA DX [12].

Como se puede observar en la Figura 14, dentro de la directiva de rombankmap, se utiliza

bankstotal para definir el número de bancos totales que va a contener el cartucho y,

mediante banksize, se establece el tamaño de cada uno de estos bancos indicados en la

directiva banks.

Page 33: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

33

Una vez realizado todo esto, ya se tiene configurado el número de bancos que va a contener

la ROM y el número de ranuras que van a haber en el espacio de direcciones del Z80. En la

Figura 15, se observa una configuración mucho más simple a la que se ha visto

anteriormente, que servirá para realizar el ejemplo que se desea mostrar, donde solamente

se define una ranura y un banco, ambos de 32 kb de tamaño.

Figura 15. Definición de las ranuras y bancos que contendrá la ROM.

Fuente: Elaboración propia.

6.3. Validación de la ROM

Ahora se procede a configurar el arranque de la consola. Lo primero que se debe realizar

es validar la ROM, ya que la BIOS comprueba todas las ranuras disponibles de la consola

para comprobar si hay algún software valido a ejecutar y arranca lo primero que encuentre,

es decir, comprueba si hay algún videojuego válido a ejecutar.

La manera más rápida para poder realizar que la cabecera de la ROM sea válida y pueda

funcionar no solo en un emulador, sino también en una SMS real, es utilizar otra de las

directivas que proporciona WLA DX [12], la directiva sdsctag.

Esta directiva, es la que permite que el videojuego pueda funcionar en una consola real de

Sega Master System, ya que permite que la BIOS tome como válida la cabecera de la ROM.

Por otro lado, también permite añadir información adicional al programa creado, como su

versión, el nombre etc.

Page 34: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

34

Figura 16. Definición de la etiqueta SDSC.

Fuente: Elaboración propia.

Mediante el simple proceso de incluir esta directiva, la cabecera de la ROM ya es válida.

Esto es gracias a que la directiva sdsctag define a su vez otra directiva, la smstag.

Esta última directiva, lo que realiza es forzar al ensamblador a escribir una etiqueta ROM

común en el fichero de ROM mediante la escritura del string “TMR SEGA” en los 8 primeros

bytes de la cabecera de la ROM. Asimismo, la smstag, también define otra directiva, la

directiva computesmschecksum, la cual escribe a su vez un checksum de ROM en la

dirección de memoria correspondiente.

Luego, mediante la simple inclusión de la directiva sdsctag se realizan una serie de

operaciones que permiten validar la ROM. Todo esto proceso, se encuentra explicado con

mayor detalle en la sección BIOS del anexo perteneciente a este documento.

Tras realizar la validación, se debe indicar en que zona va a comenzar a ejecutarse el código

del videojuego. Concretamente. hay que indicar el banco, la ranura y la dirección de memoria

correspondiente.

Esto puede ser realizado fácilmente con la directiva bank, mediante la cual indicamos el

banco donde escribiremos el código y la ranura donde va a ser insertado ese banco. Si solo

especificamos el banco, este se insertará en la ranura que hayamos indicado por defecto en

defaultslot.

Page 35: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

35

Figura 17. Definición de la dirección de memoria en la cual se escribirá el código.

Fuente: Elaboración propia.

Siempre que se defina la directiva “.bank 0 slot 0”, seguidamente debe utilizarse la directiva

org u la orga. La directiva org, indica la dirección de inicio de memoria relativa al banco de

ROM que se haya indicado previamente en bank, la otra directiva, también proporciona la

dirección de inicio de memoria, pero de manera absoluta con respecto a toda la memoria. En

nuestro caso se utilizará org.

6.4. Gestión de interrupciones

A continuación, se deben gestionar las interrupciones, si se realiza hincapié en la Figura

18, se puede observar que se desactivan las interrupciones, esto se realiza con la intención

de que la ejecución del programa no salte a otras direcciones de memoria y ejecute otro

código distinto, además se define el modo 1 de interrupción del Z80, lo que hace que todas

las interrupciones salten a la dirección $0038 cuando estas se produzcan.

Figura 18. Gestión de interrupciones en la SMS.

Fuente: Elaboración propia.

Page 36: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

36

Tras este código, se realiza un salto absoluto hacia la dirección donde se encuentra definida

la etiqueta main que es donde se encontraría el código principal de nuestro programa. Sin

embargo, antes de ir a esa dirección, se debe realizar una cosa antes: La gestión de la

interrupción de pausa de la consola, ya que es una interrupción no enmascarable [14] y no

puede ser ignorada por la CPU, por lo que debe ser tratada en la dirección $0066. En este

ejemplo, no se realizará ninguna acción cuando se pulse el botón de pausa, por lo que

simplemente se escribe la instrucción retn que se utiliza para volver a la dirección de

memoria donde se estaba antes de que saltase la interrupción.

Figura 19. Gestión del botón de pausa en la SMS.

Fuente: Elaboración propia.

6.5. Inicialización de la pila y el VDP

Llegados a este punto, ya se han realizado aspectos como la configuración del espacio de

ROM, la validación de la ROM o la gestión de aquellas interrupciones que puedan

producirse. Por lo que ya se han realizado todos los aspectos que son necesarios al principio

del código de la ROM.

Ahora, se va a empezar a trabajar con el programa principal, pero antes de realizar esto, es

necesario inicializar ciertas partes del sistema de la consola para que funcione correctamente.

En primer lugar, es necesario que la pila apunte a algún sitio de la memoria RAM y esto

debe hacerse antes de que ocurra cualquier interrupción, ya que estas harán uso de ella. Por

este motivo, es obligatorio realizar la inicialización del puntero a la pila lo más pronto posible

en el programa para evitar así problemas.

Page 37: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

37

Sabiendo que la pila aumenta su espacio hacia direcciones inferiores de la RAM cada vez

que se apila un valor en ella, lo normal es inicializar el puntero a la dirección de memoria

RAM más alta disponible para evitar así que sobrescriba alguno de los datos que tengamos

escritos en la RAM. EN este ejemplo, no es estrictamente necesario realizar esto ya que la

pila no va a ser utilizada, pero conviene saberlo para evitar futuros problemas en otros

programas que se deseen realizar.

Dicho esto, se inicializa el puntero de la pila a la dirección $DFF0, que corresponde a casi

el final de la RAM disponible en nuestra consola y es la dirección recomendada por el

manual oficial de la Sega Master System [28].

Después de haber inicializado el puntero de la pila, hay que configurar el chip de gráficos de

la consola, denominado Video Display Processor. Lo que se va a realizar es la inicialización

de los registros que contiene el VDP. Para ello, se le pasará un bloque de datos que permite

inicializar cada uno de los bits de los registros con un valor determinado.

Figura 20. Valores iniciales para los registros del VDP.

Fuente: Elaboración propia.

De manera resumida, cada uno de los bits que contienen estos registros configuran ciertos

aspectos gráficos de la consola, como, por ejemplo, el color de fondo mediante el uso de los

colores de la paleta, activar o desactivar la visualización de pantalla, la dirección de memoria

donde se copiarán los tiles a usar en los sprites, si se quiere desactivar el desplazamiento en

algunas columnas de la pantalla etc.

Page 38: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

38

Concretamente, los aspectos que se han configurado con estos valores iniciales han sido:

• Activación el modo específico de visualización que tiene el chip de VDP de la SMS

(El modo 4).

• Deshabilitación de la visualización de la pantalla.

• Establecer las direcciones base de memoria de la VRAM a su valor más

frecuentemente utilizado.

• Uso de los colores de la paleta para el fondo/borde de la pantalla.

• Etc.

Estos valores indicados en la figura 20, son valores que no necesariamente deber ser los

mismos siempre, cambiarán en función de cómo sea el videojuego a crear y los aspectos que

se deseen realizar, por lo que es importante conocer qué es lo que hace cada uno de los

registros del VDP [18][33]. De nuevo, para no extender la explicación, en el anexo se

encuentra disponible toda la información sobre lo que hace cada uno de los bits de estos

registros.

Una vez definido los valores iniciales de los registros del VDP, deben ser enviados al puerto

de control del mismo para poder establecer dichos valores y, para ello, se utiliza la

instrucción del Z80: otir, la cual permite mover un conjunto de bytes desde una dirección al

puerto que se desea (véase la figura 21).

Figura 21. Se envía los valores iniciales del VDP al puerto de control.

Fuente: Elaboración propia.

Page 39: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

39

6.6. Liberación de la VRAM

Ahora que se han inicializado los registros del VDP a unos valores adecuados para la ROM

básica, hay que vaciar la memoria de video. Es necesario vaciar el contenido que pueda

contener la VRAM del VDP, ya que no se sabe qué es lo que puede contener y es preferible

vaciarla antes de que se realice cualquier acción con ella. De hecho, la primera vez que se

acceda a una consola real, la VRAM contendrá el logo oficial de Sega procedente de la

BIOS.

Para poder hacer esto, hay que enviarle la información necesaria al puerto de control del

VDP, es decir, hay que indicarle que queremos escribir en la VRAM y la dirección en la cual

se quiere escribir.

Figura 22. Dirección de VRAM y orden a realizar.

Fuente: Elaboración propia.

Una vez que se le ha indicado correctamente lo que se quiere hacer, hay que rellenar toda la

memoria de video con un valor (en el caso que repercute este ejemplo se utilizará el valor

$00 que hace referencia al color negro) y para ello, se necesita un contador que permita

recorrerla la zona de memoria entera. En la Figura 23, se observa el uso de un contador que

contiene el valor $4000 que corresponde a los 16 kb que tiene la memoria de video de la

SMS.

Page 40: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

40

Figura 23. Vaciado de la VRAM.

Fuente: Elaboración propia.

Llegados a este punto, prácticamente ya está realizado todo lo necesario para poder obtener

una ROM básica. Lo único que faltaría sería activar la visualización de la pantalla para poder

observar algo por pantalla y esto se realiza pasando el valor $E4 al registro $01, tal cual

como se ha realizado anteriormente en la sección de inicialización del VDP. Al activar la

visualización de la pantalla debería verse aquello que hayamos implementando. En la figura

24, se puede ver mi versión realizada del “hola mundo” procedente del tutorial de Maxim en

SMSPOWER [23].

Figura 24. Mensaje de ¡Hola Mundo! mostrado por pantalla.

Fuente: How to program de Maxim [23].

Page 41: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

41

Con esta base, y teniendo siempre en cuenta los valores de los registros del VDP que habrá

que modificarlos en función de las necesidades del proyecto, ya es posible construir

cualquier videojuego para la SMS y que funcione, tanto como en un emulador como en una

consola real.

Page 42: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

42

7. Diseño del videojuego

En las siguientes líneas aparecerán imágenes relacionadas con el contenido gráfico utilizado

dentro del juego de Invasion realizado para este proyecto. Todo este contenido artístico no

está realizado por el autor de este proyecto y ha sido obtenido de diferentes sitios web

que ofrecen la posibilidad de usar gratuitamente materiales tales como: diseño de enemigos,

de armas, de objetos u otros [6][10][16].

Debido a esto, junto a la descripción de la imagen, se adjuntará el autor o autores de cada

uno de los materiales artísticos utilizados dentro del videojuego.

7.1. Descripción general

Invasion es un videojuego de plataformas para la Sega Master System. El jugador controla

a un soldado perteneciente a las fuerzas especiales de Estados Unidos a través de los bosques

de algún lugar de Vietnam, con el objetivo de destruir una nueva amenaza que acaba de

surgir. Toda la acción tiene lugar desde una perspectiva lateral y los niveles están conectados

mediante puertas que al abrirse transportan al jugador al siguiente mapa.

Mediante el D-Pad, el jugador puede mover al personaje principal a través de los niveles de

izquierda a derecha en función de la colocación del mismo. Del mismo modo, si el D-Pad se

encuentra colocado hacia arriba, el personaje efectuará un salto. Además, mediante el uso

de los dos botones disponibles de la consola, se puede efectuar un disparo para eliminar a

los enemigos e interaccionar con los elementos del nivel como pueden ser las puertas u otros.

El personaje controlable por el jugador dispone de un fusil de asalto. Este fusil, es la única

arma utilizable en el juego y se utiliza principalmente para eliminar a los enemigos que

puedan ir apareciendo durante el transcurso del juego.

Desde que comienza el juego hasta que finaliza hay un contador de tiempo que determina el

tiempo total disponible para finalizar el juego. Si el tiempo finaliza antes de llegar al final

del nivel se acaba la partida, independientemente del número de vidas restantes del jugador.

Page 43: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

43

7.2. Historia

Corre el año 1966, se han detectado una serie de criaturas extrañas en los bosques de

Vietnam, las cuales están destruyendo todo a su paso, matando a civiles y cualquier otro tipo

de vida que se encuentren. Informes recientes de nuestros agentes asignados en la zona

indican que estas criaturas son inteligentes y están preparando una especie de defensa

alrededor de la zona para evitar que nadie entre o salga. Se prevé que la zona deba estar

fuertemente defendida por estas criaturas e incluso hay construidas estructuras desconocidas

cuya función se desconoce.

Tu eres Mark, un soldado perteneciente a las fuerzas especiales de Estados Unidos que ha

sido enviado para acabar con esta amenaza desconocida. La misión asignada a tu pelotón es

encontrar la fuente de origen de estos seres y destruirla. Para ello, deberás avanzar por los

bosques de Vietnam y enfrentarte a estas criaturas hasta encontrar la forma erradicarlas.

Durante la misión, tu pelotón ha sido emboscado y acribillado por estos seres inteligentes.

Por suerte o por desgracia, tu eres el único superviviente, pero has caído en una especie de

cuevas que se encontraban ocultas hasta ahora y estás fuertemente herido. Ahora debes

acabar a toda costa con la misión que empezaste con tu equipo para poder salvar a la

humanidad.

7.3. Género y Audiencia

Invasion es un juego de género de Acción/Aventura, ya que, por una parte, el jugador debe

utilizar su habilidad, reflejos y puntería para eliminar a los enemigos que van apareciendo

por los niveles del juego, y, por otro lado, en algunas zonas el jugador deberá resolver

pequeños puzles.

Por otro lado, Invasion está enfocado para todos los públicos y edades.

7.4. Ámbito

Invasión tiene lugar en los bosques de Vietnam. Los niveles que aparecerán principalmente

en el juego corresponderán a unas cuevas ocultas dentro de los bosques. En los niveles

finales del juego, el personaje Mark consigue salir a la superficie por lo que los niveles

corresponderán a una simple representación de estos bosques. Hay disponibles un total de 9

niveles jugables en Invasión

Page 44: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

44

Solo habrá disponible un personaje controlable por el jugador, el soldado Mark. El cual

solo podrá utilizar una única arma, el fusil de asalto del ejército.

Como NPC’s únicamente se encontrarán dos tipos de enemigos a los cuales el jugador deberá

eliminar. Cada uno con su patrón de comportamiento distinto.

Finalmente, distribuidos por los niveles habrá un corazón que proporcionará vida al jugador

y una llave para poder abrir las puertas en caso de que sea necesario.

7.5. Jugabilidad

La forma para progresar a través de los diferentes niveles del juego es bastante sencilla.

Mediante la apertura de una puerta el jugador podrá pasar de un mapa a otro. Esta puerta

se podrá encontrar en prácticamente todos los niveles.

En ocasiones, para poder abrir esta puerta, el jugador requerirá de una llave que estará

colocada en algún lugar del mismo nivel. En los mapas que se requiera de una llave, la puerta

no se abrirá hasta que no se haya recogido.

Por otro lado, para poder avanzar por los mapas, el jugador deberá hacer uso del salto y

movimiento del personaje jugable para avanzar por las plataformas. Asimismo, en algunos

niveles aparecerán enemigos que se podrán eliminar mediante el arma principal del

personaje.

En el último nivel jugable de Invasion, aparecerá un portal el cual el jugador deberá destruir

para poder cumplir la misión de Mark. Para lograr este objetivo, el jugador deberá destruir

los 3 cristales que dan vida al portal. Para destruir los cristales valdrá con simplemente

dispararles una única vez.

Una vez destruidos todos y cada uno de los cristales el juego habrá finalizado, pudiendo

reiniciar la partida si el jugador lo desea.

7.6. Mecánicas

A continuación, se van a detallar todas las mecánicas disponibles en Invasion. En primer

lugar, se van a describir aquellas que están relacionadas con la interfaz del juego:

Page 45: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

45

• Contador de Tiempo: Una vez comenzado el juego, en la interfaz aparecerá un

simple contador de tiempo que indicará el tiempo total restante para terminar el

juego. El tiempo total disponible para finalizar el juego son unos 150 segundos. Si el

contador de tiempo llega a 0 y el jugador no ha logrado alcanzar el último nivel y

destruir los cristales, aparecerá por pantalla un mensaje indicando al jugador que se

ha terminado el juego Dentro de esta pantalla, el jugador podrá reiniciar la partida

pulsando el botón 2 de la consola. Si se pulsa dicho botón, el contador vuelve a su

valor inicial.

• Contador de vidas: En la interfaz también hay disponible un contador de vidas que

indica el número de vidas restantes del jugador. El número de vidas disponibles

puede ser aumentado recogiendo los corazones distribuidos por los niveles. Si el

contador de vidas llega a 0 se termina el juego. El número de vidas inicial al

comenzar el juego es 1 para simbolizar que el personaje está herido. Acciones como

recibir daño o caer fuera del mapa restan una vida al contador.

A continuación, se van a describir las mecánicas relacionadas con el personaje controlable

por el jugador:

• Saltar: Permite al personaje elevarse una pequeña distancia hacia arriba. Esto

permite al jugador poder avanzar por las plataformas y obstáculos que se encuentran

distribuidos a lo largo de los niveles de juego.

• Disparar: Acción que permite el personaje disparar las balas del fusil con alcance

limitado. Solamente se puede efectuar un disparo hacia la derecha o hacia la

izquierda. Cada vez que se pulsa el botón de disparo se disparará una bala siempre y

cuando no se haya disparado una ya. En el caso de que ya se haya disparado una bala

y no haya transcurrido el tiempo requerido para disparar la siguiente bala (2

segundos) no se podrá efectuar otro disparo. Mediante las balas del arma se pueden

eliminar a los enemigos y los cristales del último nivel.

• Interaccionar con el entorno: Si el jugador se encuentra cerca de una puerta podrá

pulsar el botón 2 de la consola para abrirla, siempre y cuando esta no requiera de una

llave para poder abrirse. En uno de los niveles finales, se utilizará esta misma acción

para poder usar una escalera.

Page 46: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

46

• Salud: Representa la vida restante del jugador. Este valor puede ser incrementado

mediante la recogida de corazones o decrementado por el daño de los enemigos, por

la caída fuera del mapa o por que el contador de tiempo haya llegado a 0. En este

último caso, se pierden todas las vidas dando resultado al final del juego.

• Invulnerabilidad: Cuando el jugador reciba daño de cualquier fuente, el sprite de

su personaje empezará a realizar un efecto de parpadeo. Mientras duré este efecto

significará que el jugador es invulnerable para los enemigos por lo que no recibirá

daño alguno mientras dure el efecto. El efecto de invulnerabilidad persiste hasta 4

segundos. La invulnerabilidad no impide que el jugador pueda perder una vida por

caída fuera del mapa.

Figura 25. Sprite del personaje principal.

Fuente: Blue Yeti Studios [4].

Ahora se van a describir todas las mecánicas relacionadas con el nivel, es decir, todas

aquellas que tienen que ver con la interactuación con el entorno, los elementos disponibles,

el progreso de las secciones y demás:

• Agua/precipicio: En alguno de los niveles del juego, se encontrarán plataformas

separadas por una pequeña distancia. Entre las plataformas puede encontrarse agua

o un precipicio, en ambos casos si el jugador no consigue alcanzar la plataforma

siguiente y cae fuera de la misma, perderá una vida. Si no le quedan vidas, perderá

la partida.

• Corazones de vidas: Al pasar por encima de estos elementos el jugador verá

incrementado en uno su valor de salud. No hay límite máximo de vidas que pueda

tener el jugador.

Page 47: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

47

Figura 26. Sprite de vida.

Fuente: DontMind8.

• Llaves: En algunos mapas aparecerá una llave. En estos casos, será necesario

recogerla para poder abrir la puerta del nivel. Para recoger la llave solo hay que pasar

por encima de la misma y se recogerá automáticamente. Al hacer uso de la llave se

pierde la misma.

Figura 27. Sprite de llave.

Fuente: BizmasterStudios [34].

• Cristales: En el nivel final de Invasión, aparecerán un total de 3 cristales. Estos

cristales proporcionan la energía necesaria para que el portal de los enemigos se

mantenga encendido. Pueden ser destruidos mediante el impacto de una sola bala del

arma del protagonista. Al ser destruidos todos los cristales el portal se desactiva y se

termina el juego.

Figura 28. Sprite del cristal.

Page 48: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

48

• Puertas: En la mayoría de los niveles aparecerá una puerta dibujada por pantalla.

Esta puerta será necesaria para poder avanzar al siguiente nivel. Para hacer uso de

una puerta será necesario que el jugador pulse el botón 2 de la SMS. Si no hay

ninguna llave en el mapa, la puerta se abrirá lo que resultará en un cambio de mapa.

Figura 29. Sprite de puerta.

Fuente: Coolphill.

7.7. Enemigos

En Invasión el jugador podrá encontrar 2 tipos de enemigos diferentes, cada uno con un

patrón de comportamiento distinto.

En primer lugar, se encuentran los enemigos a melé los cuales son caracterizados por hacer

daño al jugador cuando este colisiona con ellos. A continuación, se detallan las

características de los mismos:

• Salud: Son necesarios 3 disparos del jugador para acabar con los enemigos a melé.

• Movimiento: Realizan un pequeño recorrido de un punto A hasta otro punto B y

viceversa durante un corto periodo de tiempo. Cuando este tiempo finaliza, el

enemigo se queda detenido en un punto fijo hasta que transcurra otro corto periodo

de tiempo. Una vez finalizado este segundo tiempo, este vuelve a realizar el recorrido

entre los dos puntos asignados. Siempre realiza el mismo recorrido, aunque el punto

donde puede permanecer inmóvil puede variar algunas veces.

• Ataque: No realizan ningún tipo de ataque especial. El daño que pueden hacer el

jugador se basa en el contacto de estos enemigos con el jugador. Cuando el jugador

colisiona con alguno de estos enemigos, este ve decrementado en uno su contador de

vidas.

Page 49: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

49

Figura 30. Sprites del enemigo a melé.

Fuente: Jesse M (@Jsf23Art).

En segundo lugar, se encuentran los enemigos a distancia. A continuación, se detallan las

características principales de los mismos:

• Salud: Son necesarios 2 disparos del jugador para eliminar a estos enemigos.

• Movimiento: Realizan un pequeño recorrido de un punto A hasta otro punto B y

viceversa durante un corto periodo de tiempo. Cuando este tiempo termina, el

enemigo se queda detenido en un punto fijo hasta que transcurra otro pequeño

tiempo. Una vez finalizado este segundo tiempo, este vuelve a realizar el recorrido

entre los dos puntos asignados. Siempre realiza el mismo recorrido, aunque el punto

donde puede permanecer inmóvil puede variar algunas veces.

• Ataque: Dentro del periodo de tiempo en el cual el enemigo permanece inmóvil en

una posición fija, es cuando este determina la posición del jugador para calcular la

dirección en la que disparar la bala, por lo que el disparo de la misma siempre irá en

la dirección en la que se encuentra el jugador. La bala del enemigo dispone de un

alcance limitado y puede colisionar con los elementos del entorno.

Page 50: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

50

7.8. Niveles

Existen un total de 9 niveles jugables en Invasión. Cada uno de ellos tiene una distribución

única y el jugador deberá afrontarlo de una manera distinta.

En total hay disponibles 2 biomas distintos. El primer bioma estará basado en una temática

de cuevas, donde principalmente se observarán materiales de piedra o roca y pequeñas

estructuras como columnas. Además, en algunos niveles también se podrá encontrar

pequeñas zonas con agua.

Figura 32. Bioma de zona cuevas.

Fuente: Adam Saltsman (@FinjiCo).

Figura 31. Sprites del enemigo a distancia.

Autor: Warren Clark.

Page 51: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

51

El segundo bioma disponible será un bosque donde principalmente se encontrarán materiales

formados por hierba, madera o tierra. Todas las plataformas que se puedan encontrar en este

bioma estarán formadas por los materiales mencionados anteriormente.

Figura 33. Bioma de bosques.

Fuente: Vnitti (@vnitti_art).

A continuación, se muestran algunos ejemplos de los niveles creados para Invasión a partir

de estos tiles mostrados anteriormente:

Figura 34. Niveles creados para el primer bioma.

Fuente: Elaboración propia.

Page 52: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

52

Figura 35. Niveles creados para el segundo bioma.

Fuente: Elaboración propia.

7.9. Controles

En la siguiente figura se pueden observar los controles del juego Invasion para la Sega

Master System.

Figura 36. Controles de Invasion para la SMS.

Fuente: www.1001freedownloads.com por PanamaG.

Page 53: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

53

8. Desarrollo del videojuego

El objetivo de este apartado, es describir, lo más detalladamente posible, todo el proceso que

he tenido que realizar para poder conseguir un videojuego jugable en la Master System.

Mi intención es conseguir que esta sección sirva como apoyo de ayuda a otras personas que

se encuentren en la misma situación que la mía y quieran o deban crear un videojuego para

esta consola de 8 bits.

De esta manera, describiré todos los pasos que he ido realizando y todos aquellos problemas

que me haya ido encontrando durante el desarrollo, así como, las soluciones (si las he

encontrado) de los mismos.

8.1. Prototipo Mágica

Antes de empezar realmente a crear el videojuego para este proyecto, primero pretendo

realizar un pequeño prototipo donde pueda probar algunos aspectos básicos, ya que es la

primera vez que realizo algo para la SMS y desconozco como se realizan muchos aspectos

como: cargar tilemaps, sprites, hacer animaciones, recoger la entrada del teclado por el

usuario, colisiones etc.

A todo esto, hay que sumarle que voy a utilizar programas y software que no he utilizado

nunca, como es el ensamblador WLA DX [12] y el emulador MEKA [8], por lo que este

primer prototipo me sirve también para poder probarlos y entenderlos mejor antes de

ponerme directamente con el videojuego de este proyecto.

El prototipo que voy a realizar está basado en el videojuego Mágica de Amstrad CPC

creado por Juan J. Martínez para el CPCRetrodev de 2015 [15]. La razón por lo que he

decido crear un prototipo basado en este videojuego es debido a que comparte diversas

similitudes con el juego de mi proyecto: Juego de plataformas con mecánicas básicas como,

por ejemplo, saltar, disparar, colisiones con el entorno etc. Todo esto son mecánicas simples

que mi videojuego va a requerir tarde o temprano, por lo que la mayor parte de lo que realice

en este prototipo es trabajo que ya tendré realizado para integrar el videojuego del proyecto.

Page 54: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

54

Figura 37. Mágica para Amstrad CPC.

Fuente: Sitio web de Juan J. Martinez, creador de Mágica [15].

Como ya he comentado previamente, es la primera vez que realizo un videojuego para esta

consola y, por consiguiente, no tengo ni idea de cómo empezar a hacer algo jugable para la

SMS. Lo único que conozco, es crear una pequeña base para un juego, de manera que este

pueda funcionar sin problemas en una consola Master System real. Dicha base, no la voy a

explicar aquí, ya que esta explicación ya está realizada en el apartado de creación de una

ROM básica de este documento, donde se explican aspectos como: el proceso de inicializar

el arranque de la consola, inicializar los registros del VDP etc.

A partir de esta base que ya tengo creada, voy a empezar a escribir el código de mi prototipo.

Para ello, buscando por internet información sobre aspectos de la SMS, me he topado con

una página muy interesante, llamada smspower [9].

En esta página, he encontrado información muy útil que me ha ayudado a crear la base para

que mi programa pueda funcionar de manera correcta en la consola. Además, en una de sus

secciones, he encontrado a un usuario llamado Anders, el cual explica cómo crear un juego

para la Master System y realiza un ejemplo de un juego de carreras de coches [3].

Es a partir del tutorial de este usuario, de donde me he fijado mayormente para poder hacer

los primeros pasos de mi prototipo de Mágica y haré referencia a este tutorial varias veces a

lo largo de la explicación.

Page 55: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

55

Figura 38. Racer para la SMS.

Fuente: Create a Racing Game [3].

8.1.1. Código básico para la ROM

Lo primero que vamos a hacer es crear un fichero llamado main, el cual más adelante lo

único que contendrá será el bucle principal del prototipo y las funciones de inicialización de

la consola. Sin embargo, por ahora, este fichero contendrá todo el código del prototipo hasta

que logre entender cómo funciona más o menos todo y lo divida en varios ficheros para que

todo sea mucho más fácil de entender.

La mayor parte de lo que hay realizado en el apartado de Creación de una ROM básica va a

servir para este prototipo, sin embargo, hay ciertos detalles que son necesarios modificar. En

primer lugar, la configuración de bancos y ranuras de la ROM no va a ser la misma que

la que se tenía anteriormente, por lo que hay que cambiarla de tal manera que en nuestro

mapa de memoria tengamos 2 ranuras de ROM y una de RAM, donde en esta última la

utilizaremos para almacenar variables y datos que vayan a cambiar durante la ejecución de

nuestro juego.

También especificamos 2 bancos en nuestro cartucho del mismo tamaño que las 2 ranuras

de ROM. Recordemos que los bancos nos sirven para que en un futuro podamos llegar a

tener más espacio en nuestra consola. Aunque es probable que en este prototipo no lleguemos

a llenar todo el espacio que tenemos disponible, viene bien realizar la declaración para ir

acostumbrándonos a hacer una correcta definición de nuestro espacio de memoria.

Page 56: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

56

Figura 39. Configuración de las ranuras y bancos para el prototipo.

Fuente: Elaboración propia.

Esta configuración de bancos y ranuras es libre, es decir, se puede hacer como uno quiera y

no tiene que porque ser necesariamente de esta forma, sin embargo, con la finalidad de poder

seguir el tutorial de la mejor manera y no cometer errores, se va a utilizar la misma

configuración que se utiliza en el tutorial y, más adelante, se modificará si es necesario.

El segundo aspecto que hay que cambiar de nuestro código base son los valores de

inicialización del VDP. Al igual que antes, las valores que se van a poner por ahora son los

mismos que utiliza Anders en su tutorial [3], de esta manera evitamos así problemas futuros

relacionados con el malfuncionamiento del prototipo debido a que se han utilizado otros

valores distintos a los del tutorial. Cuando ya se hay aprendido como funciona todo, se

cambiarán estos valores para hacer más pruebas y ver que todo lo explicado en el tutorial se

entiende a la perfección.

Figura 40. Inicialización de los registros del VDP para el prototipo.

Fuente: Elaboración propia.

La función que realiza cada registro del VDP ya se encuentra explicado en el Anexo. Aquí

solo se van a describir los aspectos más importantes que se están especificando en cada uno

de los registros:

Page 57: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

57

• Registro 0: Se activa el modo 4 de visualización. Este es un modo específico que

contiene el chip del VDP de la SMS y debe estar activado siempre según la

documentación oficial de la consola [28].

• Registro 1: Se desactiva la visualización de la pantalla, ya que esta será activada

justo después de haber inicializado todo lo necesario en la consola. Además, se activa

el VBlank, de tal manera que cada vez que se pinte un frame, se generará una

interrupción cuando se tengan las interrupciones habilitadas. Esto se utilizará como

contador básico de tiempo en la consola.

• Registro 2: Se establece la dirección donde se encontrará el nombre de la tabla en

VRAM. Se utiliza la dirección por defecto que recomienda la documentación oficial

[28], que corresponde a la dirección $3800.

• Registros 3 y 4: Estos 2 registros no tienen ningún uso en la consola. Se establecen

todos sus bits a 1 que es como se recomienda para un funcionamiento estándar.

• Registro 5: Se establecen todos los bits a 1 para que la dirección de la tabla que

contiene la información de los sprites dibujados en pantalla corresponda a la

dirección por defecto ($3F00).

• Registro 6: El bit más importante de este registro es el segundo, el cual, al estar

activado, indica que todos los tiles que se utilizan para los sprites empezarán a

almacenarse a partir de la dirección $2000.

• Registro 7: Se indica que quiere emplear el color 3 de la paleta para el borde de la

pantalla.

• Registros 8 y 9: Por el momento no se requiere de ningún valor de scroll horizontal

ni vertical, por lo que se le pasa el valor $00.

• Registro 10: Se desactiva el HBlank, de esta manera no se generan interrupciones

cada vez que se dibuja una línea por pantalla.

Con estos dos aspectos cambiados y el resto de código que ya se disponía, ya se puede

empezar a probar cosas y, lo primero que se va a realizar, es probar a pintar un fondo y un

Sprite por pantalla.

Page 58: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

58

8.1.2. Dibujado de un sprite y un fondo por pantalla

Como primera prueba, lo que se va a hacer es cargar el Sprite y el fondo que se proporciona

en el tutorial [3] y, posteriormente, ya se probará a cargar un fondo y Sprite distinto para

corroborar que se ha entendido el procedimiento.

Antes de mostrar cualquier cosa por pantalla, primero hay que cargar en memoria todos los

“assets”, es decir, todos los recursos que se necesitan tanto para pintar el fondo como para

pintar los sprites. Estos recursos consisten en:

• La paleta de colores para indicar que colores van a ser utilizados. Hay 2 paletas, una

para el fondo y otra para los sprites.

• Los índices de tiles (denominados charcodes en inglés) tanto de los sprites como del

fondo.

• El tilemap para el fondo.

Lo primero que vamos a cargar va a ser la paleta de colores, para ello, lo que hay que hacer

es indicar al VDP lo que se quiere realizar. En este caso, lo que queremos es copiar datos a

la CRAM, que es una memoria interna de solo escritura que se utiliza para guardar los datos

de las paletas a usar. La siguiente imagen muestra cómo está distribuida esta memoria:

Figura 41. Direcciones de la paleta de colores (CRAM).

Fuente: Software Reference Manual for the Sega Mark III Console [28].

Page 59: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

59

Como se puede observar en la imagen, los primeros 15 colores corresponderían a la primera

paleta (la del fondo) y los 15 colores siguientes, a la segunda paleta (la de los sprites).

Por lo tanto, si yo quisiese, por ejemplo, cargar la paleta de colores de los sprites, hay que

pasar el valor $C010 al puerto de control, donde los dos últimos valores hexadecimales

representan la dirección de la CRAM, que fijándonos en la figura 41 vemos que corresponde

a la primera posición de la segunda paleta de colores (Bank 2, Color 0) que es la de los

sprites. Si por el contrario quisiera cargar la paleta del fondo, los dos últimos valores

corresponderían a $00 (Bank 1, Color 0).

El porqué de los dos primeros valores hexadecimales se encuentra explicado en el Anexo de

este documento, concretamente en el apartado de programación del VDP. De manera

resumida, estos dos valores indican al VDP la orden a realizar, es decir, qué es lo que se

quiere hacer exactamente. Cuando se trabaja con CRAM, el valor a pasar por el puerto

siempre empezará por $C0 ya que la CRAM es más pequeña que la VRAM y no se

requieren tantos bits para representar la dirección a donde copiar los datos.

Una vez que se han enviado por el puerto de control la orden y dirección, ahora tenemos que

copiar los datos de la paleta a la CRAM, para ello, creamos 2 etiquetas que indican la

dirección de inicio y final de los datos de la paleta, de tal manera que, si hacemos una resta

de estas 2 etiquetas, obtenemos el tamaño que ocupan los datos de la paleta y podemos

cargarlos en el registro necesario.

Figura 42. Cargado de la paleta de colores a los sprites.

Fuente: Elaboración propia.

La función denominada “prepararVDP” lo único que realiza es enviar el valor del registro

HL al puerto $BF (puerto de control) y la función nombrada “cargarPaleta” utiliza la

instrucción “otir” para enviar los datos de la paleta al puerto de datos del VDP.

Page 60: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

60

Ahora vamos a cargar los tiles del Sprite que queremos dibujar por pantalla. Para ello, al

igual que con la paleta, lo primero que hay que hacer es indicar la orden y dirección al VDP.

En este caso, cargo el valor $2000 a HL, ya que como se especifica en el registro $06 del

VDP, los tiles de los sprites se van a comenzar a copiar a partir de la dirección $2000. Si

correspondiesen a los tiles del fondo, el valor a pasar sería $0000.

Ahora tenemos que indicar donde tenemos almacenado la información sobre los tiles y aquí

es cuando entra en juego un programa que nos va a ayudar mucho en cuanto al tema de

dibujado de sprites y fondo, el BMP2TILE [22].

Este programa, que es utilizado por Anders en el tutorial, permite cargar una imagen

cualquiera (siempre que cumpla unos requisitos como que no tenga más de un número

determinado de colores o que dichos colores correspondan a los de la paleta de la SMS). Al

cargar esta imagen, que corresponderá o a un fondo o a un Sprite, el programa nos

proporcionará automáticamente los índices de tile, el tilemap y la paleta de colores, y,

además, nos permitirá exportar cada uno de estos datos obtenidos en ficheros independientes,

los cuales podemos incluir en el proyecto.

Figura 43. Valores de la paleta de colores para un Sprite de Sonic usando BMP2TILE.

Fuente: BMP2TILE [22].

Page 61: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

61

Gracias a este programa, podemos realizar un “include” del fichero exportado por el

programa a partir del sprite o fondo cargado en él. En nuestro caso, corresponderá al fichero

que contiene la información de los índices de tile de un sprite.

Para acceder a esta información creamos una etiqueta justo antes del “include” del fichero

y, de esta manera, tendremos una etiqueta que indica la dirección de inicio donde se

encuentran todos los datos de los índices.

Ahora, lo que hay que realizar es una copia de todos estos datos a la memoria de video y

para ello, utilizaremos la forma que utiliza Anders en su tutorial: indicar el tamaño total de

todos los datos a copiar en el registro BC e ir copiando uno a uno estos datos al puerto $BE

hasta que el registro BC llegue a 0. En este caso en concreto, hay 16 tiles donde cada uno

ocupará unos 32 bytes en total.

Figura 44. Copia de los datos de los índices de tiles a la VRAM.

Fuente: Elaboración propia.

Llegados a este punto, ya se tendrían todos los recursos necesarios para poder pintar un sprite

por pantalla, sin embargo, para el fondo aun es necesario hacer una cosa más: Cargar el

tilemap, es decir, el fichero que indica donde se dibuja en pantalla cada tile y se obtiene de

la misma manera que los índices de tile, mediante BMP2TILE [22].

Page 62: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

62

El procedimiento para copiar el tilemap del fondo es el mismo que se ha estado realizando

hasta ahora para los índices de tile: primero se indica la orden y dirección al VDP, después,

se copia al registro HL la dirección donde se encuentran todos los datos del tilemap y en el

registro BC, se almacena el tamaño que ocupan estos datos. Con estos datos, ya se puede

utilizar la función de “escribirVRAM” para realizar la copia de los datos.

Figura 45. Carga de un tilemap de un mapa en VRAM.

Fuente: Elaboración propia.

Si ahora ejecutamos el juego en el emulador, ya se podría distinguir el fondo dibujado por

pantalla, sin embargo, el sprite aún no se puede observar. Es en este momento, cuando me

doy cuenta de que dibujar un Sprite en SMS es más complicado de lo que pensaba, ya que

mientras que, con el fondo, una vez cargado la paleta, los tiles y el tilemap, este ya se dibuja

por pantalla, con el Sprite no sucede lo mismo.

Si se quiere dibujar el Sprite correctamente por pantalla, es necesario cargarlo en el

Sprite Attribute table (SAT), que corresponde a la tabla de la memoria de video donde se

almacena toda la información relacionada con los sprites, como puede ser su posición

vertical y horizontal que ocupa en la pantalla, y el índice de tile a dibujar.

Para poder cargar dicho Sprite en la tabla, primero lo vamos a almacenar en un buffer que

represente dicha tabla y, de esta manera, una vez tengamos todos los datos necesarios, solo

hay que efectuar una copia de todo lo que se encuentra en el buffer a la tabla de la VRAM.

Page 63: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

63

Figura 46. Buffer del Sprite Attribute Table.

Fuente: Elaboración propia.

Una vez creado nuestro buffer, hay que comenzar a copiar los datos del sprite en él. Lo

primero que vamos a copiar son los índices de tile del Sprite en cuestión y, para ello,

necesitamos saber cuál va a ser la primera posición que van a ocupar en la tabla del SAT, la

cual recordemos que es aquella donde se va a almacenar toda la información relacionada con

los sprites que pintamos por pantalla.

Con la finalidad de lograr entender mejor el funcionamiento de esta tabla, en la figura 47

imagen se puede observar su estructura:

Figura 47. Representación del SAT de la SMS.

Fuente: Sega Master System VDP Documentation por Charles MacDonald [18].

Page 64: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

64

El SAT ocupa un total de unos 256 bytes, los cuales se encuentran desde la dirección $3F00

de la memoria de video hasta la dirección $3FF0. Sin embargo, si nos fijamos en el buffer

que hemos creado anteriormente, podemos observar que está situado en la RAM,

concretamente desde la dirección $C000 hasta la $C0F0, por lo que a la hora de indicar la

posición inicial de la tabla para los datos de cada uno de los sprites, hay que tener en cuenta

la dirección del buffer y no la de la VRAM. Una vez que tengamos los datos en el buffer

ya efectuaremos la copia de los datos a la dirección donde se encuentra la tabla en memoria

de video.

De estos 256 bytes totales, existen 64 que son libres para usar. Ahora mismo, los que nos

interesan son los bytes que se encuentran entre las hpos (posiciones horizontales) del sprite,

que corresponden a los bytes para los índices de tile (en la figura 47 están representados con

la letra n).

Teniendo esta información en mente, indicamos la primera posición de la tabla donde se van

a empezar a escribir los charcodes y los empezamos a copiar, siempre teniendo en cuenta

que cada vez que se copie uno hay que avanzar 2 bytes para poder copiar el siguiente, ya que

recordemos que los charcodes se encuentran entre las hpos.

Figura 48. Copia de los índices de tile del Sprite al buffer del SAT.

Fuente: Elaboración propia.

Una vez tenemos los índices de tile del Sprite en el buffer, hay que pasar las posiciones

iniciales X e Y del Sprite.

Page 65: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

65

Para poder hacer esto, primero hay que cargar estos valores iniciales en alguna variable. Yo

he creado dos variables denominadas “ObjX” y “ObjY” que cumplirán dicha función. Estos

valores hay que actualizarlos con las hpos y vpos de la tabla de sprites y, para ello, he creado

dos funciones: una para actualizar la X y otra para actualizar la Y del objeto.

Por ahora, no voy a proporcionar el código que permite realizar estos dos aspectos, ya que

son funciones copiadas del tutorial de Anders y considero que no son lo más óptimas

posibles. Más adelante, aportaré un código que realiza lo mismo, pero de manera más óptima

y generalizada. De momento, si necesitas ver el código puedes ir a la página del tutorial de

Anders en smspower [3]. Lo que si voy a hacer es explicar qué es lo que se está realizando

en estas 2 funciones.

En primer lugar, en la función de actualizar la coordenada Y del Sprite con la vpos del SAT,

se ha de tener en cuenta las dimensiones que ocupa el Sprite. En este caso en concreto, el

Sprite del coche de carreras es de 4 tiles de ancho y 4 tiles de alto, donde cada tile a su vez

es de 8x8 píxeles.

Sabiendo esto, nos fijamos en que, para poder dibujar una fila del sprite por pantalla,

internamente de esa fila, la coordenada Y no va a cambiar su valor hasta que no pasemos a

la subsiguiente fila del sprite, ya que todos los tiles de la misma fila van a tener la misma

altura y, por consiguiente, el mismo valor de coordenada Y.

Teniendo en cuenta de que el sprite es de 4 tiles de ancho, cargamos 4 valores de Y iguales,

y cuando lleguemos al final de la fila, para pasar a la siguiente fila, sumamos 8 (cada tile

ocupa 8x8) al primer valor de Y de la fila actual en la que nos encontramos y repetimos todo

este proceso hasta conseguir pintar las 4 filas que ocupa el sprite.

El proceso para el valor de la coordenada X del sprite es similar, con la única diferencia de

que cada vez que introducimos un valor de X a este hay que sumarle 8, ya que cada columna

del sprite va a estar 8 píxeles más adelante que la anterior y hay que repetir este proceso para

todas las filas y columnas que ocupe el sprite. Además, no hay que olvidarse de que hay que

avanzar de 2 en 2 en las direcciones del buffer ya que las hpos están entre los charcodes.

Ahora lo único que queda es copiar toda esa información que se encuentra en el buffer a la

dirección de VRAM de la SMS donde se encuentra realmente la tabla del SAT y lograr así

dibujar nuestro sprite por pantalla:

Page 66: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

66

Figura 49. Copia del buffer al SAT.

Fuente: Elaboración propia.

Una vez copiado, ahora sí que se puede observar dibujado por pantalla el fondo y el Sprite

del tutorial:

Figura 50. Sprite y fondo del tutorial dibujados por pantalla.

Fuente: Emulador Meka [8].

8.1.3. Lectura de la entrada del jugador por teclado

Una vez que se ha conseguido dibujar por pantalla el sprite y el fondo, lo que nos interesa

ahora es que el jugador sea capaz de moverlo, pero, para ello, primero tenemos que

configurar el VBlanking o también denominado frame interrupt.

Page 67: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

67

El frame interrupt consiste en una interrupción que se genera en la Master System cada vez

que se dibuja un frame por pantalla, es decir, cada vez que se alcanza la última fila de la

pantalla y se vuelve a comenzar por la primera. Este es el método más simple que tiene la

consola para contar el tiempo y sincronizar correctamente todo lo que se dibuja por pantalla.

Más adelante observaremos como utilizar esto para poder controlar el tiempo en nuestro

videojuego.

Hay que gestionar esta interrupción, pero, para ello, primero hay que activar las

interrupciones, ya que recordemos que las teníamos desactivadas. Una vez activadas, en la

dirección $0038, que corresponde a la dirección donde se gestionan las interrupciones en la

SMS, lo que hacemos es leer del puerto de control para poder obtener el estado de las flags

de la VRAM. Este byte lo guardamos en una variable que hemos creado previamente, ya que

más adelante comprobaremos que nos va a proporcionar información realmente útil.

Figura 51. Zona de memoria para la gestión de interrupciones en SMS.

Fuente: Elaboración propia.

Con las interrupciones habilitadas, podemos ahora hacer un pequeño bucle en el programa

principal que empiece siempre y cuando se genere una interrupción, y como solo tenemos

activado el frame interrupt, hasta que no se dibuje un frame no volverá a empezar el bucle

otra vez. Esto se realiza con la instrucción “halt” que suspende la CPU hasta que ocurra una

interrupción o se produzca un reset.

Con todo esto, ya se tiene lo mínimo necesario para poder empezar a mover el sprite. Lo que

vamos a hacer es simplemente moverlo de izquierda a derecha y, para conseguir esto, hay

que fijarse en el manual oficial de la SMS [28].

Page 68: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

68

En este manual, se encuentra la información necesaria para poder implementar la recogida

de la información que pueda generar el jugador, es decir, los botones o teclas que este pulsa.

Este documento indica que existen múltiples puertos encargados de almacenar la

información introducida por el jugador, dependiendo del joystick o dispositivo que se esté

utilizando. Para el caso que nos repercute, es el puerto $DC el encargado de almacenar la

información relacionada con el input del usuario.

Por lo tanto, leemos de este puerto y nos fijamos en los bits que se encuentran a 0, ya que

son los que indican qué botón ha sido pulsado por el jugador. Concretamente, nos indica que

para poder mover a la izquierda o a la derecha, hay que fijarse en los bits 2 y 3

respectivamente (véase la figura 52).

Figura 52. Información sobre los bits correspondientes al Joystick P1.

Fuente: Manual de la Sega Mark III [28].

Sabiendo esta información, comprobamos si se quiere mover a la derecha o la izquierda y

movemos el sprite actualizando esta nueva posición obtenida en el buffer con las 2 funciones

que se han mencionado anteriormente.

Page 69: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

69

Lo más probable es que queramos que nuestro sprite no traspase los límites de la pantalla de

la consola. Cuando se llegue a alguno de los límites de la pantalla, el sprite deberá dejar de

moverse para evitar así que aparezca por el extremo opuesto.

La implementación más óptima para lograr esto sería a través de las colisiones del mapa,

pero como aún no han sido realizadas, es necesario idear una alternativa más simple:

mediante el tamaño de la resolución de la pantalla.

Si queremos controlar el límite derecho, sabemos que la pantalla tiene unos 256 píxeles de

ancho, por lo tanto, le restamos a esa cantidad el ancho que ocupa el Sprite, que en nuestro

caso actual son 32 píxeles (8 pixeles x 4 tiles de ancho) y obtendríamos así la posición

máxima a la cual puede llegar el Sprite.

Otra alternativa sería almacenar el valor máximo de la resolución de la pantalla, que ya

hemos indicado que es 256 píxeles. Tras esto, se recoge la posición X del sprite y se le suma

su ancho, obtendremos así la posición exacta hasta la cual podrá moverse el sprite sin que se

salga de la pantalla. Bastara con hacer una simple comparación con el valor del borde

derecho de la pantalla para comprobar si se puede mover hacia esa dirección o no (véase

Figura 53).

Figura 53. Control del movimiento hacia la derecha del jugador.

Fuente: Elaboración propia.

Para, controlar el lado izquierdo de la pantalla el proceso es idéntico al del lado derecho,

pero con la diferencia de que el valor más pequeño que se podrá alcanzar es 0 y no será

necesario saber el ancho del sprite.

Page 70: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

70

8.1.4. Organización del proyecto y makefile

Ahora que ya tenemos un mapa y fondo pintados (aunque no sean nuestros) y podemos

mover el sprite por la pantalla, vamos a organizar un poco nuestro código. Esto es necesario

debido a que en este momento todo lo que hemos realizado lo hemos hecho en un único

fichero main y aunque ahora mismo tampoco tengamos mucho código, conforme vayamos

realizando más mecánicas para nuestro videojuego veremos cómo nuestro código va

creciendo poco a poco, por lo que conviene reorganizar ya nuestro proyecto antes de que se

tengan demasiadas cosas y sea un proceso más complicado de realizar.

Por el momento, solo vamos a añadir un fichero más, que es donde se encontrará todo el

código relacionado con el jugador, es decir, el sprite protagonista del prototipo y, de esta

manera, en el main, solo se encontrará la gestión de arranque de la consola, la carga de los

recursos necesarios y el bucle principal del juego.

La mejor forma para lograr que nuestro proyecto funcione a través de diferentes ficheros, es

mediante la inclusión de una cabecera en todos los ficheros de nuestro proyecto. En esta

cabecera se encontrará todo lo relacionado con la validación de la cabecera de la ROM y la

organización de los bancos y ranuras que ya se había relacionado previamente. Esto se

realiza fácilmente mediante la etiqueta “include” seguido del fichero que contenga la

cabecera.

Además de incluir todo lo necesario para que nuestro proyecto pueda ser ensamblado y

linkado correctamente, también podemos incluir, como veremos posteriormente, nuestras

definiciones o estructuras que necesitemos para nuestro videojuego ya que solo ocuparán

espacio en la consola cuando sean utilizadas.

Esta cabecera es necesaria incluirla en todos los ficheros del proyecto, ya que, si no,

surgirán diferentes problemas: En primer lugar, si en un fichero no se indica la definición

del mapa de memoria con sus bancos y ranuras, se mostrará un error al ensamblar nuestro

proyecto. Por otro lado, si no se incluye la etiqueta “sdsctag” necesaria para poder validar la

ROM del juego, en aquellos ficheros que no contengan dicha etiqueta, saltará un error de

linkado indicando que dichos ficheros no pertenecen al mismo proyecto.

Page 71: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

71

Asimismo, en todos nuestros ficheros se debe indicar en qué dirección se encuentra nuestro

código. Para ello, podemos utilizar una directiva del WLA DX, denominada “.section”.

Mediante esta directiva, si le pasamos el parámetro “FREE”, todo lo que este entre dicha

directiva y “.ends” se colocará dinámicamente en la ROM, es decir, en el primer sitio

disponible de la memoria dentro del banco que se haya indicado previamente. De esta

manera, no hay que preocuparse de estar viendo donde tener que colocar el código y, a su

vez, se aprovecha al máximo el espacio que tenemos disponible en la ROM de la consola.

Figura 54. Ejemplo de uso de la directiva”. section” de WLA DX.

Fuente: Elaboración propia.

Siguiendo estos pasos, podremos conseguir que nuestro proyecto sea construido mediante

diversos ficheros. Sin embargo, para evitar perder tiempo innecesario, debemos de hacer uso

de un pequeño makefile que nos evite tener que estar ensamblado y linkando uno a uno todos

los ficheros del proyecto.

El único problema que podemos encontrar es con el linkado. Mediante el ensamblador WLA

DX, el linkado debe realizarse con fichero denominado “linkfile” el cual contendrá todos los

ficheros con extensión “.o” que se hayan generado tras ensamblar y se vayan a linkar. Este

fichero no se puede generar de manera automatizada y debe actualizarse cada vez que

creemos un fichero nuevo en nuestro proyecto.

Page 72: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

72

Figura 55. Makefile básico para poder generar un archivo ".sms".

Fuente: Elaboración propia.

Con este makefile, desde la terminal podremos utilizar el comando “make” para generar

nuestro ejecutable del prototipo o juego y el comando “make cleanall” para borrar

rápidamente todos los ficheros objeto y el fichero ejecutable que se haya generado mediante

“make”.

8.1.5. Dibujado de un sprite y fondo creados desde cero

Una vez que ya tenemos todo más o menos organizado, vamos a intentar cargar y dibujar un

fondo y sprite creado por nosotros. No vamos a hacer un sprite de cero ni vamos crear los

tiles nosotros, al menos no por ahora, lo que vamos a realizar es descargar algunos gratuitos

de internet.

Lo que se pretenden conseguir con todo esto es usar un fondo y sprite construido por nosotros

(o al menos parcialmente) y observar que funcione correctamente en la consola y verificar

que entendemos todo el proceso que conlleva realizar tal cosa.

Page 73: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

73

Para ello, lo primero que hay que hacer, una vez descargado los tiles que nos interesan, es

cargarlo en Tiled [17]. Tiled es un programa que permite crear tilemaps y exportarlos para

poder integrarlos en nuestro juego, pero, en nuestro caso, lo único que nos interesa es

conseguir la imagen de dicho tilemap y utilizamos tiled porque se pueden cargar tilesets para

formar el tilemap de manera sencilla y rápida, además de que se puede definir el tamaño del

fondo y, así como, el de los tiles.

Figura 56. Tilemap creado con Tiled para representar un nivel de Mágica.

Fuente: Elaboración propia.

Una vez que hemos exportado la imagen del fondo a través del programa, lo que hay que

hacer es reducir su profundidad de color, ya que ahora mismo tiene una profundidad de color

de RGB y hay que convertirla a la de índice de paleta de 256 colores, si no, nos daremos

cuanta que al cargar la imagen en el BMP2TILE no funcionará ya que saltará un error de

formato, debido a que la SMS no soporta dichos colores. Esto se puede realizar con cualquier

programa de edición de imágenes, yo he usado GIMP [21].

Al mismo tiempo, hay que tener cuidado con los colores que se utilizan, ya que en la Master

System solo hay 64 colores disponibles y si ponemos un color que no esté disponible en la

consola tampoco funcionará.

Page 74: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

74

Una vez creado el fondo correctamente, solo hay que cargarlo en la consola como hemos

hecho para el fondo del tutorial, la única diferencia es que habrá que cambiar los valores

para la paleta de colores, los tiles y el tilemap.

Sin embargo, el problema llega a la hora de dibujar un sprite distinto al del tutorial. Vamos

a ver el proceso para cargar un sprite y los problemas que nos encontraremos:

Para obtener el sprite y todos sus assets necesarios se realiza de manera parecida que para el

fondo. Primero, hay que descargar el tileset que contiene el sprite que se desea como, por

ejemplo, The Spriters Resource [10]. Esta página permite descargar y usar sprites siempre y

cuando no sea para uso comercial.

Yo utilizo esta ya que tiene una sección con sprites para la Master System que ya se

encuentran en el formato adecuado para la consola y como por ahora solo se están realizando

pruebas y no va a ser nada definitivo no hay problema con los derechos de autor ya que todos

los recursos que se pueden encontrar aquí provienen de videojuegos conocidos de la época

de la SMS, por lo que desconozco cuanto de fiable es el sitio y estos recursos no van a ser

utilizados para crear el videojuego del proyecto.

Dicho esto, se realiza la carga de dicho tileset en Tiled, se observa cuáles son las dimensiones

del sprite y se crea un mapa con estas dimensiones. Posteriormente, se debe exportar en

formato imagen, se carga en GIMP, se le pone un fondo transparente y se le reduce la

profundidad de color. Con todo esto, se carga en el BMP2TILE y se obtienen los includes

con la información de los tiles y la paleta.

A pesar de todo esto, cuando nos dispongamos a cargar el Sprite ya exportado en el proyecto

y les cambiamos los valores correspondientes para que pueda ser dibujado bien, nos daremos

cuenta de que el sprite no se dibuja correctamente y no sabremos el porqué, ya que se está

realizando lo mismo que con el Sprite del tutorial, pero simplemente cambiando los valores

de tamaño, de la paleta y de los índices de tile.

Page 75: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

75

Aquí es cuando descubriremos una de las cosas buenas que tiene el emulador de MEKA: el

editor de memoria. Este editor, te permite ver el mapa de todas las memorias de las que

dispone la consola y podemos observar que valores se almacenan en cada posición de la

memoria. Además, da la posibilidad de modificar una posición de memoria de manera

sencilla. Gracias a esto, se puede observar la memoria de VRAM en la dirección $3F00, que

como ya sabemos es donde se encuentra el SAT con toda la información de los sprites y así

observar si algo no está funcionando bien.

Figura 57. Editor de memoria del emulador MEKA.

Fuente: Emulador Meka [8].

Observando este editor, cargamos el sprite del tutorial y luego cargamos el sprite que

hayamos descargado nosotros para comparar los valores que hay en uno y en otro dentro de

la tabla del SAT. Al hacer esto nos daremos cuenta de que cuando se carga el sprite del

tutorial todas las hpos tienen un valor de charcode entre ellos, mientras que cuando cargamos

nuestro Sprite, las últimas posiciones de hpos están vacías, es decir, con valor de 00.

Con el fin de asegurarse de que esto no es una casualidad y de que está relacionado, podemos

probar a bajar más sprites y cargarlos en la SMS para ver si ocurre lo mismo. Al realizar

esto, nos daremos cuenta de que en que todos los sprites que se dibujan mal pasa exactamente

lo mismo que el primer sprite que intentamos cargar: hay valores de hpos que no tienen

índices de tile entre ellos y, por lo tanto, cuando esto ocurre los tiles del sprite no se dibujan

en la posición correcta en pantalla.

Page 76: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

76

El origen de este problema reside en el programa de BMP2TILE. En el primer momento,

pensé que no funcionaba correctamente y que genera los índices de tile de manera incorrecta.

Sin embargo, si echamos un vistazo más a fondo al programa y a su documentación oficial,

nos daremos cuenta de que dentro de la ventana relacionada con los tiles del sprite, hay una

pestaña llamada “remove duplicates” que por defecto aparece marcada. Esto quiere decir

que cada vez que esta pestaña esté marcada todos los tiles que se repitan, es decir, aquellos

que sean idénticos a otros que ya están, el programa no los pondrá en el fichero para así

ahorrar espacio en la consola.

Al desmarcar esta pestaña y cargar el nuevo fichero obtenido al proyecto, ya se dibuja

correctamente el Sprite y, además, verifico que todos los hpos tienen sus valores de

charcodes entre ellos.

Todo este tiempo que yo he “perdido”, ha sido básicamente por no haber mirado la

documentación justo cuando empecé a usar el programa, debido a que, en este, entre otras

cosas, se explica todo esto que acabo de comentar. Por lo que os recomiendo

encarecidamente que siempre que uséis un programa nuevo primero os leáis la

documentación que proporciona y así os evitáis este tipo de problemas como me ha ocurrido

a mí.

Figura 58. Dibujado de Sprites antes y después de resolver el problema con BMP2TILE.

Fuente: Elaboración propia.

Page 77: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

77

8.1.6. Generalización de las funciones de dibujado

Una vez que resuelto el problema de dibujado de sprites, vamos a mejorar algunas de las

funciones que se han realizado para que puedan servir para dibujar un sprite de cualquier

tamaño.

El principal problema con las funciones que hemos creado es que solo funcionan para dibujar

un sprite de un tamaño y valores específicos. Esto quiere decir que si ahora, por ejemplo,

queremos dibujar un sprite distinto, con unos tamaños diferentes a los del sprite que tenemos

dibujado actualmente, las funciones de actualizar los valores de X e Y en el SAT ya no nos

van a servir.

Esto es un problema, ya que las funciones encargadas de dibujar un sprite por pantalla son

idénticas, solo cambian los valores de tamaño del sprite en cuestión a dibujar. La idea es

conseguir que estas funciones de dibujado sean generales y puedan ser usadas por cualquier

sprite que queramos cargar, ahorrando así espacio y tiempo. Vamos a ver como generalizar

las funciones de actualizar las posiciones X e Y del sprite con las hpos y vpos del SAT.

Lo primero que hay que hacer es crear una zona de datos dentro del fichero que contiene

todo lo relacionado con el personaje que controla el jugador. Esta zona, contendrá todos los

datos relacionados con el protagonista como pueden ser los valores de X e Y, el ancho del

sprite, el alto del sprite etc. Mediante esto, podemos hacer uso de la instrucción IX del Z80

para crear una función donde lo único que se haga es obtener un puntero que apunte al

principio de los datos del personaje.

Todo esto podemos hacerlo exactamente de la misma manera en cualquier otra identidad que

tenga nuestro juego, ya sea un enemigo o algún objeto. La única condición que hace falta

cumplir es que la zona de datos que va tener cada identidad sea idéntica en todas, es decir,

si en el player la primera posición de memoria de datos almacena al valor de la posición X,

entonces el primer valor de la zona de datos para los enemigos también tiene que coincidir

con la posición X, ya que, si no, a la hora de usar la instrucción IX y acceder a una posición

de memoria en la cual se supone que tiene que contener el valor de la posición X de la

identidad, pero, en realidad es, por ejemplo, el valor de la posición Y, no va a funcionar

correctamente ya que esteremos usando un valor que no es el correcto.

Page 78: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

78

Figura 59. Zona de datos del Player mediante “enum”.

Fuente: Elaboración propia.

Inicialmente comencé a utilizar la etiqueta “enum” para poder definir las variables de

cualquier entidad. Sin embargo, más adelante me doy cuenta de que hay una directiva que

realiza la misma función que “enum”, pero de manera parecida a cómo funciona las

directivas “section” del ensamblador.

Esta directiva se denomina “ramsection” y permite definir una zona de RAM en nuestro

proyecto para poder definir variables. La ventaja que tiene frente a “enum” es que no hay

que preocuparse de dónde colocar las variables, ya que esta etiqueta coloca de manera

automática todo el código escrito dentro de ella a partir de la primera posición de memoria

RAM disponible. Luego, recomiendo que utilices esa directiva en el lugar del “enum”.

Para evitar problemas y facilitar la lectura del código del proyecto, se podrían crear unas

definiciones en la cabecera que indiquen la posición en la que se encuentran cada uno de los

datos de la identidad respecto a la dirección a la que apunta IX, es decir, representarían la

cantidad necesaria a sumar al registro IX para poder acceder a la dirección de memoria donde

se encuentre la variable que queramos utilizar. Por ejemplo, para la posición X del player,

que es la primera posición a la que va a apuntar IX, se podría crear una definición

denominada “spr_x” con el valor 0 indicando que está en la primera posición de memoria.

De esta manera, cuando sea necesario usar el valor X del sprite, se puede acceder a dicho

valor haciendo uso de la instrucción IX junto con el valor de la definición que se ha creado

para poder acceder a la posición correcta donde se encuentra el valor de X.

Page 79: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

79

Además, si en algún momento se cambia el orden en el cual se encuentran los datos de

nuestra entidad, solo haría falta cambiar los valores asignados a las definiciones y no cambiar

el valor en cada uno de los sitios donde ha sido utilizado. Más adelante, observaremos que

esto puede ser automatizado más aun, pero por ahora utilizaremos esta forma.

La única condición necesaria para hacer uso de los registros índice como IX o IY, es que

deben apuntar a la dirección de memoria donde se encuentren los datos justo antes de hacer

uso de ellos. De lo contrario, estaríamos accediendo a valores que no corresponden a los que

se desean y comenzar a aparecer muchos errores.

La figura 60 hace referencia a la función encargada de actualizar la coordenada X del sprite

con el SAT, haciendo uso del registro índice IX.

Figura 60. Función para actualizar la coordenada X del Sprite en el SAT utilizando IX.

Fuente: Elaboración propia.

Como se puede observar, cada vez que necesitemos usar un dato de la identidad, se hace uso

del registro IX y la definición de la cabecera.

Page 80: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

80

Figura 61. Función para actualizar la coordenada Y del Sprite en el SAT usando IX.

Fuente: Elaboración propia.

Las funciones correspondientes a las figuras 60 y 61, son las funciones explicadas en la

sección de dibujado de sprite que han sido generalizadas para que puedan funcionar con

cualquier sprite que queramos dibujar.

8.1.7. Disparo del jugador

Llegados a este punto, ya hemos aprendido, entre otras cosas, a pintar correctamente un

fondo y un sprite. Ahora, vamos a ver cómo realizar una sencilla mecánica de disparo que

permita al jugador disparar una bala cada vez que este pulse el botón de disparo.

Para conseguir esto, es necesario cargar 2 sprites distintos a la vez en la memoria de VRAM

y que estos se muestren correctamente por pantalla. El primer paso que hay que realizar es

averiguar el espacio que ocupan todos los tiles de cada uno de los 2 sprites, debido a que hay

que colocarlos en la misma zona de memoria (a partir de la dirección $2000) y hay que evitar

que se solapen los valores de cada uno entre ellos.

En mi caso, el primer sprite ya cargado corresponde al de Batman. Este está formado por

unos 15 tiles en total, donde cada uno de estos ocupa 32 bytes, lo que quiere decir que el

sprite tendrá un tamaño total de 480 bytes en tiles. Esta información la podemos extraer

directamente del fichero de tiles que obtenemos del BMP2TILE.

Teniendo en cuenta que las posiciones de memoria están en hexadecimal, hay que convertir

el valor obtenido en decimal de los bytes a hexadecimal. Por ejemplo, para mi sprite de

batman, se obtendría el valor $1E0 que corresponde a los 480 bytes de los 15 tiles.

Page 81: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

81

Este valor obtenido, hay que sumarlo a la dirección $2000, ya que esta es la primera dirección

desde donde se va a empezar a cargar los tiles de los sprites. Obteniendo así el valor $21E0,

que corresponderá a la primera posición de memoria a partir de la cual se puede copiar los

tiles del segundo sprite que se quiere cargar, que, en este caso, será el sprite de la bala para

poder hacer el disparo.

Este proceso que acabo de explicar será el mismo para los tiles de todos los sprites que se

quiera cargar en un futuro. Simplemente hay que calcular los bytes que ocupa en total, pasar

el valor obtenido a hexadecimal y sumarlo a la dirección de memoria del último tile cargado

del sprite.

Tras haber cargado todos los tiles, haber indicado cuales son los índices de tile que van a

usar los 2 sprites y cargado las vpos, hpos y los charcodes en el buffer del SAT, ya se puede

visualizar por pantalla el dibujado de ambos sprites.

Figura 62. Dibujado de 2 Sprites: Player y bala.

Fuente: Elaboración propia.

Page 82: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

82

El único problema que seguramente encontréis ahora mismo, son los colores de la paleta, ya

que, para los ambos sprites, se están utilizando los mismos colores, es decir, los colores del

primer sprite que cargamos que corresponden a los primeros valores de la paleta, mientras

que los colores de la bala no se utilizan, ya que estos se encuentran en las últimas posiciones

de la paleta de colores. Por el momento, desconozco la manera de arreglar esto, habría que

averiguar la forma para que cada sprite obtenga sus colores correspondientes. Por ahora,

vamos a dejarlo así y más adelante intentaremos resolver este problema.

Una vez que ya tenemos dibujado ambos sprites por pantalla, es hora de hacer que el sprite

de la bala actúe como tal. Lo primero que vamos a hacer es una función de borrado de

Sprite, que nos permita borrar el sprite de la bala cada vez que llamemos a la función

encargada de realizar este proceso. Esta primera versión de la función de borrado, lo único

que hace es meter el valor 0 en todas las posiciones correspondientes del buffer, es decir, en

las posiciones de vpos, hpos y charcodes del sprite de la bala. De esta manera, el sprite de la

bala dejará de dibujarse.

Para poder saber desde que posición empezar a borrar en el SAT, tenemos que definir en el

proyecto las posiciones iniciales de memoria donde van a empezar las vpos, las hpos y los

charcodes del sprite que queramos borrar. Además, también debemos tener almacenada la

información sobre el ancho y el alto del sprite, para que de esta forma podamos saber hasta

qué posición de memoria hay que copiar los ceros, gracias a que solo hay que hacer una

multiplicación entre ambos valores de ancho y alto.

El problema es que en Z80 no existe la multiplicación como tal, es decir, no existe una

instrucción que permita multiplicar dos registros (o al menos yo no la conozco). Sin

embargo, podemos simular una multiplicación. Para ello, lo que vamos a hacer es sumar

al registro A el valor del ancho del sprite tantas veces como filas tenga dicho sprite. Por

ejemplo, si mi sprite es de 4x3 (4 tiles de ancho y 3 tiles de alto), se suma el valor del ancho,

que es cuatro, un total de tres veces que corresponde a las tres filas de alto que ocupa dicho

sprite, obteniendo así un resultado de 12, que es el resultado de multiplicar 4x3 y que

coincide con la cantidad de ceros que hay que copiar para poder borrar todas las posiciones

verticales del sprite en el buffer (véase figura 63).

No obstante, para poder borrar las posiciones horizontales y los charcodes, hay que copiar

dos ceros por cada iteración que se haga, ya que, recordemos que las hpos y los charcodes

se encuentran juntos en la tabla del SAT.

Page 83: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

83

Figura 63. Función de borrado de la bala.

Fuente: Elaboración propia.

Una vez tenemos la función para borrar el sprite de la bala, lo siguiente que vamos a hacer

es moverla. Para moverla es fácil, al igual que con el movimiento del player, comprobamos

de nuevo el byte que contiene la información sobre el puerto $DC, el cual a su vez contiene

la información sobre las teclas que está pulsando el jugador.

Volviendo a mirar la documentación oficial de la consola [28], nos fijamos que es el bit

cuatro el cual hay que comprobar para saber si se está pulsando el botón de disparo. Si este

bit está a cero, quiere decir que se ha pulsado el botón y cuando esto suceda, lo que haremos

será establecer a uno una variable que tendremos que crear, que en mi caso la he denominado

“disparando”. Esta permitirá saber si el jugador está pulsando o no el botón de disparo.

Aquí surge otro problema y es el problema del tiempo. ¿Cómo se podría hacer para controlar

el tiempo? Es decir, hacer que la bala se dibuje durante x segundos desde que el jugador

dispara y, pasados esos segundos, se deje de dibujar la bala. El caso es que en el Z80 yo no

conozco ninguna instrucción que permita controlar el tiempo y no se me ocurre por ahora

una forma de implementarlo.

Page 84: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

84

Por ello, para este pequeño ejemplo que estamos haciendo vamos a hacer que la bala se

dibuje hasta que “colisione” con algún elemento, en este caso, será la pared derecha o

izquierda del mapa. Pongo “colisión” entre comillas ya que realmente no va a colisionar,

simplemente vamos a comprobar si ha llegado a una determinada posición o no. Más

adelante, realizaremos esto de una manera mucha mejor mediante la comprobación de

colisiones con el mapa.

Para implementar esto que acabo de comentar es muy sencillo: Lo único que hay que realizar

es una comprobación antes de mover la bala. Esta comprobación la utilizamos para saber si

la bala se encuentra ya o no en el límite derecho del mapa (o en el límite izquierdo si se está

moviendo a la izquierda) y como ya tenemos previamente definido el valor del límite derecho

e izquierdo de la pantalla, lo único que hay hacer es comprobar ese valor con el de la posición

del sprite. Si se cumple la condición no se mueve la bala y, en caso contrario, sí que se puede

mover.

Figura 64. Movimiento hacia la derecha del Sprite de la Bala.

Fuente: Elaboración propia.

De esta manera, cuando el jugador dispara, la bala se mueve hasta alcanzar uno de los dos

límites del mapa y ahí se queda parada. Además, si almacenamos la dirección a la que mira

el player podemos hacer que la bala se dirija en la dirección a la que mira el jugador. Lo

único que hay que hacer es crear una variable nueva en nuestro fichero de bala, la cual en

mi caso se llamará bala_dir.

Page 85: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

85

El motivo de crear una variable nueva para la dirección y no usar la misma que tiene ya el

player, es debido a que la del player va a estar cambiando continuamente cada vez que el

jugador mueva el sprite de un lado a otro. Esto lo que originaría es que, si la bala está yendo

en una dirección y el jugador en ese momento mira para el lado contrario, la bala cambiará

su dirección a la misma vez que el player y esto es algo que no queremos que suceda.

Por lo tanto, para evitar esto, creamos una variable nueva la cual va actualizar su dirección

solamente cuando esta haya finalizado su recorrido, que, en este caso en concreto, será

cuando llegue a uno de los límites del mapa.

Asimismo, con otra variable llamada “bala_state”, podemos hacer que el jugador no pueda

volver a disparar hasta que el anterior disparo aun no haya finalizado, evitando así que el

jugador pueda disparar demasiado rápido. Esta variable, proporciona información sobre si

la bala existe o no. La bala existirá siempre que el jugador haya disparado y esta no haya

finalizado su recorrido, cuando lo finalice entonces será cuando la bala dejará de existir,

siempre y cuando el jugador no haya vuelto a presionar la tecla de disparo.

Figura 65. Actualización de todos los aspectos del Sprite de la bala.

Fuente: Elaboración propia.

Con todo esto, ya tendríamos más o menos lo que queríamos. Un personaje capaz de disparar

una bala en función de la dirección a la que este mirando y, además, de que se pueda borrar

dicha bala. Está claro que la forma que lo hemos hecho no es la más óptima posible y

seguramente hay formas mejores de hacerlo, pero de momento cumple su función.

Page 86: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

86

8.1.8. Colisiones entre sprites

Lo que realmente quería implementar ahora son las colisiones con el mapa de fondo, pero

tras estar investigando un poco y no encontrar casi nada de información sobre cómo hacerlo,

además que yo tampoco tengo mucha idea de cómo hacerlo, he decidido que vamos primero

a realizar las colisiones entre sprites que también es algo necesario e importante y más

adelante averiguaremos como efectuar las otras colisiones.

Para poder detectar una colisión entre dos sprites hay que tener en cuenta sus posiciones X

e Y que ocupan en la pantalla, además del ancho y alto del sprite. Con estos 4 valores, hay

que hacer un total de 4 comprobaciones para poder determinar correctamente si se ha

producido una colisión o no entre las dos entidades que hayamos decidido.

¿Por qué 4 comprobaciones? Esto es debido a que hay que comprobar todos los casos

posibles que puedan ocurrir, es decir, hay que comprobar en todo momento si la 1º entidad

que pasamos a la función está situada arriba, debajo, a la izquierda o a la derecha de la 2º

entidad que pasamos también por parámetro a la función dedicada a detectar las colisiones.

Si se cumple que esa primera entidad está en alguna de estas 4 situaciones, entonces no ha

habido colisión entre ambos sprite. En caso contrario, querrá decir que ambos sprites se están

solapando y, por consiguiente, están colisionando. Sabiendo esto, vamos a ver cómo realizar

las primeras dos comprobaciones que corresponden al eje X.

Lo primero que necesitamos son los datos de las dos entidades y, para ello, vamos a hacer

uso de los dos registros índices que existen en el Z80: los registros IX e IY. Recordemos que

antes de hacer uso de cualquiera de los dos registros será necesario guardar en ellos la

dirección de memoria donde empiezan los datos de las entidades que se van a tratar.

Además, al utilizar estos 2 registros podemos hacer que la función nos sirva no solo para

comprobar la colisiones entre dos entidades del juego concretas, sino para todas las entidades

que vaya a tener nuestro juego, de tal manera que la función para detectar colisiones sea una

función general.

La única condición que hay que cumplir es que los datos de cada entidad estén todos en el

mismo orden, para que así a la hora de acceder a los datos coincida, por ejemplo, que la

posición de memoria donde está la información sobre el ancho del sprite sea la misma para

todas las entidades.

Page 87: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

87

Una vez tenemos los dos registros índices apuntando a las dos entidades que queremos

detectar colisión, nos disponemos a hacer la primera comprobación. Vamos a comprobar si

la 2º entidad (la entidad que pasamos en el registro IY) se encuentra situada a la izquierda

de la 1º entidad (la que pasamos por el registro IX) que en este caso en concreto que estamos

efectuando coincidirá con la bala y el enemigo respectivamente.

Para poder ver si la bala esta encima del enemigo o no, hay que comprobar si la posición X

del enemigo es mayor o igual que la posición X correspondiente de la bala sumado al ancho

que ocupa dicho Sprite. Esto resumido sería la siguiente condición: if (ent2_x +

ent2_w <= ent1_x) donde ent1 y ent2 hacen referencia a la entidad que pasamos

por el registro IX (el enemigo) y a la entidad que pasamos por el registro IY (la bala)

respectivamente.

Si se cumple esta condición quiere decir entonces que la bala estará situada a la derecha del

sprite del enemigo y, por lo tanto, no hay colisión. Hay que tener en cuenta que ent2_w

hace referencia al ancho que ocupa el sprite en píxeles no en tiles. Esto lo indico ya que

anteriormente para dibujar los sprites se tomaba el tamaño que ocupaban los sprites en tiles

y no en pixeles, por lo que es necesario añadir dos datos más a cada entidad que indiquen el

alto y el ancho del sprite en píxeles. Esto se consigue simplemente multiplicando el número

de tiles que ocupa por ocho, ya que cada tile en la SMS (si no se activa la visualización de

sprites de 8x16) ocupa 8x8 píxeles.

Dicho esto, y volviendo a la condición anterior para ver si la bala está a la izquierda o no, se

puede ver que es una simple inecuación y como tal podemos despejar la variable ent1_x

hacia la izquierda para tener todas las variables en un lado y que a la derecha se quede un

cero. El resultado tras despejar sería el siguiente: ent2_x + ent2_w – ent1_x <=

0. Luego, si el resultado es menor o igual que cero se cumplirá la condición de que la bala

está a la derecha del enemigo, si no, se estará produciendo una colisión.

Page 88: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

88

Figura 66. Comprobación de si la 2º entidad está situada la izquierda de la 1º entidad o no.

Fuente: Elaboración propia.

A partir de aquí, las tres comprobaciones restantes siguen la misma estructura: cargamos los

valores necesarios en los registros, realizamos las operaciones necesarias de la condición

correspondiente y, finalmente, ponemos los dos saltos condicionales para que salten a la

dirección correspondiente en caso de que no haya colisión y que de esta manera no siga

comprobando el resto de condiciones.

Voy a poner simplemente el código de las condiciones que faltan para poder detectar la

colisión correctamente en todas las direcciones:

• Comprobación en X. ¿2º entidad a la derecha de la 1º? → if (ent1_x +

ent1_w <= ent2_x). Que despejándolo daría lugar a ent1_x + ent1_w

– ent2_x <= 0.

• Comprobación en Y. ¿2º entidad debajo de la 1º? → if (ent1_y + ent1_h

<= ent2_y). Que despejándolo daría lugar a ent1_y + ent1_h – ent2_y

<= 0.

• Comprobación en Y. ¿2º entidad encima de la 1º? → if (ent2_y + ent2_h

<= ent1_y). Que despejándolo daría lugar a ent2_y + ent2_h – ent1_y

<= 0.

Page 89: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

89

Para poder verificar que se están haciendo bien las colisiones lo que podemos hacer es

cargar un valor aleatorio en una dirección de memoria cualquiera cada vez que se produce

una colisión. De esta manera, podemos ver de manera exacta si se produce una colisión

cuando ambas entidades se solapan.

Hay que tener cuidado a la hora de crear los sprites, ya que yo he tenido un problema a la

hora de comprobar si las colisiones funcionaban correctamente. El caso es que al estar

verificando el funcionamiento de la función me di cuenta que las colisiones no eran exactas

e indicaba que se producía colisión cuando no se estaban solapando los sprites.

Tras estar investigando el problema, me di cuenta que lo que pasaba era que al coger los

sprites del tilesheet no los cogí exactos y el fondo del sprite sobresalía por los lados del

mismo. Como yo después lo que hice fue convertir el fondo a transparente mediante GIMP,

a la hora de dibujar el Sprite por pantalla en la SMS, no se ve el fondo ni dichos bordes, pero

el tamaño del sprite sí que incluye los bordes por lo que la colisión se estaba haciendo

teniendo en cuenta esos bordes. Por lo tanto, para poder comprobar que se están haciendo

bien las colisiones he tenido que dibujar los sprites con el fondo original sin convertirlo a

transparente.

8.2. Replanificación

Llegados a este punto me encuentro con un problema bastante grave: el tiempo. Cuando me

encuentro escribiendo estas líneas es principios de abril y solo me quedan unos dos meses

para la entrega del proyecto en junio, lo cual es poco tiempo ya que no tengo realizado nada

jugable.

Está claro que siempre puedo atrasar la entrega y realizarla en septiembre, pero al menos

quiero intentar llegar a esta primera deadline y si no lo consigo que al menos no sea a causa

de que no lo he intentado.

Con esto claro, lo primero que tengo que hacer es una replanificación del proyecto, es

decir, debo planificar todo este tiempo que me queda para ver qué aspectos voy a

implementar del juego y cuáles no, pero siempre con la idea de intentar entregar un producto,

es decir, algo acabado y jugable.

Page 90: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

90

Para ello, mi intención es que lo primero que debo hacer es una pequeña demo jugable de

mi videojuego la cual contenga todas las mecánicas básicas que va a tener, es decir, todo lo

que sí o sí debe estar en mi juego para que este sea algo jugable y acabado, aunque este sea

muy simple.

Tras esto, debo plantearme que más mecánicas podría introducir en lo que me resta de tiempo

para hacer que mi videojuego sea más divertido, pero siempre en mente de que debo entregar

un producto.

Sabiendo esto, he realizado un pequeño calendario donde he escrito todo lo que quiero hacer

dentro del tiempo que tengo (véase figuras 67 y 68). Un pequeño inciso es que el tiempo el

cual dispongo para terminar el juego no es hasta junio, si no hasta el 8 de mayo. Ya que

después de este día tendré que acabar otros aspectos como la memoria del proyecto o

preparar la entrega final.

Figura 67. Planificación abril (preproducción).

Fuente: Elaboración propia.

Page 91: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

91

Figura 68. Calendario Mayo (producción).

Fuente: Elaboración propia.

Como se puede observar, he realizado una planificación de las tres primeras semanas, en las

cuales pretendo obtener todas las mecánicas que deseo. Esto se encontraría dentro del

apartado de preproducción y, tras este, comenzaría el de producción en el cual ya no se

deberían hacer más mecánicas si no que se realizará todo lo relacionado con el contenido del

juego: niveles, sprites, música etc.

8.3. Demo jugable

Tras tener claro más o menos qué es lo que se va a realizar en las próximas semanas, me

dispongo a hacer el primer paso: intentar conseguir una demo jugable de mi juego con todas

las mecánicas básicas del mismo. Esto significa que en esta demo deben estar todas las

mecánicas que deben de estar si o si en el juego para que este pueda ser algo jugable.

Como siempre, intentaré redactar todo el proceso para que otras personas que lo lean les

pueda servir de ayuda y no cometan los mismos errores que yo.

8.3.1. Colisiones con el mapa

Lo primero que he decidido que voy a realizar son las colisiones con el mapa. Esta mecánica

es algo que pospuse anteriormente debido a que no conocía la manera de realizar su

implementación. Sin embargo, ahora no queda otra que encontrar la forma de realizarla, así

que vamos a ello.

Para realizar este tipo de colisiones tenemos que acceder a la zona de memoria donde se

guarda toda la información relacionada con nuestro nivel. Esta información nosotros ya

sabemos dónde se encuentra ya que la hemos utilizado para poder pintar el nivel por pantalla.

Page 92: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

92

Si hacemos memoria, recordaremos que utilizábamos una etiqueta para saber en la dirección

en la que se encontraba el fichero con la información de nuestro mapa, de tal manera que

simplemente llamando a dicha etiqueta ya estaríamos apuntando a la primera posición del

mapa.

Sin embargo, no hay que confundir la etiqueta que almacena los tiles con la del tilemap. A

nosotros nos interesa la que almacena el tilemap, ya que es la que contiene los índices de tile

que se van a dibujar en cada posición de la pantalla, mientras que en la etiqueta de tiles se

encontrarán todos los tiles a utilizar en el mapa.

Figura 69. Zona donde se almacena la información relacionado con el tilemap.

Fuente: Elaboración propia.

Ya sabemos dónde se encuentra la información que necesitamos, ahora tenemos que

averiguar cómo acceder a ella de manera correcta.

El problema que tenemos ahora mismo es que averiguar la manera para acceder al tile exacto

donde se encuentra el sprite el cual queremos detectar colisiones con el mapa. Una forma

para poder obtener esta posición podría ser mediante la división de la posición de nuestro

sprite en pantalla entre ocho. ¿Por qué entre ocho? Bien esto es debido a que cada tile

dibujado por pantalla tiene un tamaño de 8x8 pixeles, por lo que si queremos obtener el tile

exacto donde se encuentra el sprite bastaría con dividir tanto su posición en X como en Y

entre ocho.

Page 93: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

93

No obstante, aquí surge otro problema ¿Cómo hacemos una división en Z80? Ya que, al

igual que con la multiplicación, no hay una instrucción que realice la división tal cual. A

pesar de ello, hay una forma muy sencilla de conseguir la división de un numero por otro en

base binaria.

Si queremos dividir un número binario esto se consigue sencillamente desplazando el byte

un bit a la derecha. Por ejemplo, supongamos que tenemos el valor 1002 (410) y queremos

dividirlo entre dos (21). Simplemente desplazando un bit a la derecha el divisor, ya

obtendríamos el resultado de dividir este entre 2, que en nuestro ejemplo daría 102 (210).

Si queremos hacer la división por otro número, por ejemplo, entre ocho como es en nuestro

caso para obtener el tile del mapa. Bastaría con desplazar tres bits a la derecha el divisor, ya

que ocho es equivalente a 23, es la potencia lo que nos indica el número de bits a desplazar

a la derecha.

Luego si queremos implementar esta operación en Z80, hay una instrucción que hace

exactamente esta operación que acabo de explicar, la instrucción srl. Esta instrucción lo que

va a realizar es desplazar un bit a la derecha el byte del registro que indiquemos,

consiguiendo así la división entre dos del resultado que tengamos almacenado en dicho

registro. Por lo tanto, si queremos dividirlo entre ocho, llamamos tres veces a la función de

srl.

Figura 70. División entre 8 del valor almacenado en el registro A.

Fuente: Elaboración propia.

De esta manera, aplicamos esta división a las posiciones X e Y donde se encuentra nuestro

sprite y así obtenemos el tile exacto donde se encuentra. Obtenido estos valores, los

almacenamos en un registro de 16 bits cualquiera para poder utilizarlos más adelante, ya que

ahora hay que acceder a dicho tile en la zona donde tenemos almacenado nuestro tilemap.

Page 94: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

94

El primero paso es acceder a la fila donde se encuentra el tile. Fijándonos en nuestro fichero

donde almacenos el tilemap, podemos ver que cada fila del mismo ocupa un total de 64

bytes, ya que hay 32 valores, pero cada uno de ellos ocupa dos bytes. Sabiendo esto, nosotros

tenemos la información de la posición Y, es decir, la fila donde está el tile, por lo que

simplemente hay que ir sumando desde la primera dirección donde empieza el mapa hasta

la fila que tengamos que llegar, teniendo en cuenta que para pasar de una fila a otra hay que

sumar el tamaño que ocupa que son 64 bytes.

Figura 71. Acceso a la fila correspondiente donde se encuentra el tile a obtener del tilemap.

Fuente: Elaboración propia.

Llegados a este punto, ya estaríamos en la fila correspondiente del tilemap, ahora hay que

acceder a la columna exacta. Para ello, podemos seguir un procedimiento parecido al que

hemos realizado para las filas, con la diferencia que no hay que no sumar 64 bytes para cada

columna, sino 2 bytes.

Page 95: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

95

Figura 72. Acceso a la columna del tile del tilemap.

Fuente: Elaboración propia.

Ahora ya tendríamos en el registro A nuestro valor del índice de tile en el que se encuentra

nuestro sprite. Ahora lo que debemos hacer es comprobar si ese valor es un valor de colisión

o no. Esta información se puede extraer simplemente observando la imagen de nuestro mapa

y la dirección de memoria donde lo almacenamos. Podremos darnos cuenta así de cuál es el

valor del índice de tile que representa el suelo, por ejemplo, o cualquier otra estructura de

nuestro mapa por el cual nuestro sprite debe colisionar en pantalla.

El aspecto más importante a conseguir en esta función es el de hacer que se convierta en una

función automatizada. De tal manera, que no tengamos que crear va una función distinta por

cada mapa que tengamos.

Es por eso, que debemos tener toda la información de cada uno de nuestros mapas

almacenada. Información como la dirección donde empieza el tilemap o los tiles, cuanto

ocupa cada fila del tilemap etc. De esta manera, simplemente tenemos que llamar a nuestro

registro índice IX o IY para que apunte a la zona donde tenemos almacenado toda esta

información.

8.3.2. Gravedad

Ya tenemos nuestras colisiones del mapa funcionando. Puede ser que no sean las mejores

colisiones del mundo, pero cumplen su función. En nuestra mano está ahora en darles uso

para que parezca que nuestro personaje se mueva realísticamente por el entorno del nivel.

Para ello, una cosa que podemos hacer es mejorar es el salto del sprite.

Page 96: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

96

Si hacemos memoria, recordamos que la forma que hacíamos el salto del jugador era a través

de una tabla que recorríamos, la cual almacenaba valores que representaba la cantidad que

el sprite subía o bajaba en el salto. El problema es que si queremos que el salto parezca un

salto de verdad no podemos definirle nosotros los valores de bajada, ya que siempre va a

bajar la misma cantidad y puede ser que en nuestro mapa haya una caída más grande o más

pequeña. Por lo que, una solución a este problema es dejar que el valor de caída lo defina

nuestro mapa.

El proceso de subida del salto será el mismo, ya que nosotros definiremos cuanto queremos

que salte y a que velocidad. Sin embargo, ahora la tabla terminará justo cuando alcancemos

el valor más alto de subida y, a partir de ahí, utilizaremos otra tabla para representar la

velocidad a la que queremos que caiga el jugador.

Figura 73. Tablas para implementar el salto del jugador.

Fuente: Elaboración propia.

Serán las colisiones del mapa las que nos indicarán hasta cuando tiene que bajar el jugador,

de tal manera que recorreremos la tabla de caída hasta que se colisione con un elemento del

mapa. Para intentar simular que la caída parezca una caída de verdad, lo que hacemos es

recorrer la tabla desde el principio hasta llegar al último valor de la misma. Si al llegar a este

último valor, el jugador aún sigue cayendo entonces se sumará solo el último valor de la

tabla hasta terminar la caída.

Page 97: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

97

Figura 74. Función para controlar la caída del personaje hasta colisionar con el mapa.

Fuente: Elaboración propia.

8.3.3. Control del tiempo mediante VSYNC

Una de los aspectos más transcendentales que hay que implementar en el juego es el tiempo.

Esto es muy importante ya que con él podemos controlar prácticamente todo en nuestro

juego: el tiempo del nivel, el tiempo que lleva la bala disparada, la IA de los enemigos etc.

Por ello, vamos a averiguar cómo podríamos hacer para poder guardar el tiempo que pasa

en nuestro videojuego.

Si nos vamos al apartado de los registros del VDP, podemos ver que en el bit 5 del registro

$01 se utiliza para habilitar/deshabilitar el VSYNC o, también conocido como frame

interrupt.

Page 98: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

98

El frame interrupt es una interrupción que se genera cada vez que se dibuja un frame, es

decir, cada vez que pintamos todas las filas de la pantalla y se vuelve a empezar desde la

primera fila. De esta manera, si habilitamos el VSYNC, cada vez que se pinte un frame y

tenemos las interrupciones habilitadas, la ejecución de nuestro juego saltará a la dirección

$0038, que recordemos es la dirección donde se gestionan las interrupciones en la Sega

Master Sytem.

Por consiguiente, si nosotros tenemos un registro del número de frames que se han pintado

hasta el momento, podemos hacer que cada vez que el juego pinte 60 frames se incremente

el tiempo en un segundo. Este valor de tiempo lo almacenamos en otra variable y así tenemos

almacenado los segundos que han transcurrido desde que se ejecutó el juego.

Figura 75. Contador de frames y de tiempo mediante VSYNC.

Fuente: Elaboración propia.

Con nuestro contador de tiempo aparentemente funcionando podemos hacer alguna prueba

para ver si realmente funciona como debería. Vamos a realizar, por ejemplo, el control del

disparo del jugador.

Vamos a hacer que el jugador solo pueda disparar cada x segundos, de esta manera evitamos

que el jugador pueda realizar más disparos de la cuenta. Para ello, tenemos que tener un

registro del tiempo que ha pasado desde que se ha disparado la última vez y aquí es donde

tenemos que hacer uso de nuestro contador de tiempo.

Page 99: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

99

Cuando el jugador pulse el botón de disparo, tenemos que registrar en otra variable el

segundo exacto cuando se realiza dicho disparo, para ello, lo que hacemos es recoger el valor

de nuestra variable tiempo, que contiene los segundos que han pasado y copiamos su valor

en otra variable llamada tiempoDisparo por ejemplo, la cual almacenará el segundo de

cuando se ha disparado. Esto solo lo haremos si dicha variable de tiempoDisparo estaba a

cero, ya que quiere decir que aún no se había efectuado el disparo anteriormente y es la

primera vez que se dispara.

Sin embargo, si no estaba a cero hay que comprobar si ha pasado el tiempo suficiente para

que el jugador pueda efectuar otro disparo. ¿Cómo hacemos esto? Pues simplemente

restando el valor del tiempoDisparo con el del tiempo del juego. Esto nos dará un valor que

representa el tiempo que ha transcurrido desde que se disparó la bala. Si este valor es mayor

que el que nosotros habíamos indicado, querrá decir que ya se puede disparar otra bala, si

no, entonces aún no se puede disparar otra vez.

Para poder hacer una comparación de mayor o igual en Z80, esto se puede conseguir

mediante la instrucción “jr nc”. Esta instrucción efectuara un salto a una dirección de

memoria siempre y cuando en la última operación no se haya producido acarreo, es decir,

no se requiere de un bit más para poder representar el valor. Lo que es equivalente a hacer

una comparación de mayor o igual en el Z80.

Figura 76. Uso del tiempo para controlar la frecuencia de disparo del jugador.

Fuente: Elaboración propia.

Page 100: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

100

8.3.4. Inicialización de los datos

Lo siguiente que vamos a ver es el proceso de cambiar de nivel dentro del juego, sin

embargo, para poder realizar esto tenemos que tener implementado antes otro aspecto muy

importante y necesario para poder hacer de manera correcta el cambio de niveles, la

inicialización de nuestros datos.

Cada vez que arranquemos nuestro videojuego, este debe saber de alguna forma que datos

son los que necesita inicialmente para mostrar lo que tenga que mostrar, es decir, si nuestro

juego al arrancar muestra el mapa que corresponde al nivel 1 y este a su vez, contiene 2

enemigos, una puerta o lo que sea que necesite. Es por ello, por lo que necesitamos tener

almacenados nuestros datos en algún sitio para que cuando arranquemos el juego, lo primero

que se haga sea acceder a estos datos para saber qué es lo que se va a dibujar y donde.

Esta inicialización no solo es necesaria al arrancar el juego, también la necesitaremos a la

hora de cambiar de nivel, para saber que mapa hay que dibujar, que variables inicializar etc.

Para lograr esto, vamos a hacer uso de las estructuras (structs) que proporciona el

ensamblador WLA DX.

Con el objetivo de tener todo nuestro proyecto de manera más o menos organizada,

crearemos un fichero nuevo que hará de zona de almacenamiento de datos. De esta forma,

en este fichero guardaremos todos los datos que necesitemos en cualquier momento de

nuestro juego, ya sea para inicializar, para guardar información de los sprites o cualquier

otro aspecto relacionado con datos de nuestro juego.

Esta zona de datos, se encontrará en nuestra ROM del juego ya que son datos que no van a

cambiar en ningún momento de la ejecución de nuestro juego y que solo los utilizaremos

para inicializar los diferentes aspectos del mismo.

Antes de inicializar nada, tenemos que crear nuestras zonas de RAM donde vamos a copiar

todos esos datos que requerimos. Para ello, primero usaremos los structs, que permiten crear

estructuras de datos.

Page 101: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

101

Figura 77. Ejemplo de creación de un struct.

Fuente: Elaboración propia.

Como vemos en la figura anterior, de esta manera crearíamos una estructura de datos. Sin

embargo, al hacer esto no estamos ocupando espacio aun, es decir, este código no va a ocupar

espacio en nuestra ROM, ya que funciona igual que las definiciones, hasta que no llamemos

a la estructura no se va generar el código en la consola. Si queremos crear nuestra estructura

de datos, tenemos que utilizar “instanceof”.

Mediante esta directiva, primero escribimos el nombre con el cual queremos llamar a la

estructura, tras esto, escribimos “instanceof”, el nombre de la estructura que queremos

utilizar y el número de estructuras a crear.

Veámoslo mejor con un ejemplo. Basándonos en la figura anterior, observamos que hemos

creado un struct llamado game_objects, el cual nos permitirá tener los datos de todas nuestras

entidades del juego. Enemigos, vidas, balas cualquier entidad que sea necesaria dibujarla por

pantalla o realizar modificaciones de su posición o de cualquier otro aspecto, tendremos que

crear un struct de ella.

Figura 78. Creación struct game_object player.

Fuente: Elaboración propia.

Page 102: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

102

Con el ejemplo que se muestra en la figura previa, estaríamos creando una zona de datos

exclusiva para el player, es decir, es como si estuviésemos reservando memoria para todos

los datos de nuestro jugador. Datos que corresponderán a los que hay indicados en el struct

que hemos hecho referencia que en este caso es el de game_objects.

Pero la principal ventaja que proporcionan los structs es que podemos acceder a ellos de

manera bastante sencilla utilizando los registros índices ya que, como se observa en la

siguiente figura, el ensamblador nos genera automáticamente etiquetas con el nombre de

la estructura que hemos creado y cada una de las variables del mismo.

Figura 79. Símbolos generados automáticamente con el struct del player.

Fuente: Elaboración propia.

Gracias a esto, si queremos acceder en cualquier momento al valor de la posición X del

jugador, esta información la obtenemos con la etiqueta “player.x”. Pero, no solo eso, si

además tenemos varios game_objects y queremos hacer una función que sirva para todas las

entidades de nuestro juego, como nuestras funciones de copiar datos al buffer o de dibujado

de sprites, podemos apuntar con IX al principio de la estructura de nuestra entidad y, cuando

queramos acceder a cualquier dato del mismo, simplemente con hacer

(IX+game_object.estado) ya estamos automáticamente apuntando a la zona del struct que

almacena los datos de la variable que queremos obtener y esto nos sirve para todas las

entidades que utilicen la misma estructura, ya que la disposición de los datos siempre va a

ser de la misma manera.

Ahora que ya sabemos cómo crear nuestras zonas de datos para cada una de nuestras

entidades del juego, lo único que nos queda es inicializar estos datos a sus valores iniciales.

Page 103: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

103

Hay diferentes maneras que podemos implementar esto, pero la manera más sencilla es

utilizando la instrucción del Z80 denominada “ldir”. Mediante esta instrucción podemos

realizar la copia de la cantidad de datos que nosotros queramos, desde una zona de memoria

a otra.

Para poder utilizar esta instrucción, primero en el registro HL, tenemos que almacenar la

dirección de memoria donde se encuentran los datos que queremos copiar. Como hemos

visto antes, hemos creado una zona exclusiva para almacenar todos nuestros datos, por lo

que simplemente tenemos que crear una etiqueta dentro de esta zona, que almacene la

dirección donde comienzan los datos que queremos duplicar. Una cosa que hay que tener en

cuenta, es que estos datos deben estar escritos de la misma forma que lo está el struct a donde

pretendemos copiar los datos, ya que, en caso contrario, almacenaremos los datos en las

variables incorrectas.

En segundo lugar, en DE, necesitamos tener la dirección a donde queremos copiar esos datos

a los que apuntamos en HL. En nuestro caso, almacenaremos en DE la etiqueta player, que

contiene la dirección donde empieza la zona de datos del jugador. Recordemos, que esta

zona de datos se encuentra en RAM, ya que van a ser modificados durante la ejecución del

juego.

Finalmente, tenemos que tener en el registro BC, el valor que indique la cantidad de datos

que queremos copiar. Este registro se utilizará como contador para saber cuántas posiciones

de memoria se tienen que avanzar tanto en HL como en DE. Siguiente el ejemplo de las

figuras anteriores, queremos copiar 17 bytes en total.

Figura 80. Ejemplo copia datos con ldir.

Fuente: Elaboración propia.

Page 104: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

104

8.3.5. Cambio de mapas

El proceso de cambio de nivel, una vez que ya sabemos cómo inicializar nuestros datos, es

bastante sencillo. El único problema que puede llegar a ocurrir sea que con tantos datos nos

hagamos un lio y no sepamos donde están cada uno de ellos, o copiemos los datos que no

son etc. Es muy importante tener bien organizado nuestro proyecto para que en todo

momento lo tengamos bien estructurado y diferenciado en sus diferentes partes, para evitar

futuras confusiones.

Es por eso, por lo que considero necesario crear un fichero aparte que se encargue única y

exclusivamente de almacenar todos nuestros datos que necesitemos inicialmente, tanto a la

hora arrancar la consola como al hacer el cambio de nivel. De esta manera, evitamos lo

sobrecarga de código en los otros ficheros de nuestro proyecto.

Sabiendo esto, la mecánica de cambio de nivel, implica saber que datos vamos a necesitar

para el siguiente nivel. Estos datos pueden ser desde el mapa que queremos dibujar hasta el

número de entidades que van a haber o en qué posición del SAT se va a dibujar cada sprite.

La mayoría de estos datos ya los tenemos en nuestra estructura de game_objects, donde entre

otras cosas almacenamos la información relacionada con la dirección a donde copiar las

posiciones verticales y horizontales del sprite del game_object.

Sin embargo, no tenemos información sobre el nuevo mapa que hay que dibujar, el valor del

tile que indica las colisiones con el mapa o el número de cada tipo de entidades que van a

haber en el nivel. ¿La solución? Crear otra estructura para almacenar los datos de los

mapas.

Esta estructura almacenará toda la información que he comentado anteriormente. Además,

crearemos una zona extra de datos, para poder almacenar aspectos como el número de

enemigos o el de cualquier otra entidad. El motivo de esto es debido a que, aunque ya

tengamos en el struct del mapa los datos que indican el número de entidades de cada tipo

que van a haber, tener los datos en variables aparte del struct facilita su acceso, que, en caso

contrario, deberíamos hacerlo mediante los registros índice para apuntar al mapa que

corresponda al del nivel que nos encontramos y a su vez acceder a los datos de este para

saber la cantidad de entidades.

Page 105: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

105

Si todo esto, en vez de hacerlo repetidamente cada vez que necesitemos acceder a los datos,

lo realizamos solo una vez justo cuando cambiamos de nivel, nos ahorraremos mucho tiempo

de ejecución y de espacio.

Con todo esto, la única información que nos falta es saber cuándo hay que hacer el cambio

de nivel y que nivel hay que cargar. Para esto, simplemente creamos dos variables, una que

almacene el nivel en el que estamos y otra que almacene dos valores (0 o 1) cuando está en

un valor quiere decir que no queremos cambiar de nivel, si está en el otro queremos cambiar.

Ambas variables se actualizarán al terminar el proceso de cambio de nivel.

Figura 81. Proceso de cambio de nivel del juego.

Fuente: Elaboración propia.

Ahora, cada vez que queramos cambiar de nivel, simplemente hay que cambiar ambas

variables cuando lo necesitemos a su valor correspondiente y cuando entremos en la función

de la figura previa, se hará el cambio automáticamente.

Dentro de cada nivel, debemos tener todos los datos que requiere este: dirección a la zona

de datos del mapa, inicialización de todos los game_objects que hay en el nivel, así como

del número de entidades de cada tipo etc.

Por otro lado, antes de hacer el cambio, siempre que no sea el primer nivel, debemos borrar

toda la información del nivel anterior. Esto es información relacionada con el mapa a dibujar,

borrar todos los sprites del SAT para evitar que se queden dibujados y desactivar todas las

entidades para que no se sigan haciendo actualizaciones de los mismos.

Page 106: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

106

Para borrar el mapa del nivel anterior, simplemente cargamos el tilemap del nuevo mapa y

ya se haría el borrado del mismo. Para los sprites del SAT, bastaría con llenar de ceros toda

la tabla que almacena la información de los mismos y como sabemos cuánto ocupa gracias

a nuestro buffer del mismo, este proceso es bastante sencillo.

Figura 82. Función para borrar los sprites de la pantalla.

Fuente: Elaboración propia.

Finalmente, lo único que falta es “matar” a las entidades para que no continúen haciendo

cosas. Como ya tenemos la información de cuantas entidades hay de cada tipo en el nivel,

simplemente hay que hacer un bucle que recorra todas las entidades y ponga su estado a 0.

Figura 83. Función para "matar" todas las entidades del nivel.

Fuente: Elaboración propia.

Page 107: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

107

Pero, para que esto funcione, hay que hacer que todas nuestras entidades solo funcionen si

su estado está activo, es decir, está a 1. Si no, aunque hagamos esto seguirán actualizándose

en los siguientes niveles.

Por último, a la hora de cargar los mapas, el programa BMP2TILE tiene una característica

muy útil que nos ahorra bastante trabajo y es el poder indicar el valor del primer índice de

tile del mapa que vamos a exportar. De esta manera, los valores del tilemap del mapa se

actualizarán automáticamente en función del valor del primer índice de tile y podremos

colocar los tiles deñ mapa en memoria justo después de donde acaba el mapa anterior, ya

que el tilemap ya cogerá automáticamente los tiles que le corresponden.

También es recomendable hacer esta carga de los tiles justo al arrancar la SMS y no cuando

se hace el cambio de nivel, ya que es preferible que ese tiempo que va a durar la carga de los

tiles de los mapas sea al arrancar la consola y no durante la ejecución del juego.

8.3.6. Muerte del jugador

Con el cambio de nivel aparentemente funcionando es hora de implementar la mecánica de

muerte del jugador. Esta mecánica requiere de dos aspectos: primero necesitamos saber la

vida restante del jugador para saber si este ha muerto o no y, por otro lado, si el jugador

muere, es necesario reiniciar el juego en caso de que este lo desee.

El primer punto es bastante sencillo de implementar, ya que todos nuestros game_objects

van a tener una variable que servirá para controlar el número de vidas restantes que le

quedan, cuanto este valor esté a cero, entonces es cuando la entidad muere.

Para restar una vida, basta con acceder a la variable de vida del struct de game_objects y

decrementar dicho valor en uno. Si después de decrementar, el valor es igual a cero, entonces

se ha muerto la entidad.

Page 108: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

108

Figura 84. Función para decrementar la vida del jugador una unidad.

Fuente: Elaboración propia.

La variable mundo_state es una variable que veremos en profundidad más adelante, pero

básicamente se utiliza para controlar los distintos estados en los que se encuentra el juego.

En este caso, cuando el jugador muere, cambiamos al estado de muerte y, por lo tanto, la

ejecución de nuestro programa saltará a la dirección encargada de gestionar esta muerte.

La gestión de la muerte consiste en un “reset” del juego (reinicio en español). Esto quiere

decir que tenemos que hacer que el juego vuelva a estar como cuando arrancamos la consola.

Para ello, una vez que la ejecución del juego salta a la zona de reinicio, hay que hacer

diferentes cosas:

• Vaciar el SAT: Tenemos que vaciar nuestra tabla de sprites para evitar que estos se

queden dibujados en pantalla en caso de que no hayan sido borrados en el nivel donde

se produjo la muerte. Nosotros sabemos cuánto ocupa dicha tabla gracias al buffer

del SAT que construimos anteriormente, por lo que lo único que hay que realizar es

rellenar de ceros toda esta zona de memoria hasta que ya no queden posiciones

restantes.

Page 109: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

109

• Inicialización de datos: Al igual que al arrancar la consola, tenemos que volver a

cargar todos los datos iniciales que requiere el primer nivel. Como estos datos los

tenemos guardados y ya sabemos cómo realizar una copia rápida y sencilla mediante

la instrucción “ldir”, hacemos la copia de todos estos datos a sus direcciones

correspondientes. Además, como esta copia se va a realizar de forma idéntica tanto

para arrancar la consola como para reiniciar el juego, es conveniente hacer una

función que sirva para ambos casos y así nos ahorramos espacio en memoria. No

olvidemos que estos datos también incluyen aquellos que hacen referencia al nivel,

como la variable que indica el nivel en el que se encuentra el jugador y la que indica

que queremos hacer un cambio de nivel. Como ya implementamos el cambio de nivel

anteriormente, cuanto se termine la función de reinicio, se llamará a la de comprobar

el nivel y está se encargará de borrar el mapa anterior, cargar el siguiente tilemap,

controlar el número de entidades etc.

• Cargar sprite del jugador y copia al SAT: Lo último que tenemos que implementar

para poder conseguir hacer el reset de manera correcta, es cargar el sprite del jugador

al buffer, ya que es el único sprite que sabemos que si o si hay que dibujar en pantalla

después de reiniciar. Podríamos dibujar otros sprites que sabemos que también están

en nuestro nivel, pero si más adelante los modificamos, nos tocará cambiar también

aquí todos los sprites que no dibujamos, mientras que el jugador sabemos que

siempre se va a dibujar. Tras cargar el sprite, solo nos queda llamar a la función que

copia el buffer al SAT y ya tendríamos nuestra función de reiniciar el juego. Lo único

necesario es llamar a la función de cambio de nivel, para que se gestione los aspectos

restantes como el borrado del mapa y carga del siguiente.

8.3.7. IA primer tipo de enemigos

Una de las últimas mecánicas que voy implementar para que el videojuego sea un juego

jugable, podría ser la de los enemigos. En cualquier juego de plataformas es necesario que

haya algún tipo de enemigo o IA con la cual jugar contra ella o con ella. En este caso, vamos

a ver cómo realizar la IA de un enemigo muy simple, de tal manera que podamos matarlo y

viceversa.

Page 110: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

110

Para implementar la IA de este enemigo y la de los siguientes, vamos a utilizar una técnica

conocida como máquina de estados. La máquina de estados, de manera resumida, es una

de las metodologías que se utiliza para implementar la inteligencia artificial de los

videojuegos, donde se parte de un estado o comportamiento, mientras el cual esté activo, la

IA del enemigo o entidad correspondiente realizará la acción o acciones que se hayan

indicado dentro de este estado. Esto se realizará hasta que se cambie de estado a otro, donde

se realizará otras acciones distintas hasta que vuelva a cambiar al estado siguiente o vuelva

al anterior.

El motivo de usar está técnica y no otras es debido a que es una técnica bastante sencilla de

implementar y teniendo en cuenta que debemos realizarla en Z80, creo que es la más

conveniente y aparte de que no se va a implementar una IA demasiado compleja que requiera

utilizar otra técnica distinta a esta.

Vamos a tomar como referencia a uno de los enemigos que aparecen en el videojuego Master

of Darkness para la Sega Master System, que son los perros. Este tipo de enemigos, a simple

vista se puede apreciar que dispone de dos estados de IA diferentes: un estado de reposo, por

así llamarlo, en el cual la entidad se encuentra quieta en el lugar y, por otro lado, está el

estado de ataque, donde el perro, cuando ve al jugador, comienza a correr de un lado a otro

durante un tiempo determinado, donde incluso puede llegar a saltar plataformas. Si durante

este proceso se cruza con el jugador, este recibe daño.

Basándonos en este tipo de enemigo, vamos a crear nosotros uno bastante parecido, pero

más simple. Nuestro enemigo también va a disponer de dos comportamientos: el de reposo

y el de ataque. Durante el primer estado, la IA lo que va realizar es quedarse quieta durante

los segundos que le indiquemos. Tras transcurrir los segundos necesarios, esta cambiará al

comportamiento de ataque, durante el cual se moverá de una posición a otra hasta que, de

nuevo, se agote el tiempo disponible para el comportamiento. Lo que pretendemos conseguir

aquí es que el jugador se dé cuenta de que una vez que transcurra un breve lapso de tiempo,

el enemigo dejo de correr por lo que es más fácil eliminarlo sin recibir daño que cuando este

se mueve. Pero ahí es donde el jugador tiene que decidir si perder el tiempo en esperar a que

acabe o jugársela a matarlo antes con el riesgo de perder una vida o varias.

Para conseguir esto, tenemos que implementar una función que se encargue de gestionar

todo lo relacionado con el enemigo mientras este esté vivo y que cuando muera, se deje de

controlar y se borre del juego.

Page 111: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

111

Para lo primero, podemos realizarlo de forma sencilla, ya que como sabemos, tenemos una

variable que nos indica el estado del enemigo (si está vivo o no) por lo que cuando esta esté

a cero, no haremos ninguna de las funciones de la entidad.

Figura 85. Comprobación del estado del enemigo.

Fuente: Elaboración propia.

Evidentemente, como podemos ver en la figura previa, necesitamos llamar antes a una

función encargada de controlar el estado del enemigo y borrarlo en caso de que este muera

ya sea por la causa que sea.

Figura 86. Función para comprobar si hay que eliminar al enemigo o no.

Fuente: Elaboración propia.

Page 112: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

112

Para comprobar si el enemigo hay que borrarlo o no, podemos hacerlo con nuestra variable

de colisión de los game_objects. Cuando esta variable esté activa, querrá decir que ha

colisionado con alguna otra entidad del juego. Como ya sabemos, en la variable se almacena

el identificador de la entidad con la que colisiona, por lo que comprobamos dicho valor para

comprobar si es el de la entidad de bala o no.

Nosotros comprobamos si es de la entidad de la bala porque es lo único (por ahora) por lo

que podría morir el enemigo, pero aquí podríamos poner cualquier otro identificador por el

que nosotros queramos que el enemigo muera. En función del identificador que sea, si al

colisionar y llamar a la función comprobar vida de los enemigos esta devuelve cero, querrá

decir que ha muerto y hay que borrarlo del mapa.

En caso contrario de que la entidad haya muerto, entonces tenemos que comprobar el estado

de la IA en la que se encuentra, en el estado reposo o de ataque, para ello podemos utilizar

una variable para controlar este aspecto. En función del valor que sea, cero o uno, la entidad

tendrá un comportamiento u otro.

Comenzaremos primero con el estado de reposo, como ya hemos visto, necesitamos

controlar el tiempo que lleva el enemigo en cada uno de los estados. El proceso para calcular

el tiempo transcurrido siempre va ser el mismo: comprobamos si la variable encargada de

almacenar el tiempo es distinto de cero o no, ya que si está a cero quiere decir que el estado

aún no ha sido activado y por lo tanto, necesitaremos registrar el segundo exacto en el que

se entra a la función, que como ya hemos hecho otras veces, esta información la recogemos

de la variable tiempo que recordemos que se incrementa en uno cada vez que se dibujan

sesenta frames. Si, por el contrario, el valor de tiempo del estado es distinto de cero, entonces

el estado ya ha comenzado y debemos comprobar cuanto tiempo ha transcurrido con la

instrucción “jr c” que como sabemos era el equivalente a hacer una comprobación de mayor

o igual en el Z80.

Como en el estado de reposo lo único que hace el enemigo es quedarse quieto hasta que pase

el tiempo necesario, cuando el tiempo haya acabado cambiamos el estado de la IA al

siguiente valor, para que así la siguiente vez que se entre al update del enemigo se haga el

siguiente comportamiento.

Page 113: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

113

Figura 87. IA enemigos estado reposo.

Fuente: Elaboración propia.

Para el estado de movimiento la primera parte va ser idéntica a la del estado de reposo, con

la diferencia de que habrá que utilizar otra variable distinta para controlar el tiempo de este

estado. Mientras que se esté en este estado, el enemigo tendrá que moverse entre las distintas

posiciones que nosotros le indiquemos, sin embargo aquí puede surgir un problema y es el

de que cuando el enemigo entre en este estado siempre debe coger la misma posición como

margen de distancia, de tal manera que, independientemente de donde se quede al acabar el

estado de movimiento, siempre recorrerá la misma distancia y no se desplace por el nivel de

manera incontrolada.

¿Cómo hacemos esto? Muy sencillo, al inicializar los datos del enemigo, podemos utilizar

una dirección de memoria para guardar la posición de inicio del enemigo, la cual

corresponderá a la posición donde aparece el mismo al cargar el nivel y así siempre tendrá

esta posición como referencia y sabremos que no ocurrirá nada raro ya que la hemos definido

nosotros y la tenemos controlada.

Page 114: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

114

Además de esta variable, necesitamos información sobre la cantidad de desplazamiento, es

decir, cuánto se va a poder mover desde esa posición inicial. Podemos definir un valor que

represente esta distancia de desplazamiento del enemigo tanto para la izquierda como para

la derecha y de esta manera ya tendríamos una manera de hacer que el enemigo se mueva

entre dos posiciones distintas y siempre la misma cantidad.

Tras leer esto, puede que nos hayamos dado cuenta de que necesitamos unas cuentas

variables extras para gestionar la IA de nuestro enemigo. Podríamos simplemente añadir

estas variables a nuestra estructura de game_objects y así tener toda la información en una

única estructura, sin embargo, no sería eficiente, ya que estas variables solo nos van a servir

para los enemigos y para el resto de entidades no. Por lo que estaríamos ocupando espacio

de memoria RAM que no vamos a usar nunca. Si que es cierto que obtendríamos la ventaja

de poder acceder a esta información con solo un registro índice, pero sigue sin ser suficiente

como para salga rentable utilizar este método.

La forma más eficiente sería crear una nueva estructura encargada de almacenar esa

información extra que requieren los enemigos como puede ser el tiempo de cada

comportamiento o la posición de inicio.

Con toda la información ya almacenada, ya podemos usarla para hacer el movimiento del

enemigo. Esto se hará igual que mover cualquier otra entidad, con la diferencia de que se

podrá mover a la derecha o a la izquierda siempre que no haya superado el límite máximo

de distancia a recorrer que le hayamos indicado.

Page 115: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

115

Figura 88. Movimiento de los enemigos.

Fuente: Elaboración propia.

Con todo esto, lo único que faltará sería llamar a la función para que dibuje el sprite del

enemigo siempre que este esté vivo y ya tendríamos a nuestro enemigo simple funcionando.

Lo normal sería que quisiésemos pintar más de un enemigo a la vez, por lo que tenemos que

hacer que el update de los enemigos se haga para todos los enemigos que hayan en el nivel.

Como habrás podido notar, en los ejemplos que he compartido se hace uso de los registros

índices para acceder a los datos de los enemigos y como ya deberías saber esto permite que

la función sea general y se pueda usar para todos los enemigos y no solo uno.

Pero, para poder usar los datos de cada enemigo, debemos estar apuntando previamente con

cada registro índice a la dirección de memoria correcta que almacena la información y

además debemos hacer esto de manera automatizada, es decir, no deberíamos tener que

indicarle nosotros manualmente la dirección de cada una de las entidades, teniendo en

cuenta también que el número de enemigos variará en función del nivel.

Page 116: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

116

A pesar de todo, hacer que todo el update de los enemigos funcione de manera automatizada

no es tan complicado, aunque sí que puede llegar a dar problemas si nos liamos con los datos

y sus tamaños.

La información del número de enemigos que hay en el nivel ya sabemos que la tenemos

almacenada, por lo tanto, este valor lo usaremos para saber cuántas veces tenemos que hacer

el bucle o si no tenemos que hacerlo en caso de que no haya enemigos. En caso afirmativo,

lo siguiente que tenemos que hacer es apuntar con los dos registros IX e IY al principio de

los datos del enemigo actual que se esté actualizando. Para ello, podemos apuntar a los datos

del primer enemigo y a partir de ahí avanzar hasta los datos del enemigo correspondiente a

actualizar. Pero para poder hacer esto necesitamos saber en todo momento el tamaño de

datos que tiene cada enemigo y gracias a las structs del WLA DX, está información se guarda

automáticamente en una variable del tipo “sizeof” por lo que cada vez que se haga una

iteración del bucle, al final sumamos al registro índice el tamaño de los datos del enemigo y

de esta manera al empezar el bucle de nuevo ya estamos apuntando a los datos correctos del

mismo.

Figura 89. Update para más de un enemigo.

Fuente: Elaboración propia.

Page 117: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

117

Hay que tener cuidado también con no perder los valores de los registros durante el bucle,

como por ejemplo el valor del contador del bucle. Este valor lo estamos almacenando en el

registro B y lo más probable es que sea modificado a lo largo de la iteración del bucle por lo

que para evitar problemas posteriores, apilamos el registro BC en la pila, ya que para hacer

un push a la pila se tiene que hacer con registros de 16 bits y cuando necesitemos recuperar

el valor del contador, que será antes de acabar la iteración, hacemos pop para desapilar de la

pila.

8.3.8. Recibir daño

Ya tenemos un enemigo muy simple funcionando, por lo que el último paso para conseguir

tener algo con lo que poder jugar es conseguir que ese enemigo nos haga daño y así poder

morir. La manera de comprobar cuando hay que restar una vida al jugador ya lo tenemos

implementado en la función de comprobar colisión, la cual solo hay que pasarle en los

registros IX e IY, los datos de las dos entidades a colisionar, que en este caso en concreto

serán el jugador y el enemigo.

No obstante, hay que hacer una comprobación de colisión con todos los enemigos que haya

en el nivel y no solo con uno, pero esto ya sabemos que podemos controlarlo con la variable

de número de enemigos del nivel y el tamaño de datos de cada uno como ya hemos realizado

anteriormente en el update de los mismos, por lo que el proceso es el mismo pero llamando

a la función de comprobar colisión y tras este comprobar si el id devuelto en colisión coincide

con el de los enemigos.

La cuestión que quiero explicar reside en cuando el jugador recibe daño por parte de

cualquier fuente, ya sea por un enemigo, por una bala, por una trampa o cualquier otra cosa.

Aquí debemos implementar una mecánica que seguramente todos habremos visto en muchos

juegos, el de la invulnerabilidad.

A lo que me refiero es al lapso de tiempo durante el cual el jugador no puede recibir daño

una vez que este ya ha recibido daño. De esta manera, cuando el jugador reciba daño tendrán

que pasar unos segundos hasta que pueda volver a recibir daño, de esta manera evitamos así

que el jugador pierda demasiadas vidas de golpe y que disponga de un tiempo para

recuperarse.

Page 118: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

118

La implementación es sencilla, de hecho, es igual que cualquier otra función de comprobar

el tiempo transcurrido, ya que la manera a implementar esta mecánica es mediante el control

del tiempo con una variable. Si acabamos de recibir daño, es decir, la variable estaba a cero,

entonces registramos el segundo cuando recibimos daño y le restamos una vida al jugador.

En caso contrario, se comprueba cuanto tiempo ha pasado y si no ha pasado el tiempo

necesario no se resta una vida.

8.4. Mecánicas extra

Hasta este punto ya tendría lo que sería una pequeña demo del juego funcional con todas las

mecánicas básicas del mismo, es decir, todas aquellas que son si o si necesarias para que se

pueda jugar. Estas mecánicas consisten en las siguientes:

• Mecánicas del jugador: salto, disparo, movimiento, caída y recibir daño.

• Colisiones entre sprites.

• Colisiones con el mapa.

• Cambio de nivel.

• IA de un enemigo.

• Reinicio del juego.

• Control de tiempo.

A partir de ahora, se van a implementar todas las mecánicas extras que sean posibles dentro

del tiempo disponible para ello, que harán que el juego sea más divertido, pero que no son

estrictamente necesarias para que el juego funcione de tal manera que si hay alguna que por

cualquier razón no se puede integrar en el proyecto este no se vea afectado.

8.4.1. IA segundo tipo de enemigos

Teniendo solo un tipo de enemigo en el juego seguramente hará que la gente que lo juegue

se acabe cansando de enfrentarse siempre a lo mismo. Es por eso, por lo que considero que

la primera mecánica a añadir sea un segundo tipo de enemigo diferente y de esta manera,

añadir una mayor variedad al videojuego. Me gustaría añadir un par más de tipos enemigos

distintos, pero seguramente el tiempo no me lo permita. Además de que hay unas cuantas

más mecánicas que deseo implementar también.

Page 119: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

119

Al igual que el primer enemigo, nos vamos a fijar de nuevo en uno de los enemigos que

aparecen en Master of Darkness. Este enemigo, al igual que el perro, se desplaza

continuamente dentro de una zona previamente indicada. Esto correspondería a su primer

estado de comportamiento, el cual, al finalizar, cambia a su siguiente comportamiento donde

se quedará quieto durante unos segundos. Durante este tiempo, la entidad lo que realiza es

cambiar consecutivamente su sprite de derecha a izquierda para dar así la impresión de que

el enemigo está mirando de un lado a otro, tras pasar los segundos necesarios, disparará hacia

una dirección que intuyo que corresponderá a la dirección donde se encuentra el jugador.

Como vemos, la IA es un poco más compleja pero tampoco en exceso, el mayor problema

que podemos llegar a tener es gestionar el comportamiento de las balas que dispara cada

enemigo, porque es posible que deseemos tener este tipo de enemigo repetido más de una

vez en el nivel y tenemos que hacer que la bala sea independiente de cada enemigo. En

cuanto a las animaciones del mismo, esto es algo que ya veremos más adelante cuando nos

encontramos en el apartado de añadir contenido al juego.

Para poder tener claramente diferenciados los dos tipos de enemigos que hay en el juego, se

utilizará el término de “básicos” para hacer referencia al primer tipo de enemigo que se

implementó anteriormente y el término “avanzado” para el siguiente tipo de enemigo que se

va a implementar a continuación.

La estructura para crear los enemigos avanzados va ser prácticamente la misma que hemos

implementado para los básicos. Por lo tanto, tendremos que crear una función que recorra

todos los enemigos avanzados que existen en el nivel y apuntar con los dos registros índices

a las estructuras de datos de cada uno de ellos. Esto es exactamente igual que la función de

actualizar los enemigos básicos, simplemente hay que cambiar la función a la que se llama

para hacer todo lo relacionado con ellos. Se podría haber implementado una función general

que sirva tanto para un enemigo como otro, pero no dispongo del tiempo para realizar tal

tarea actualmente.

De nuevo, dentro del enemigo avanzado, seguirá siendo una estructura bastante parecida a

la de los básicos. Primero comprobamos si la entidad esta viva, para ello podemos utilizar la

misma función que usamos para los básicos, la denominada “checkEstadoEnemigo” que

comprobaba si había que borrar al enemigo pasado en el puntero IX en función del valor de

su variable de su colisión y como se utilizan los registros índices, podemos usar la función

de manera general para todos los tipos de enemigos.

Page 120: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

120

Por otro lado, tendremos también una variable para controlar el comportamiento de la

entidad, al igual que los básicos, en función de su valor hará un estado u otro. El primer

estado, el de movimiento, utilizaremos las mismas funciones que el primer tipo de enemigo

ya que va a hacer lo mismo, pero a una velocidad de movimiento reducida que el primero.

Por eso, es necesario añadir una variable extra dentro de nuestra estructura de variables extra

de los enemigos, la cual nos proporcionará información sobre la velocidad a la queremos

que vaya el enemigo. Con esto, solo tenemos que llamar a la función que controla el estado

de movimiento de la entidad y ya tendríamos este primer comportamiento funcionando.

Hay que tener cuidado cuando modificamos cualquiera de las estructuras de datos que

tenemos, ya que si añadimos o quitamos un dato su tamaño cambiará y por lo tanto, a la hora

de copiar todos los datos iniciales que tenemos en ROM al struct, tenemos que cambiar el

tamaño que se pasa al registro BC que requiera la instrucción “ldir”. Este es un problema

que he me ha surgido varias veces durante el desarrollo del proyecto, ya que muchas veces

olvidaba donde tenía que realizar las modificaciones correspondientes y el juego empezaba

a fallar ya que se asignaban valores erróneos a las variables de los structs.

Para evitar estos problemas recomiendo utilizar constantes, que en Z80 sería definiciones,

que representen el tamaño total a copiar a nuestra RAM. De tal manera que, si hacemos una

modificación, solo tenemos que cambiar el valor aquí y no en todos los sitios donde lo

utilicemos.

Siguiendo con el tema de los enemigos avanzados, solo nos quedaría implementar el estado

de ataque del mismo. Al igual que siempre, el tiempo lo controlamos de la misma forma que

hemos realizado hasta ahora. Dentro del comportamiento de ataque, el enemigo lo que va a

realizar es comprobar a que dirección disparar, para ello, durante los segundos que dure este

estado, el enemigo alternará entre los dos sprites que disponga para observar hacia una

dirección y a la otra. Esto se implementará más adelante cuando se estén realizando las

animaciones. Por ahora, solo es necesario saber que durante el comportamiento se alternarán

los sprites.

Page 121: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

121

Cuando finalice el estado, se comprobará la posición X del jugador respecto al enemigo

avanzado, de tal manera, que, si esta posición es mayor, se realizará un disparo hacia la

derecha y, en caso contrario, hacia el lado izquierdo. Sin embargo, para poder efectuar el

disparo, primero hay que indicar la bala que se va a utilizar, ya que tendremos que tener

varias entidades de bala para poder conseguir que todas ellas se puedan ver a la vez por

pantalla y funcionen de manera independiente.

Por lo tanto, justo cuando se acabe el comportamiento, necesitaremos llamar a una función

que nos prepare la entidad de la bala que se va a disparar.

Figura 90. Función para preparar la bala del enemigo para ser disparada.

Fuente: Elaboración propia.

Como se puede ver en la figura, básicamente lo que estamos haciendo es acceder a las dos

zonas de datos que necesita la bala que va a ser disparada. Para ello, previamente deberemos

haber creado las balas que nosotros sepamos que vaya a haber. En mi caso, he creado un

máximo de tres entidades de bala, ya que nunca se va a dar el caso en el que se vayan a

dibujar más de tres por pantalla.

Page 122: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

122

Tras apuntar a los datos de la bala, hacemos una simple función de reset de la misma que lo

que realiza es ponerla justo en la posición exacta donde se encuentre el enemigo, más o

menos a como se realizó para el disparo del jugador. Además, será dentro de esta función

donde comprobaremos la posición del jugador para saber dónde colocar la bala justo antes

de ser disparada.

Figura 91. Reseteo de la posición de la bala en función de la posición y dirección del enemigo.

Fuente: Elaboración propia.

Ahora llega la parte de controlar el movimiento de la bala. El enemigo ha terminado su

estado de ataque, ha efectuado su disparo y ha vuelto a su comportamiento anterior. Ahora

hay que mover la bala disparada y comprobar si colisiona en algún momento con alguna

entidad o con algún elemento del mapa.

Este control de movimiento no es necesario que lo explique ya que el proceso es

prácticamente a como se realiza con las balas del jugador. Indicamos el tiempo que queremos

que se mueva y cuando este acabe o colisione se llama a la función de borrado de sprites.

Page 123: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

123

Sin embargo, hay que tener en cuenta que cuando el jugador muere, la bala se va a borrar

también, pero, cuando el enemigo muera no queremos que esto ocurra. Por lo tanto, tenemos

que seguir llamando a la función encargada de controlar el movimiento de la bala, aunque

el enemigo avanzado haya muerto.

Además de esto, tenemos que hacer que la bala haga daño al jugador cuando esta colisione

con él. Para ello, creamos una función que solo se llamará si hay enemigos avanzados en el

nivel actual, ya que son los únicos capaces de efectuar un disparo por ahora.

Figura 92. Función para comprobar la colisión de la bala de los enemigos con el jugador.

Fuente: Elaboración propia.

Como curiosidad, cuando he implementado está función y la he probado, he observado que

las balas de los enemigos no se borraban, sino que se quedaban inmóviles justo donde

colisionaban o justo cuando había acabado el tiempo de disparo. Sin embargo, me he dado

cuenta que la bala del jugador si se borraba, además lo hacía justamente cuando tenía que

borrarse cualquiera de las otras balas que había en el nivel.

Page 124: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

124

Esto por su puesto no era ninguna casualidad, por lo que tras pasar un buen rato depurando

para buscar el error me di cuenta que este residía en la llamada de las etiquetas de salto. Es

decir, dentro de la función de controlar el movimiento de la bala, justo en la condición de

salto para comprobar si hay que borrar la bala o no por si ha colisionado, llamaba a una

etiqueta que no lo había puesto como local y que, casualmente, recibía el mismo nombre que

otra etiqueta colocada en la función de borrado de la bala del jugador.

Como utilizaba la instrucción “jp” para hacer la condición de salto y no “jr” podía saltar

hasta esa dirección, ya que en la SMS todo el código está escrito de manera continua y no

importa que nos encontremos en diferentes ficheros, podemos acceder a todo lo que se

encuentre en ficheros distintos, todo lo contrario, a como ocurre en Amstrad CPC que es el

primer computador donde empecé a trabajar con Z80.

8.4.2. Puzles

Tras implementar el segundo tipo de enemigos, voy a realizar la implementación de unos

puzles muy sencillos para los niveles del juego. La idea es dar un poco más de juego a los

niveles y que no se resuman simplemente a matar a los enemigos y avanzar. Les he dado el

nombre de puzles, pero realmente no lo son, solo es una forma diferente de avanzar por el

nivel.

Mi intención era la de implementar estos “puzles” mediante unas palancas y una puerta, de

tal manera que el jugador tuviese que colocar la combinación correcta de las palancas para

abrir la puerta. Sin embargo, tras estar buscando sprites gratuitos de palancas, no he

encontrado ninguno que me convenciese realmente y los que si lo hacían eran demasiado

grandes y al reducir su tamaño se perdía calidad. Teniendo en cuenta de que no me sobra el

tiempo, he tenido que buscar una alternativa.

He decidido sustituir las palancas por llaves, algo más simple pero que al fin y al cabo cumple

con mi objetivo de dar algo más de complejidad a los niveles. La idea a implementar es

bastante sencilla, si en el nivel se encuentra alguna llave, la puerta no se abrirá a no ser que

se haya recogido la llave, en caso contrario, si el jugador se encuentra dónde está la puerta y

pulsa la tecla “alt” se cambiará el nivel. Vamos a ver como implementar todo esto.

Page 125: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

125

Lo primero que tenemos que hacer es crear el objeto de puertas, es decir, crear una instancia

nueva de game_objects para las puertas. La razón de esto es porque de las puertas vamos a

necesitar bastantes datos que se encuentran en el struct de game_objects, aunque haya unos

pocos que no sean necesarios. Realmente, siempre que necesitemos que una entidad se dibuje

por pantalla, vamos a necesitar que esta sea una instancia de game_objects. Por lo tanto, para

las llaves también tendremos que hacer una instancia de esta estructura.

Una vez creado el objeto puerta, al igual que con todos los game_objects, haremos una

función para actualizar todo lo relacionado con la puerta, que corresponderá a dibujarla y

comprobar si el jugador puede cambiar de nivel.

Necesitaremos pues otra función encargada de comprobar si hay que cambiar de nivel o no.

Para ello, utilizamos la función de checkColision para comprobar la colisión entre el jugador

y la puerta. Si se produce colisión, entonces habrá que comprobar si hay llaves en el nivel.

Si no hay llaves entonces salimos de la función, ya no hay que realizar nada más. En caso

contrario, debemos comprobar si el jugador lleva la llave encima. Para ello, podemos crear

una variable extra para el jugador que se utilice para almacenar el valor que indica si se ha

código la llave o no (cero o uno).

En caso de que la lleve encima, entonces se comprueba si se pulsa la tecla correspondiente

y si esto ocurre, realizamos todo lo necesario para el cambio de nivel.

Page 126: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

126

Figura 93. Función para efectuar el cambio de nivel con las puertas.

Fuente: Elaboración propia.

8.4.3. Contador de tiempo

La siguiente mecánica que me gustaría realizar es una que podemos observar en

prácticamente todos los juegos clásicos de plataformas de la Sega Master System, el

contador de tiempo por nivel. Básicamente, esto es simplemente un reloj que indica el tiempo

que queda para terminar el nivel correspondiente, si su tiempo se acaba entonces el juego

termina y se tiene que empezar de nuevo. Vamos a ver cómo realizar la implementación de

este reloj en el videojuego.

A simple vista, puede parecer que realizar esta mecánica puede ser complicado, de hecho,

yo lo pensaba justo cuando empecé a investigar para poder realizarla. Sin embargo,

descubriremos que no es tan complicado como parece y que realmente su implementación

es bastante parecida a como se deberán hacer las animaciones del juego.

Page 127: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

127

Lo primero que hay que saber es que los números que se pueden ver por la pantalla de la

consola, los que representan el valor del tiempo, son un sprite y como cualquier sprite

necesitamos cargar previamente los tiles del mismo para posteriormente dibujar los valores

por pantalla.

Figura 94. Tilesheet de ejemplo que contiene los números a dibujar.

Fuente: Blackwolfdave.

En la 94 tendríamos un ejemplo sobre cómo sería el tilesheet del cual partiríamos para

obtener los tiles. El creador del tilesheet es Blackwolfdave y lo he obtenido de la página

OpenGameArt [16].

Como vemos, la imagen solo contiene los números a utilizar para representar el reloj de

tiempo. La idea sería conseguir tener una imagen con solo los números que necesitemos, que

en nuestro caso serían los valores del cero al nueve. Por lo tanto, mediante cualquier

programa de edición de imagen, realizamos los ajustes necesarios para conseguir tener los

números juntos en una misma fila y, una vez hecho esto, los cargamos al programa de

BMP2TILE para obtener los tiles correspondientes.

Con nuestros tiles cargados, tendremos que crear una entidad de game_objects para el reloj

de tiempo. Dentro de los datos de esta entidad, en la variable que representa el ancho del

sprite a dibujar, debemos indicar la cantidad de números que queremos mostrar por pantalla,

en nuestro caso, queremos dibujar un valor de tres dígitos, por lo que aquí ponemos el valor

tres.

Page 128: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

128

¡Cuidado! Esto solo funcionará si los valores de nuestros números tienen un ancho de ocho

píxeles, es decir, un tile. Si por el contrario fuesen más grandes, el valor de ancho del sprite

a dibujar será distinto, por lo que el valor variará en función del ancho de cada uno de los

números de la imagen de la cual hemos partido. Por ejemplo, si en nuestra imagen los valores

ocupan dieciséis píxeles, entonces en el ancho del sprite que tendremos que poner será seis,

ya que cada valor numérico ocupará un total de dos tiles.

El valor del índice de tile del sprite es el que va a representar cada uno de los números

de la imagen que nosotros cargamos previamente, por lo que si queremos cambiar uno de

los valores del número para, por ejemplo, hacer que el reloj de tiempo vaya decrementándose

de uno en uno como si fuese un reloj de verdad, tendremos que cambiar solamente los

charcodes del sprite ya que este no se va a mover su posición en ningún momento por lo que

no es necesario estar actualizando sus posiciones X e Y en el buffer.

La cuestión es que, si la imagen la hemos construido bien y tenemos los valores ordenados

en una fila de menor a mayor, eso querrá decir que el primer índice de tile representará el

número cero, el segundo el número uno y así hasta el último valor, siempre teniendo en

cuenta que el primer índice de tile dependerá de la posición donde hayamos cargado los tiles

en memoria.

Como vamos a tener que estar cambiando estos valores continuamente de manera

automática, lo lógico sería que tuviésemos almacenado en una variable el número que

queremos representar en todo momento y así saber que índice de tile hay que cargar en cada

posición de los charcodes del buffer. La variable en cuestión será una variable que ocupará

tantos bytes como números a dibujar. En nuestro caso serán tres bytes ya que el número a

representar contendrá tres dígitos en total.

Figura 95. Definición de una variable de más de dos bytes con WLADX.

Fuente: Elaboración propia.

Page 129: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

129

Con esta variable creada, creamos una función que sirva para inicializar dicha variable al

valor que nosotros queramos, en nuestro caso será 300. Tras inicializar, llamaremos a una

función que servirá para dibujar el sprite con el tamaño que habremos indicado, pero aun sin

haber indicado los tiles que queremos utilizar, es decir, los números que queremos que se

visualicen.

Figura 96. Función para inicializar el reloj de tiempo al valor 300.

Fuente: Elaboración propia.

Ahora tenemos que realizar una función que con a partir de ese valor que estamos

almacenado en la variable de tres bytes, conseguir representar un contador de tiempo que

vaya desde el valor 300 hasta el 000.

Para implementar el decremento del reloj esto lo podemos hacer de manera sencilla mediante

nuestro contador de tiempo del juego, que recordemos que cuenta un segundo cada vez que

se dibujan 60 frames. Por lo que cada vez que pase un segundo decrementamos en uno la

variable “contador_tiempo” para así que el reloj disminuya como si fuese uno real.

A continuación, debemos implementar la función que se encargue de cambiar los valores de

índices de tile a sus valores correspondientes en función del valor que haya en nuestra

variable de 3 bytes. Para ello, tendremos que hacer un bucle, mediante el cual, cada vez que

se entre en el mismo, se comprueba el valor de la variable que contiene los 3 valores del

contador de tiempo, que según en que iteracción del bucle nos encontremos, corresponderá

a las centenas, decenas o unidades del número.

Page 130: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

130

Si este valor es 0, ya tendremos en HL nuestra dirección a los números de los tiles que

representan cada número a dibujar, como es el 0 que hay que dibujar y el primer tile ya

representa este valor, no hay que hacer nada más para obtener el valor a dibujar. Si fuese

distinto a 0, se realiza otro bucle que accede hasta la dirección de memoria correspondiente

para obtener el tile correcto que representa dicho número. Por ejemplo, si fuese el número

9, se avanza 9 posiciones de memoria desde la primera dirección a la que apuntaba la etiqueta

con la información de todos los charcodes.

Tras esto, independientemente del valor a representar, ya tenemos en HL el número del tile

a dibujar, por lo que solo hay que cargarlo en la posición correcta del buffer para

posteriormente copiarlo al SAT. Para ello, como hemos creado el tiempo como un

game_object, tendremos almacenado la información de la posición donde hay que copiar el

valor en el buffer en nuestra variable “cc”.

Al realizar la copia, se avanzan dos posiciones en la memoria para apuntar a dirección del

buffer del siguiente índice de tile a copiar y se almacenan en los datos del game_object del

tiempo, de tal manera que la siguiente vez que se entre al bucle ya tendremos la dirección de

a donde hay que copiar el tile del sprite (véase figura 97).

Page 131: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

131

Figura 97. Carga del tile correspondiente del sprite de los números en la posición correcta del buffer.

Fuente: Elaboración propia.

Con todo esto, llamamos todo el rato a la función que comprueba si ha pasado un segundo o

no. Si ha transcurrido ese segundo, entonces decrementamos el reloj. Al final de esta función

siempre se llamará a la función encargada de copiar los tiles correspondientes a la posición

del buffer correcta.

En la siguiente figura, podemos ver como se dibujaría el reloj de tiempo en la esquina

derecha de la pantalla y, en la esquina izquierda, el contador de vidas del jugador, que se

implementaría de manera parecida al tiempo, simplemente cambiando la cantidad de

números a dibujar y decrementado este valor cuando el jugador reciba daño.

Page 132: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

132

Figura 98. Dibujado del reloj de tiempo y el contador de vidas en Invasion.

Fuente: Elaboración propia.

8.4.4. Menú

Anteriormente, en el apartado de control de caída del jugador, se hizo mención a una variable

llamada “mundo_state”. Esta variable se describió brevemente por encima como una

variable utilizada para controlar en todo momento el estado del juego, es decir, si se estaba

jugando, el jugador había muerto o si estamos en el menú. Vamos a ver exactamente cómo

funciona esta variable y cómo podemos hacer para controlar de manera muy simple en qué

estado del juego se encuentra el jugador en todo momento.

Page 133: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

133

Considero que es necesario saber durante toda la ejecución del juego en qué estado del juego

se encuentra el jugador, de esta manera, podemos desplazar la ejecución del código a la zona

que sea necesaria donde se realizará todo lo relacionado con el estado correspondiente. Por

ejemplo, para el estado de Menú, que es el que nos concierne ahora mismo, como es un

estado al cual solo se accederá al arrancar la consola, tendremos que gestionar la

inicialización del VDP, cargar el tilemap del fondo que hayamos creado para el menú y

habilitar la visualización de pantalla. Además, si en un futuro quisiéramos realizar más cosas

dentro de este estado, como que se realice algún efecto de animación, o que hayan varias

opciones en el menú, todo esto tendremos que implementarlo en esta zona.

Por todo esto, es favorable que tengamos nuestro código claramente diferenciado para saber

que parte corresponde al menú, a la muerte o a la ejecución del juego. Para ello, creamos

una variable que controle todo esto, de tal manera que, en función del valor que almacene

en el momento de entrar a la función controlar el estado del juego, el código saltará a una

zona o otra. Esta variable la llamaremos “mundo_state”.

Como queremos que haya tres estados del juego (muerte, juego y menú) almacenará hasta 3

valores distintos. Asignaremos el valor 1 al estado de juego y colocaremos la función de

comprobar el valor de esta variable justo al principio de nuestro bucle principal del

videojuego, de tal manera que sea siempre lo primero que se compruebe. Si almacena el

valor 1, entonces saldremos de la función y se continuará con la siguiente función del bucle

principal, ya que esto significará que estamos en el estado del juego y por lo tanto la

ejecución del código debe seguir como siempre.

Si en caso contrario, no se almacena el valor 1 y almacena cualquiera de los otros dos valores

(0 o 2) querrá decir que estaremos o en el menú o en la pantalla de muerte respectivamente.

En ambos casos, saltaremos a la zona de código encargada de gestionar cada estado y, dentro

de ella, primero hará lo que tenga que hacer para preparar dicho estado.

En el caso del menú corresponderá a inicializar el VDP, cargar el tilemap del fondo del menú

y habilitar la pantalla. Tras esto, se pausará el juego mediante la función de pausa que

implementamos anteriormente, que recordemos que lo que realizaba esta pausa era que la

ejecución del juego se quedaba en un bucle infinito recogiendo la entrada por teclado del

jugador hasta que este pulsase la tecla “alt”.

Page 134: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

134

En el caso de la muerte, deberá primero cargar el fondo de pantalla de muerte, pausar el

juego y, finalmente, después de que el jugador pulse la tecla “alt”, llamar a la función de

reinicio del juego que recordemos que también la hemos implementado anteriormente.

En ambos estados, al finalizar, se cambia la variable “mundo_state” al valor 1 para que se

empiece la ejecución de la pantalla de juego.

8.4.5. Animaciones

Como tampoco me queda mucho tiempo para poder entregar el proyecto, debo ir terminando

ya la parte de mecánicas y pasar a la sección de contenido. Por lo tanto, la última mecánica

que voy a implementar es la relacionada con las animaciones de los personajes del juego, ya

sea el protagonista o los enemigos. La idea es averiguar la forma de implementar estas

animaciones y no meter todas las animaciones que vaya a tener el videojuego, ya que eso se

hará en la sección de contenido.

El primer problema que nos vamos a encontrar será a la hora de cargar los tiles de cada una

de las animaciones, ya que lo lógico sería que, si ya tenemos cargado en memoria los tiles

del sprite de un enemigo mirando a la derecha quieto, por ejemplo, no deberíamos que tener

que cargar los tiles de lo mismo, pero mirando para el lado contrario ya que estaríamos

ocupando espacio innecesario que se podría utilizar para cargar cualquier otro sprite distinto.

La mecánica de poder girar de un lado a otro un sprite debería poder realizarse durante la

ejecución del juego, sin embargo, hasta este momento no he conseguido averiguar la forma

de hacerlo.

La única información que he encontrado relacionada con este aspecto, es la posibilidad de

voltear los tiles del tilemap que sean simétricos para así ahorrar espacio y tener mayor

variedad en los gráficos de los mapas. Esto se controla con los bits 0 y 1 del segundo byte

de cada una de las posiciones del tilemap. De hecho, he comprobado que esto se realiza de

manera automática, de tal manera que si al crear nuestro mapa, dibujamos un tile idéntico a

otro ya dibujado, pero girado hacia cualquier dirección, la SMS detectará de manera

automática que es el mismo tile y modificará los bits correspondientes.

A pesar de todo, este no funciona de manera igual para los tiles de los sprites y desconozco

por ahora como implementarlo. Por lo que no queda otra opción que cargar todos los tiles

necesarios sean simétricos a otros o no.

Page 135: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

135

Las animaciones funcionarán de manera parecida a cómo funciona el reloj de tiempo que

implementamos anteriormente. Eso quiere decir que para poder cambiar de un sprite a otro

lo único que habría que hacer es modificar los índices de tile que se están cargando al SAT

a los correspondientes con el sprite que queramos que se modifique.

La idea es que todos los sprites que correspondan a una misma entidad del juego tengan el

mismo tamaño, si no, además de cambiar los charcodes tendremos que volver a cargar las

posiciones verticales y horizontales del mismo y así como, modificar su ancho o alto. En mi

caso en concreto, el sprite tiene el mismo tamaño para todas las animaciones, por lo que solo

es necesario modificar los índices de tile.

Lo que tenemos que saber es cuando queremos que se cambien estos valores, por ejemplo,

si queremos hacer que el sprite del jugador se cambie para mirar hacia la derecha o a la

izquierda en función de a la dirección que se esté moviendo, lo lógico sería que realicemos

el cambio justo cuando entremos a la función de actualizar todo lo relacionado con el

jugador.

Figura 99. Modificación del sprite a mostrar por pantalla mediante los índices de tile.

Fuente: Elaboración propia.

Al realizar este cambio justo al entrar en la función del jugador, lo que conseguimos es que

dicho cambio se mantenga en caso de que no hayamos cargado posteriormente cualquier

otro sprite relacionado con el jugador. Lo que quiero decir es que, si por ejemplo el jugador

salta, tendremos que cargar el sprite de salto y como este sprite se cargará después del sprite

de estar quieto, entonces el que se verá por pantalla será este último que hemos cargado. Si

no se carga ningún otro sprite, sabemos que siempre se estará cargando el de mirar a la

izquierda o a la derecha.

Page 136: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

136

El verdadero problema llega con las animaciones que deben estar actualizándose

continuamente durante un tiempo determinado, como, por ejemplo, la animación de correr.

La cosa es que no consigo hacer que estas animaciones se cambien a una velocidad correcta

para que la animación parezca real.

Primero he intentado hacer el cambio de sprites mediante una variable que modificaba su

valor cada vez que se cargaba un sprite u otro, pero esto hace que las animaciones cambien

cada ciclo y va demasiado rápido. La segunda prueba ha sido utilizando el contador de

frames, intentando asi que el sprite de animación cambiase justo cuando se han dibujado 30

frames, pero ocurría que las animaciones se dibujasen demasiado lentas y si incrementado

el valor de frames para cambiar se hacían demasiado rápidas.

La cuestión es que tengo que averiguar la forma para poder hacer cuentas con decimales, es

decir, hasta ahora siempre utilizo números enteros para todo: los incrementos para controlar

la velocidad de movimiento son con números enteros al igual que el contador de tiempo y

muchos otros más, pero el caso es que no sé cómo hacer para que en Z80 se utilicen números

decimales en vez de los enteros y, desgraciadamente, no tengo tiempo suficiente para

averiguar la forma para implementarlo. Debo intentar entregar algo jugable, aunque sea muy

simple para esta primera entrega del proyecto por lo que debo descartar animaciones como

las de correr, tanto como para el jugador como para los enemigos.

8.5. Contenido

Dado que dispongo de un tiempo no mucho mayor de una semana para poder terminar el

juego, tengo que ir empezando ya a introducir contenido al mismo. A partir de este punto no

voy a implementar más mecánicas, todo lo que voy a hacer es buscar contenido como sprites

para las entidades de mi juego (personaje, enemigos, llaves etc) y así como, tiles para crear

mapas. Además de esto, intentaré crear una pantalla de muerte y de menú para poder

dibujarlos en sus correspondientes estados.

Page 137: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

137

8.5.1. Búsqueda de materiales

Lo primero que voy a hacer es buscar sprites gratuitos por internet para poder usarlos en mi

proyecto. Para ello, he buscado en páginas web como OpenGameArt o Ithc.io. En ambas

podemos encontrar todo tipo de recursos para usar en nuestros proyectos, lo único que hay

que fijarse es si el autor del material a descargar nos permite usar su contenido para

proyectos no comerciales o personales y si es necesario mencionarle o no. Por mi parte,

todo material que utilice que no sea propio, mencionaré a su autor.

Como no soy un buen diseñador de sprites y no se hacer diseños de personajes guais para mi

personaje, además de que no dispongo el tiempo para hacerlo, me encuentro un problema

que me pasará con el resto de sprites que debo buscar y es que el tamaño de estos, por lo

general, suele ser mucho mayor que lo que yo necesito, ya que estos son creados para

programas modernos de hoy en día, como por ejemplo Unity, y tienen un tamaño mucho

mayor de los 32x32 píxeles como máximo que yo podría aceptar.

El motivo por el que no puedo aceptar dimensiones de sprites superiores a 32x32 pixeles es,

principalmente, por el SAT. Esta tabla, que es la encargada de mostrar los sprites por

pantalla, solo tiene espacio para 256 bytes, de los cuales, 40 son para las posiciones verticales

y 80 para los índices de tiles y posiciones horizontales, los 64 bytes restante son libres.

Por lo tanto, si cargo sprites demasiado grandes, al final no voy a tener capacidad para poder

dibujar muchos sprites por pantalla. Por ejemplo, digamos que cargo un sprite con las

dimensiones indicadas anteriormente, 32x32. Esto querría decir que dicho sprite estaría

formado de 4 filas por 4 columnas, lo que daría un total de 16 bytes para la zona de posiciones

verticales del SAT y un total de 32 bytes para la zona de posiciones horizontales y charcodes.

Esto querría decir que, si quisiese cargar más sprites del mismo tamaño, solo podría tener

espacio para un sprite más, lo cual sería un poco pobre.

Es por eso por lo que he intentado reducir de manera manual el tamaño de algunos de estos

sprites que buscaba, pero al reducirlos se perdía mucha calidad hasta tal punto de verse

demasiado píxelados. He buscado también programas que hagan una reducción de estos,

pero prácticamente ninguno conseguía hacer una reducción sin perder calidad. El único que

más o menos permite aumentar o reducir el tamaño de imágenes sin perder mucha calidad

es uno llamado “resizemypicture.com”, el cual lo he utilizado un par de veces para hacer

algunas modificaciones de algunos assets.

Page 138: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

138

Por si no fuera suficiente, con el personaje del jugador tengo un problema adicional y es que

deseo que este sprite contenga un arma en la mano para poder simular que esta la está

disparando, por lo que es más complicado de encontrar un sprite que cumple con lo que

deseo y además tenga el tamaño justo.

El caso es que, tras buscar durante un buen tiempo, he encontrado un sprite para el jugador

que me podría servir, el cual se muestra en la siguiente figura y su autor es Blue Yeti Studios.

También he buscado sprites para el resto de entidades del juego. En el apartado de Diseño

del juego de este mismo documento se pueden encontrar los diseños definitivos para cada

una de estas entidades.

Figura 100. Tilesheet con sprites para el protagonista.

Fuente: Blue Yeti Studios [4].

Otro de los problemas que me encuentro es el de los colores. Hace tiempo, para hacer

pruebas de las mecánicas utilice un par de sprites y tiles descargados de la página

“thespriteresource” la cual contiene un montón de materiales para SMS y, por lo tanto, ya

están preparados para ser directamente cargados a la consola. Aun así, en el editor de

imágenes que yo utilizo, creé una paleta de colores basada en la paleta de la SMS donde

estaban todos los colores que se podían utilizar en la consola, la cual contenía un total de 64

colores.

La idea era utilizar dicha paleta para convertir el sprite o el mapa, ya preparado para ser

cargado en BMP2TILE, a una imagen indexada que contenga solo los colores posibles de la

consola. Sin embargo, al hacer dicha conversión se utilizaban más colores de los permitidos,

ya que en la consola solo pueden mostrarse un total de 16 colores por pantalla.

Page 139: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

139

Por lo tanto, he tenido que crear una paleta nueva que solo contiene 16 colores en total, todos

ellos sacados de la paleta anterior de 64. He intentado que dichos colores sean los que más

se parecen al sprite original y así obtener más o menos un aspecto parecido.

8.5.2. Diseño de niveles

Para hacer el diseño de los mapas he utilizado el programa Tiled. La razón de ello es debido

a que Tiled permite crear mapas de un tamaño determinado, por lo que se pueden crear mapas

que tengan justo el tamaño que tiene la pantalla de la Master System. Además, se puede

ajustar el tamaño que tendrá cada uno de los tiles del mapa a 8x8 píxeles y, así como, cargar

todos los assets que necesito directamente al programa y dividirlos en rejillas de 8x8 pixeles

para poder obtener así los tiles a dibujar en el mapa.

Hay que tener cuidado a la hora de diseñar el mapa, ya que cuantos más tiles distintos haya

dibujados en el mismo, mayor será el espacio que este ocupe en la consola. Una vez

terminado su diseño, lo podemos exportar directamente a imagen. Esto será necesario, ya

que antes de poder cargarlos a la consola, debemos realizar el mismo proceso que con los

sprites, el de reducir sus colores, por lo que creamos otra paleta de colores para el mapa y se

convierte a imagen indexada.

8.5.3. Diseño de la pantalla de muerte y menú

Necesito hacer unos diseños de fondos, aunque sean muy simple, para el menú y la pantalla

de muerte, de tal manera que cuando dentro del juego se salte a uno de estos estados, se

dibuje dicho fondo sobre el mapa hasta que se vuelva al estado de juego. Para ello, he creado

unos diseños muy simples con GIMP.

Figura 101. Fondo para pantalla de muerte.

Fuente: Elaboración propia.

Page 140: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

140

Figura 102. Fondo para la pantalla del menú.

Fuente: Elaboración propia.

Sin embargo, al cargar estos fondos al BMP2TILE me doy cuenta de que el tamaño que

ocupan es demasiado excesivo, llegando a ocupar hasta 30 tiles cada uno, lo cual no es

normal. La razón es el ancho de cada uno de los textos dibujados, seguramente ocuparán

unas dimensiones demasiado grandes y para cada letra se utilizan hasta 10 tiles. Es por eso

por lo que es mejor para la próxima vez hacer los diseños en el Tiled donde se sabe

exactamente el tamaño que ocupa cada tile.

Al cargar los dos fondos, me surge un problema y es por el cual no voy a poder entregar el

proyecto a tiempo para la primera entrega. Este problema es el del espacio, me doy cuenta

que no tengo sitio para cargar los dos fondos. Esto es un cúmulo de cosas mal gestionadas

como cargar sprites idénticos, pero simplemente rotados al lado contrario o aspectos como

el espacio que ocupan estos últimos fondos creados por GIMP [21].

La cuestión es que, en muchos de los mapas, hay tiles que se repiten los mimos valores

muchas veces e incluso hay ocasiones en los que hay filas donde solo hay ceros y esto ocupa

un espacio innecesario. Por lo que debo averiguar la forma de comprimir el espacio que

ocupan dichos mapas mediante alguna aplicación creada por mí que lea dichos ficheros y

reduzca los bytes a cargar.

También debo averiguar la forma para solo cargar una única vez un sprite que es simétrico

y luego este girarlo durante la ejecución del videojuego, en vez de cargar el mismo sprite

varias veces. Con todo esto, yo creo que debería bastar para poder obtener el espacio

necesario para poder cargar un total de 10 mapas para mi juego.

Page 141: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

141

8.6. Reducción del espacio ocupado en ROM

Debido a los últimos problemas que me he encontrado, no me va ser posible entregar el

proyecto en junio ya que no dispongo del tiempo suficiente para resolver estos problemas y

entregar un producto acabado.

Por lo tanto, en el tiempo que me queda hasta la entrega de septiembre, debo intentar resolver

estos problemas, los cuales están principalmente relacionados con el espacio disponible

en la consola, concretamente con el espacio ROM.

Existe un fichero de extensión “.sym” el cual se genera con el WLA DX al escribir “-S” a la

hora de realizar el linkado del proyecto. Este fichero, además de indicar en que posición se

encuentra cada etiqueta del programa, cosa que es bastante útil para depurar en caso de

producirse algún error, también nos permite saber el espacio que ocupa cada etiqueta o

definición del mismo. Los valores escritos están en hexadecimal, pero representan el espacio

que ocupan en bytes.

Tabla 1. Etiquetas/funciones que mayor espacio ocupan en el proyecto.

Definición/Etiqueta Tamaño que ocupa (Bytes)

Tiles de los sprites 10.016 (entre 64 y 384 bytes por sprite)

Tilemaps de los mapas 6.144 (1.536 bytes x 4 mapas)

Tiles del menú/muerte 4.736 (2.368 bytes para cada uno)

Tiles del mapa 1 800

Tiles del mapa2 576

Función "cargaAssetsSprite" 345

Buffer del SAT 256

Struct de Game_Objects 187 (17 bytes por game_object)

Struct de levels 16 bytes (8 bytes por nivel)

Struct de variables enemigos 16 bytes (8 bytes por enemigo)

Page 142: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

142

Observando la tabla anterior, se puede apreciar que evidentemente lo que más espacio ocupa

actualmente en el proyecto son los tilemaps de los mapas, es decir, los valores que indican

que tiles se van a dibujar en que posición de pantalla y aquellas características extras que

necesiten como la dirección del tile, si estarán encima de los sprites etc.

Aun así, los mapas no es lo que más espacio ocupa, si no los tiles de los sprites. Esto se debe

básicamente a que cometo el error de cargar 2 veces el “mismo” sprite pero girado hacia al

lado contrario al cual había cargado el primero. Esto está realizado de manera equivocada,

ya que lo lógico sería carga los tiles del sprite hacia una dirección y luego girarlo hacia la

dirección contraria cuando sea necesario durante la ejecución del juego.

Si consiguiese realizar esto, podría llegar a reducir el espacio que ocupan los tiles de los

sprites hasta la mitad, lo que supondría un espacio total aproximado de 5.000 bytes. Sin

embargo, a día de hoy, desconozco la manera a implementar este aspecto por lo que debo

investigar sobre ello.

Por otro lado, están los mapas del menú y la muerte, que no son mas que los fondos que

cargo cuando el juego se encuentra en el estado de menú, justo al arrancar la consola, o en

el estado de muerte, el cual ocurre cuando el jugador pierde todas las vidas. Estos dos mapas,

ocupan un espacio excesivamente grande si se compara con el que ocupan los mapas 1 y 2

del juego. El motivo de esto es debido a que ambos fondos los realicé con un programa de

edición de imágenes en un intento de conseguir entregar el proyecto en la convocatoria de

junio y por lo tanto, el dibujado del fondo se realizó sin tener en cuenta el espacio que ocupa

cada carácter dibujado, dando lugar a una cantidad excesiva de tiles para poder representar

ambos mapas.

8.6.1. Precisión subpíxel

Antes de intentar solucionar el problema de espacio, voy a explicar un concepto que

investigué en su momento y el cual pretendía integrar en el videojuego. La razón por la que

voy a explicarlo es porque he dedicado tiempo a intentar aprender cómo funciona y a como

integrarlo en mi proyecto, sin embargo, finalmente decidí que me conllevaría demasiado

tiempo y tampoco es un aspecto estrictamente necesario para que el juego funcione, por lo

que decidí apartarlo y si más adelante dispongo del tiempo necesario para implementarlo, lo

haré. Aun así, voy a intentar explicar todo lo que he aprendido por si a alguien le resulta útil.

Page 143: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

143

El concepto que voy a explicar es el de la coma fija o el de cómo obtener precisión

subpíxel en consolas de 8 bits. Para explicar este concepto me he basado en el video de

coma fija de Francisco Gallego en su canal de YouTube, Profesor Retroman.

El problema que intento resolver en este apartado es el de utilizar valores decimales en vez

de enteros en mis operaciones. Como hemos podido ver a lo largo del proyecto, en casos

como el movimiento de las entidades, siempre se utilizan valores enteros, esto quiere decir

que lo más lento que puedo hacer que un enemigo se mueva es de pixel en pixel. Pero, ¿qué

pasa si yo quisiera que la velocidad de movimiento fuese 0,75 en vez de 2? ¿Cómo

conseguiría esto? La solución es la coma fija.

Si tuviésemos un número flotante como puede ser el 20’345 por ejemplo. Si a este valor lo

multiplicásemos por un factor escala como puede ser el valor 1000, obtendríamos el valor

20345. De esta manera, a la hora de operar, nosotros sabemos que ese valor realmente

representa un valor decimal y el cual se obtiene dividiendo por su factor escala, por lo que

hacemos dicha división a la hora de operar y obtendríamos el valor decimal.

El problema, sin embargo, es que en ordenadores o consolas de 8 bits utilizar un factor de

escala de valor 1000 puede resultar muy costoso para la máquina en cuestión, es por eso por

lo que es mejor usar un valor de 256. Como sabemos, 256 es la cantidad que puede

representar un byte y, además, al ser potencia de 2 permite efectuar operaciones de

multiplicación y división mediante un simple desplazamiento de bits en el byte.

Con un valor como el 20’34 por ejemplo, podemos utilizar 2 bytes para almacenar el número:

un byte para la parte entera y otro para la decimal. Hay que tener en cuenta que, en la parte

decimal, el valor será representado como 34/256, ya que 256 es el factor escala y el máximo

valor a poder representar en un byte.

De tal manera que, en operaciones sencillas de suma o resta, si queremos sumar dos valores

cualesquiera como pueden ser 50’32 y 75’180. Esta suma se representaría como (30 +

32/256) + (75 + 180/256), lo que daría lugar a 125 + 212/256. En este caso en cuestión, en

la parte decimal no se ha producido acarreo y no hay que hacer nada más. Si se diese el caso

en el que valor de la parte decimal supera los 256 que puede representar un byte, su sumaría

dicho acarreo a la parte entera.

Page 144: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

144

8.6.2. Codificador de mapas

Lo primero que vamos a intentar reducir es el espacio que ocupan los tiles de los mapas,

para ello, vamos a implementar un codificador muy simple para los mismos, de tal manera

que estos vean reducido el tamaño que ocupan.

Para ello, vamos a hacer un pequeño programa en C++, el cual nos permita leer un fichero,

que será el que contendrá los tiles de los mapas, y a partir de este, generar otro que ocupe

menos espacio. Posteriormente, dentro del código del juego, habrá que implementar la

manera de leer este nuevo fichero generado para poder copiar todos los tiles correctamente

a su zona de memoria correspondiente.

Una vez abierto el fichero que contiene los valores de los tiles del mapa mediante la

instrucción “ifstream”, ya lo tenemos preparado para poder leerlo. Esta lectura se puede

hacer o bien línea a línea o bien carácter a carácter. El problema que tiene ambos métodos

es que la lectura se realiza para todo el fichero, es decir, todo lo que haya escrito en el mismo.

Si observamos la estructura del fichero que contiene los datos de los mapas, se puede ver

que contiene comentarios para indicar que tile es cada uno, por lo tanto, estos comentarios

se van a coger también en la lectura (véase figura 103).

Figura 103. Ejemplo estructura fichero que contiene los tiles de los mapas.

Fuente: Elaboración propia.

Sabiendo esto, lo más fácil es realizar dicha lectura mediante strings, es decir, línea a línea,

ya que lo único que habrá que tener cuidado es de no leer las líneas que contengan

comentarios, lo cual se resuelve fácilmente mediante una simple implementación mediante

la cual evitemos que se lean las líneas impares que son las que contendrán estos comentarios.

Page 145: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

145

La idea es conseguir primero que se realice una codificación simple que vaya comprobando

valor a valor si se repiten y cuántas veces se produce dicha repetición. Para esto, hay que

guardarse los dos valores que se comparan en dos arrays distintos. Esto es debido ya que,

aunque la lectura del fichero sea línea a línea, para luego acceder al contenido del string, hay

que hacerlo carácter a carácter, por lo que hay que guardar el valor del tile en un array de

caracteres de 2 posiciones.

Una vez guardados ambos valores, se comparan para comprobar si son iguales. Si lo son,

entonces se incrementa en 1 un contador que usaremos para contar el número de veces que

se repiten los valores y avanzaremos hasta el siguiente valor del fichero.

Este nuevo valor, se almacenará en el segundo array de caracteres, el primero seguirá

conteniendo el mismo valor que antes para poder comparar su similitud. Todo este proceso

se repetirá hasta que se encuentre un valor distinto en el segundo array de caracteres o se

haya llegado al final de la línea.

En el primer caso, cuando se encuentra el valor distinto, se comprueba el contador de

repeticiones para comprobar si el valor del primer array se ha repetido más de una vez o no.

En caso negativo, esto significará que dicho valor no ha se ha repetido y por lo tanto debemos

escribirlo en el fichero de salida tal cual.

En caso contrario, si el valor se ha repetido más de una vez, se escribe en el fichero de salida

el número de veces que se ha repetido y el valor en cuestión que se repite. Dentro de este

apartado también se comprueba si el valor posterior al repetido corresponde al último valor

de la línea leída. Si esto se cumple, entonces se escribirá también dicho valor tal cual, en el

fichero, ya que ya no habrá más comparaciones en esa línea.

Para todos los casos en el que él se haya encontrado un valor distinto, se llamará a una

función llamada “escribirFichero” la cual será la que se encargue de escribir en el fichero de

salida el resultado. A esta función se le pasan los siguientes parámetros: el fichero a escribir,

un entero para indicar la operación a realizar (escribir un valor único o repetido), el número

de veces que se repite el valor y el valor repetido.

Tras escribir en el fichero, se reinicia el contador de “almacenados” para poder coger de

nuevo otros dos valores a comparar y el contador de repeticiones. Además, se vuelve una

posición hacia atrás para coger el valor distinto del segundo array como primer valor a

comparar con los siguientes valores.

Page 146: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

146

Figura 104. Sección encargada de gestionar lo que ocurre cuando se detecta un valor distinto.

Fuente: Elaboración propia.

Dentro de la función “escribirFichero”, lo primero que se realiza es una comprobación de un

booleano, el cual servirá para saber si lo que se va a escribir en el fichero es el principio de

una nueva línea o la continuación de una ya empezada. Esto es necesario para saber si hay

que escribir el “.db” al principio de la línea o no.

Independientemente de que sea principio de línea o no, se comprueba el entero denominado

“caso” que determina que se va escribir en el fichero: un valor único sin repeticiones o un

valor que se repetirá tantas veces como indique el entero “rept”.

Page 147: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

147

Figura 105. Función para escribir en el fichero de salida.

Fuente: Elaboración propia.

Con este pequeño código, ya tendríamos un simple compresor que se encargar de hacer una

codificación simple de valores individuales. El resultado obtenido se puede observar en la

siguiente figura.

Figura 106. Comparación fichero entrada/salida. Arriba: fichero sin comprimir. Abajo: fichero comprimido.

Fuente: Elaboración propia.

Page 148: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

148

Tras hacer este simple codificador, nos podemos dar cuenta de que realmente no nos va a

servir. Si nos fijamos en la figura 107, podemos ver que hay un valor que se utiliza para

saber cuántas veces se repite el siguiente valor posterior a este, pero ¿Qué pasa con los

valores que no se repiten? ¿Cómo implementamos un código dentro de la SMS que sea capaz

de leer este fichero y averiguar cuando se trata de una valor único o repetido?

La solución es bastante sencilla, al igual que con los valores repetidos, podemos poner

previamente antes del valor que no se repite, un número que indique que este solo se repite

una única vez. De esta manera, al leer en la SMS, nosotros ya sabemos que primero se

indicará el número de veces que se repite y después vendrá el valor repetido.

Sin embargo, al hacer esto nos encontramos otro problema, en aquellos casos donde hayan

índices de tile que no contengan ningún valor repetido, el codificador en vez de comprimir

lo que hará será aumentar el tamaño de esa línea, ya que estamos incluyendo un byte más

indicar que no se repite y si no hay ningún valor repetido en esa línea, esto dará lugar a un

índice de tile que puede llegar a ocupar hasta 64 bytes, lo que es el doble de lo que ocupaba

antes y esto no es algo que queramos que ocurra.

La solución a todo esto es algo un poco más complejo, ya que habría que realizar un

codificador mucho menos simple que el realizado anteriormente, que se encargue de hacer

comprobaciones de parejas, de tríos y de cuartetos de valores. De esta forma, se reduciría

bastante la probabilidad de que salga un índice de tile que no contenga valores repetidos y

si ocurriese, habríamos reducido bastante el espacio ocupado que, aunque se ocupe el doble

de bytes en una línea, seguiría saliendo rentable.

Pero para esto habría que dedicar bastantes horas para poder conseguir un codificador de tal

estilo y esto algo de lo cual no dispongo, por lo que no me queda otra que descartar esta

opción.

8.6.3. Aumento del espacio disponible en ROM

Debido a que el codificador no ha resultado ser útil para reducir el espacio que ocupan los

mapas del juego, hay que averiguar otra manera que si nos permita ahorrar espacio. La

primera opción que puede surgir para conseguir este propósito, volviendo a mirar la tabla

que resume las funciones/etiquetas que mayor espacio ocupan, sería la de reducir el número

de tiles sprites que se cargan a la consola, ya que estos son los que más ocupan en ROM.

Page 149: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

149

Si hacemos memoria, recordamos que el principal problema con los sprites residía en que se

cargaba dos veces en memoria ROM el mismo sprite pero girado hacia el lado contrario al

que apuntaba el primero de ellos. Esto no debería realizarse así, debería haber una forma

dentro de la consola que permita al programador solamente cargar uno de los dos sprites

simétricos que tiene y posteriormente, girarlo durante la ejecución de la consola mediante

código.

Sin embargo, tras estar investigando un buen rato, llegué a la conclusión de que esto no va a

ser posible. En múltiples foros sobre programación de consolas de antiguas como smspower

o nesdev, hay usuarios que formulan la misma cuestión que yo me planteo actualmente y las

respuestas a estos hilos inciden en que la SMS no permite girar los tiles de los sprites, sin

embargo, si permite realizar este giro en los tiles del fondo.

Por lo tanto, no es posible ahorrar espacio en los tiles de sprites pero si en los del fondo, por

lo tanto, vamos a aprovechar al máximo el aspecto de girar los tiles del fondo para conseguir

así que los mapas ocupen menos espacio en ROM.

Para ello, hay que realizar un mapa que no utilice muchos tiles diferentes para que así el

BMP2TILE nos cargue el menor número de índice de tile posible, ya que este elimina

aquellos tiles que sean repetidos, es decir, iguales. Para conseguir esto, debemos intentar

usar, siempre que se pueda, el mismo tile, pero girado, ya sea horizontalmente o

verticalmente.

La mejor manera para construir un mapa simétrico es utilizar un editor de mapas. El editor

que yo utilizo es el Tiled, el cual nos permite cargar imágenes que contengan los tiles que

queramos utilizar en nuestro mapa. Además, permite indicar el tamaño de los tiles de la

imagen, es decir, si cada tile va a ser de 8x8 píxeles o superior, lo cual es perfecto para

asegurarnos que cada tile tiene el tamaño adecuado para cargar en la consola.

Por otro lado, permite también girar los tiles en cualquier dirección mediante simplemente

pulsar una tecla, permitiéndonos así conseguir usar el mismo tile varias veces en nuestro

mapa si tener que usar otro tile distinto. Recordemos que cada tile se representa en la consola

como 32 bytes, por lo que cuantos menos tiles usemos menos espacio ocupará el mapa.

Page 150: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

150

Figura 107. Ejemplo de mapa con tiles simétricos creado con Tiled.

Fuente: Elaboración propia.

En la figura anterior se puede observar un ejemplo claro de un mapa que contiene tiles

simétricos utilizados para varios aspectos. Si nos fijamos podemos ver que la pared de la

izquierda del todo es igual que la de la derecha, pero girada hacia el lado contrario. Las

columnas, por otro lado, también hacen uso de estas dos paredes, que juntándolas dan el

aspecto de una especie de columna. Como vemos, simplemente jugando con este aspecto de

girar los tiles hemos podido crear paredes y columnas solo usando 2 tiles.

Mediante el uso de esta simple técnica de girar tiles, he conseguido que un mapa que antes

requería un total de 24 tiles para ser dibujado en la consola, ahora ocupe solamente 14. Esto

supone un ahorro de unos 320 bytes en total.

Ahora solo habría que aplicar esto técnica a todos los mapas que se construyan para lograr

así algo más de espacio sobrante en la ROM de la SMS.

8.7. Implementación del final del juego

Ahora que ya se ha conseguido obtener más espacio libre en la ROM, ya es posible

implementar los niveles finales del juego que permitan dar al videojuego una sensación de

final. Como la historia del mismo no es un punto que se haya enfatizado demasiado para este

proyecto, la sensación de que estamos siguiendo una historia no está del todo lograda y por

lo cual, conseguir representar un final que transmita al jugador una sensación de que está

siguiendo una historia de principio a fin es un proceso complicado y más teniendo en cuenta

que me encuentro en la etapa final del proyecto.

Page 151: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

151

Sin embargo, voy a intentar que los niveles finales del juego sean algo distintos a los

anteriores y que concuerden un poco con la pequeña historia que decidí construir allá cuando

empecé con el proyecto. En el apartado de diseño del juego se encuentra disponible una

pequeña explicación sobre el contexto en el cual trata el videojuego Invasion.

8.7.1. Creación de los niveles finales

Fijándose en los niveles anteriores, nos podemos dar cuenta de que todos están formados por

el mismo bioma: materiales de piedra o roca. Esto es debido a que se pretende dar la

sensación al jugador de que está en una cueva. El motivo de que el diseño esté relacionado

con cuevas es debido a que Mark, el personaje protagonista, cae a una cueva por un agujero

al sufrir una emboscada nada más empezar el juego y es en este lugar donde transcurrirá la

mayor parte de Invasión.

Mi intención ahora es la de hacer que los últimos 2 o 3 niveles sean con un bioma distinto.

Mi idea es que, en un punto determinado del juego, concretamente en el nivel 7, el personaje

encuentre una escapatoria de las cuevas y salga al exterior, a los bosques de Vietnam.

Figura 108. Diseño del nivel 7 que contiene escaleras para dar al exterior.

Fuente: Elaboración propia.

La mecánica de uso de la escalera es bastante simple, como la escalera es un elemento del

fondo y no es un sprite independiente, no es posible detectar colisión entre el personaje y la

escalera de la misma forma a como se estaba realizando hasta ahora. Entonces, para poder

saber que el jugador se encuentra en la zona correcta para usar la escalera, se utiliza

simplemente su posición.

Page 152: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

152

La posición de la escalera es fija y se conoce en todo momento, por lo que lo único que hay

que realizar es una comprobación de la posición del jugador. Se crea así un pequeño rango

de espacio tanto en X como en Y que cubra la zona donde se encuentra la escalera y un poco

más de ella para que no sea muy exacto. Para poder realizar esto necesitamos hacer

comprobaciones de mayor o menor que en Z80. Esto, como ya se ha comentado

anteriormente, se realiza mediante la comprobación del flag de acarreo. En función de si se

ha producido o no acarreo en la operación anterior se determina si el valor almacenado en el

registro A es mayor o menor que el que se encuentra en la instrucción del CP.

Si se juntan las comprobaciones necesarias para comprobar tanto la posición X como en Y,

se consigue así saber cuándo la posición del jugador está dentro de un rango de espacio

determinado.

Figura 109. Uso de la instrucción “jr c” y “jr nc” para hacer comprobaciones de mayor y menor de un valor.

Fuente: Elaboración propia.

Gracias a esta salida de la zona de cuevas, el uso de un bioma distinto en los próximos niveles

está algo más justificado y pilla con menor sorpresa al jugador.

No obstante, con un cambio de bioma no es suficiente para evitar transmitir una sensación

de monotonía, ya que si las mecánicas siguen siendo las mismas que las de todo el juego no

va a importar ese cambio de diseño. Por lo tanto, he decidido añadir una mecánica nueva

dentro del nivel final.

Page 153: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

153

La idea es que en el nivel final el jugador debe hacer destruir el portal del cual es donde están

saliendo todos los enemigos. Para lograr esto, el jugador deberá destruir una especie de

cristales los cuales estarán distribuidos a lo largo del nivel. Al destruir todos los cristales, el

portal se destruiría y tras esto, el juego habría finalizado.

Figura 110. Diseño del nivel final donde se encuentra el portal.

Fuente: Elaboración propia.

Para implementar esta mecánica de destrucción de cristales, en primer lugar, lo

recomendable es crear un fichero nuevo donde se encuentren todas las funciones encargadas

de gestionar todo lo relacionado con los cristales (dibujado, borrado, movimiento etc) al

igual que hacíamos con cualquier otra identidad del juego.

Para dibujar los cristales dentro del nivel seguimos el mismo proceso que hemos seguido

para dibujar cualquier sprite: se cargan sus tiles en la RAM, se especifica el tamaño del

sprite, se crea el espacio necesario en memoria para poder almacenar cada uno de los cristales

mediante instanceof y finalmente se utilizan las funciones de dibujado para copiar las

posiciones verticales, horizontales y charcodes en el SAT.

Una vez dibujados, queremos que estos sean borrados cuando la bala del jugador colisione

con ellos. Para ello, utilizaremos la misma estructura que se utilizaba para comprobar cuando

las balas colisionaban con los enemigos. La diferencia es que está función solo se llamará

cuando estemos en el nivel final y eso se determina fácilmente con una variable que lleve el

registro del nivel en el que se encuentra el jugador.

Page 154: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

154

Podríamos intentar hacer una función general que pueda ser usada siempre que se quiera

comprobar cuando la bala colisiona con alguna entidad y evitar hacer funciones idénticas,

pero como voy falto de tiempo y tengo el espacio necesario para realizar todo lo que quiero,

no voy a hacer tal cosa, aunque lo más eficiente sería hacerlo.

Como digo, dicha función es idéntica a otra que ya se ha realizado anteriormente cuando se

estaba implementando a los enemigos por lo que no voy a poner una captura del código para

los cristales ya que en ese apartado se puede encontrar la misma función.

Dicho esto, ya se comprueba cuando la bala colisiona con un cristal, por lo que, si hacemos

memoria a lo que hacía la función, recordaremos que almacenaba en la variable colisión de

las dos entidades colisionadas el ID de la entidad con la que ha colisionado, por lo que solo

es necesario comprobar cuando esta variable de los cristales sea distinta de 0 y cuando eso

ocurra borrarlos de la pantalla. Además, cada vez que se borre un cristal se registra el número

de los mismos que han sido destruidos en una variable y cuando este valor alcance el total

de cristales que hayan dibujados, que en el caso de mi proyecto corresponde al valor 3,

entonces se terminará el juego.

8.7.2. Creación de la pantalla de fin del juego

Con la destrucción de todos los cristales el juego debería pasar a una pantalla nueva donde

se le indique al jugador que se ha terminado. Para ello, se puede utilizar una de las funciones

ya implementadas anteriormente.

Si recordamos, ya disponíamos de una función que se encargaba de gestionar los estados del

juego, es decir, se encargaba de controlar si el jugador se encontraba en el menú, jugando o

muerto, de tal manera que en función del estado del mundo se ejecutaban unas funciones u

otras. La idea para mostrar la pantalla de fin del juego es exactamente la misma: añadir un

cuarto estado de mundo que corresponda a la de la pantalla de fin del juego, cuando se

destruyan todos los cristales es cuando se pasará a este estado.

Page 155: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

155

Figura 111. Cambio de estado de mundo a Fin del Juego.

Fuente: Elaboración propia.

Mediante el código de la figura anterior, automáticamente saltaremos a la función encargada

de cambiar el estado de mundo. Para la pantalla de fin del juego, al igual que los otros

estados, se quedará el juego en “pausa” hasta que se pulse la tecla ALT que reiniciará el

juego de nuevo. Es aquí donde habrá que cambiar el dibujado del fondo de pantalla por uno

que indique al jugador que ha terminado el juego. Esto se implementará de la misma manera

que se ha realizado para la pantalla de muerte o la de menú.

Figura 112. Pantalla de Fin del Juego.

Fuente: Elaboración propia.

Es una pantalla bastante simple pero que cumple la función de indicar al jugador que ha

finalizado el juego. Se podría hacer mucho más compleja pero la falta de tiempo y de espacio

en ROM impide actualmente que se pueda conseguir tal cosa.

Page 156: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

156

Con todo esto realizado, ya se tendría un videojuego en Sega Master System el cual se puede

jugar de principio a fin. Quizás no se pueda jugar de la mejor manera, pero es ahora en el

poco tiempo restante en donde debo intentar corregir todos los “bugs” posibles y pulir todas

las mecánicas para que el juego de una sensación de acabado y de producto.

8.8. Mejoras y arreglos

Con todas las mecánicas ya implementadas y realizado prácticamente todo lo que se

pretendía hacer dentro del tiempo disponible, es tiempo ahora de pulir todas aquellas

mecánicas implementadas y corregir todos los fallos posibles.

Esta etapa final del proyecto, por lo tanto, servirá para intentar mejorar todo aquello que no

haya quedado de la mejor manera o no de la sensación de acabado. Además, se intentará

arreglar todos los fallos que hayan surgido durante el proceso de creación del juego.

8.8.1. Efecto de parpadeo de sprites

Uno de los objetivos principales a lograr en un videojuego, es transmitir al jugador lo que se

conoce como “feedback”, es decir, conseguir que el jugador tenga la información suficiente

para que sepa todo lo que está ocurriendo y no se encuentre perdido durante su partida.

La primera forma de conseguir esto, es mediante el efecto de parpadeo de sprites.

Actualmente, cuando el jugador recibe daño de cualquier fuente, la única información que

este recibe reside en el contador de vidas y esto no es suficiente puesto que el jugador, en

algún momento, puede pensar que no ha recibido daño alguno cuando en realidad si que lo

ha recibido. Esto puede derivar en un enfado del jugador y en un pensamiento de que el

juego no está bien realizado.

Para dar una mayor información al jugador en estos casos, se puede conseguir mediante un

simple parpadeo del sprite del protagonista, es decir, un continuo borrado y dibujado durante

el tiempo que dure la invencibilidad del jugador tras recibir daño. Esto, además de indicar

de mejor manera al jugador de que ha recibido daño, también sirve para que este sepa cuando

acaba la etapa de invencibilidad.

Para implementar esta mecánica bastará simplemente con implementar una función que se

encargue de borrar del SAT (es decir poner un 0) todos los valores del sprite del personaje

controlable por el jugador.

Page 157: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

157

Figura 113. Borrado de un sprite del SAT.

Fuente: Elaboración propia.

Esto resultará en un borrado del sprite de la pantalla del juego. A continuación, en la

siguiente iteración del bucle principal del juego, se ejecutará la función de dibujado normal

del sprite, dando así lugar al efecto de parpadeo que se intentaba conseguir.

Sin embargo, este efecto, aunque sirve para lo que se pretendía, no es lo que se deseaba

conseguir exactamente. Anteriormente, recordamos que uno de los aspectos que se intentó

integrar al juego es la implementación de la coma fija, lo que hubiese permitido la utilización

de valores decimales para todos los cálculos que lo necesitasen.

No obstante, no se logró integrar este aspecto al proyecto por lo que muchos otros aspectos

del mismo se ven afectados por ello. Uno de esos aspectos es el que se trata en este apartado,

al no poder controlar con mucha precisión el tiempo que debe transcurrir entre que se dibuja

o se borra el sprite, solo es posible hacerlo con un transcurso de tiempo de 1 segundo, ya que

un valor superior no lograría el efecto que se desea al efectuarse un parpadeo demasiado

lento.

Page 158: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

158

Aun con tiempo de 1 segundo entre ambas funciones se observa un parpadeo lento para mi

gusto. Debido a esto, he decidido que se llame a una función distinta cada vez que se realiza

una iteración en el bucle y para controlar cuando se ejecuta cada una, lo realizo mediante

una variable que cambia de valor cuando se ejecuta la función de borrado o dibujado.

Una vez implementado el efecto de parpadeo, se podría intentar aplicar este mismo efecto a

los enemigos para transmitir mayor información al jugador. Sin embargo, tras pensarlo

durante un breve tiempo, llegué a la conclusión de que quedaría mejor representar cuando

los enemigos reciben daño mediante un pequeño desplazamiento de los mismos.

La idea es que cuando los enemigos reciban la bala del jugador, estos se desplazarán una

pequeña distancia hacia la dirección contraria de donde provenía la bala. Se consigue

representar así de una manera sencilla y simple, el efecto de que el enemigo retrocede por el

impacto de la bala.

Hay diferentes maneras a implementar esta mecánica, la manera que he decidido realizar es

mediante una simple función que se ejecuta justo cuando el enemigo recibe daño. Dicha

función accederá a una variable que contendrá la información relacionado con la posición

del jugador en el eje X justo cuando este realizó el disparo, de tal manera que se compara

este valor con la del enemigo que recibe el impacto, en función de si es mayor o menor, el

desplazamiento será a la izquierda o a la derecha.

Figura 114. Función para desplazar al enemigo cuando este recibe daño.

Fuente: Elaboración propia.

Page 159: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

159

Hay que tener cuidado con no utilizar la posición del jugador justo en el momento cuando el

enemigo recibe el impacto, ya que esto puede llegar a dar problemas como que el

desplazamiento no se realice hacia la dirección correcta, debido a que el jugador puede haber

cambiado de posición mientras la bala llegaba a impactar al enemigo.

8.8.2. Música

La música era uno de los aspectos más importantes que más deseaba introducir en el

proyecto, sin embargo, al no haber podido dedicarle el tiempo suficiente para investigar

sobre ella, finalmente no se ha podido integrar en el juego. No obstante, durante el proceso

de investigación he descubierto algunos detalles interesantes que pueden ser útiles para

poder lograr en un futuro implementar esta mecánica. Es por ello, por lo que a continuación

se procede a explicar todo lo que se ha aprendido.

La idea principal reside en simplemente introducir una música para que suene de fondo

durante todo el transcurso del juego. No se considera introducir efectos de sonido debido a

la falta de tiempo y a que hay otros aspectos a implementar aún.

Existe una librería de sonido llamada PSGLib, la cual pertenece al usuario Sverx [32].

Esta librería incluye una serie de funciones que permiten gestionar todo lo relacionado con

la música: Reproducir una canción, pausarla etc.

Figura 115. Una de las funciones incluidas en la librería PSGlib.

Fuente: Librería PSG [32].

Page 160: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

160

Para utilizar esta librería basta con simplemente incluir el fichero que la contiene en nuestro

proyecto con la etiqueta “include”.

Una vez integrada en el proyecto, para poder utilizar las funciones de la librería y que

funcione todo correctamente, es necesario previamente que la canción este convertida al

formato correcto para que pueda ser reproducido por el chip de sonido de la SMS. Para ello,

en primer lugar, se utilizará uno de los tres programas disponibles (hay más disponibles,

pero son los que recomienda el autor de la librería) Mod2Psg, DefleMask o VGM Music

Maker. Estos tres programas realizan la misma función, crear nuestra canción de 8 bits y

exportarla al formato de VGM.

Tras exportar la canción, es necesario que esta sea optimizada y convertida al formato PSG

utilizando la herramienta de vgm2psg que forma parte de la librería. Desconozco

exactamente cómo se realiza este proceso de creación de una canción propia, ya que no he

tenido posibilidad de probarlo al no haber podido reproducir siquiera la canción que se utiliza

en el tutorial de smspower.

La idea es que, una vez se tenga la canción en el formato correcto, la incluyamos en nuestro

proyecto de la misma manera que se incluye cualquier otro recurso que queramos usar como

los sprites o los mapas. Una vez incluida, primero habrá que inicializar la librería mediante

la llamada a la función de “PSGInit” la cual se llamará solo una única vez y se recomienda

que esta llamada sea antes de habilitar las interrupciones de la consola.

Tras esto, copiamos la dirección donde se encuentra nuestra canción almacenada en el

registro “HL” y se llama a la función de “PSGPlay” para reproducir la canción. Finalmente,

solo quedará llamar a la función “PSGFrame” de manera constante. Una forma de realizar

esto, es mediante la llamada de una interrupción constante, es decir, que se haga siempre y

siempre tras el mismo transcurso de tiempo. Un ejemplo puede ser la interrupción VBlank

que ya está implementada en el proyecto, por lo que solo hay que llamar a la función justo

después de que salte esta interrupción.

A pesar de todo esto, no he conseguido hacer que se reproduzca la canción de fondo y eso

que, aparentemente, he realizado todos los pasos que indica Sverx en su repositorio. Sin

embargo, algo estoy realizando mal o hay algún paso que no he llegado a entender del todo

bien, pero no dispongo del tiempo para poder investigar más a fondo este aspecto por lo que

no queda más remedio que descartar la música.

Page 161: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

161

9. Conclusiones

Tras casi un año de trabajo, el proyecto que empecé por octubre/noviembre ha sido

finalizado. Todos los objetivos propuestos han sido realizados y se ha conseguido crear un

videojuego que funcione de manera correcta en una consola de la Sega Master System.

En líneas generales me encuentro satisfecho con el resultado del proyecto y el trabajo

realizado a lo largo de este último año. Aunque el videojuego construido no sea el mejor

juego creado para esta consola, considero que el resultado obtenido, aunque siempre puede

ser mejor, es aceptable teniendo en cuenta que era la primera vez que trataba con esta consola

y no tenía ningún conocimiento previo sobre aspectos técnicos de la misma.

Se han conseguido realizar mecánicas que antes de empezar el proyecto no consideraba

capaces de realizar: reloj de tiempo, efecto de parpadeo de sprites, colisiones con el entorno

etc. Además, a pesar de que ya conocía el lenguaje ensamblador Z80 gracias a proyectos

previos realizados, he aprendido nuevos conceptos e instrucciones que anteriormente

desconocía y que me han ayudado a crear un mejor producto.

Por otro lado, he intentado explicar en este documento, de la mejor forma posible, todo lo

que me ha conllevado realizar este videojuego en la SMS: desde problemas encontrados y

sus soluciones hasta descripciones detallas con imágenes de las mecánicas implementadas

por mí. El motivo de esto era para que cualquier otra persona que en un futuro quiera o deba

realizar un videojuego para esta plataforma pueda encontrar en este documento una

pequeña ayuda para realizar tal tarea y que no cometa los mismos errores que he yo he

cometido (o que encuentre aquí la solución de los mismos). Considero que este objetivo se

ha logrado y espero que pueda servir de ayuda a muchas otras personas.

También he creado un pequeño anexo en este documento donde describo algunos de los

aspectos técnicos de la consola SMS que he ido aprendido a lo largo de mi pequeña

investigación. La idea principal de esto era para poder acceder a dicha fuente de información

en cualquier momento que lo necesitase durante el desarrollo del proyecto, ya sea por dudas

o simplemente para reafirmar mis conocimientos aprendidos. Sin embargo, este simple

anexo puede también ser usado por otras personas si así lo desean, ya que la mayor parte de

la información que se puede encontrar por Internet sobre la Sega Master System se encuentra

en inglés y aquí se puede encontrar, aunque de manera mucho más resumida, en español.

Page 162: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

162

A pesar de todo, la razón principal por la cual me encuentro satisfecho con el videojuego

realizado es debido a que todo lo aprendido y realizado me ayuda bastante a la hora de

futuros proyectos, ya que mi idea es volver a realizar otro juego para la SMS que por

seguro será mucho mejor que el realizado aquí. Principalmente porque no volveré a cometer

los mismos errores y, además, ahora dispongo de un conocimiento mucho mayor sobre cómo

funciona la consola por lo que podré implementar mecánicas que no he conseguido realizar

en Invasion (scroll lateral, música, efectos de sonido etc.).

Page 163: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

163

10. Bibliografía y referencias

[1] Akuma, C. Z80 Assembly programming for the Sega Master System. Sitio web:

http://www.chibiakumas.com/z80/MasterSystemGameGear.php (accedido el

05/09/19).

[2] Alonso, J. (2003). El microprocesador Z80. Sitio web: http://curso-

cm.speccy.org/fr_cap3.html (accedido el 05/09/19).

[3] Anders. (2015). Create a Racing Game. Sitio web:

http://www.smspower.org/Articles/CreateARacingGame (accedido el 05/09/19).

[4] Blue Yeti Studios. (2017-2018). Sitio web: https://opengameart.org/users/blue-

yeti-studios (accedido el 05/09/19).

[5] Boland, S. (2013). Sega Console Programming. Sitio web:

http://steveproxna.blogspot.com/2013/09/sega-console-programming.html

(accedido el 05/09/19).

[6] Corcoran, L. (2013). Itch.io Game Assets. Sitio web: https://itch.io/game-assets

(accedido el 05/09/19).

[7] Context. (2005). Resize my picture. Sitio web: http://www.resizemypicture.com/

(accedido el 05/09/19).

[8] Cornut, O., y otros. (2005). Meka Emulator. De Emutopia. Sitio web:

https://www.emutopia.com/index.php/emulators/item/298-sega-sg-1000-sc-

3000/239-meka (accedido el 05/09/19).

[9] Cornut, O., Maxim. (1997). SMS POWER. Sitio web: http://www.smspower.org/

(accedido el 05/09/19).

[10] Dazz, Petie. (2003-2019). The spriters Resource. Sitio web:

https://www.spriters-resource.com/ (accedido el 05/09/19).

[11] García, S., Américo, F. (2002). Registros de la CPU. Sitio web:

http://www.portalhuarpe.com/Medhime20/Sitios%20con%20Medhime/Computa

ci%C3%B3n/COMPUTACION/Menu/Modulo%205/5-6.htm (accedido el

05/09/19).

[12] Helín, V. (2019). Wla-dx 9.9 documentation. Recuperado de:

http://www.villehelin.com/wla-README.html (accedido el 05/09/19).

[13] Index Register. (2019). Wikipedia: La enciclopedia libre. Sitio web:

https://en.wikipedia.org/wiki/Index_register (accedido el 05/09/19).

Page 164: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

164

[14] Interrupt. (2019). Wikipedia: La enciclopedia libre. Sitio web:

https://en.wikipedia.org/wiki/Interrupt (accedido el 05/09/19).

[15] Juan, M. (2015). Magica. Sitio web: https://www.usebox.net/jjm/magica/

(accedido el 05/09/19).

[16] Kelsey, B. (2009). Opengameart. Sitio web: https://opengameart.org/

(accedido el 05/09/19).

[17] Lindeijer, T. (2008-2017). Tiled Map Editor. Sitio web:

https://www.mapeditor.org/ (accedido el 05/09/19).

[18] MacDonald, C. (2000-2002). Sega Master System VDP Documentation.

Sitio web : http://www.smspower.org/uploads/Development/msvdp-

20021112.txt?sid=4dce9e3d17f7f1819f69c80237b9839a (accedido el

05/09/19).

[19] Macdonald, C. (2000-2002). SMS/GG Hardware Notes. Sitio web:

http://www.smspower.org/uploads/Development/smstech-

20021112.txt?sid=4dce9e3d17f7f1819f69c80237b9839a (accedido el

05/09/19).

[20] Mappers. (1997). SMS POWER Development. Sitio web:

http://www.smspower.org/Development/Mappers?from=Development.Mapper.

(accedido el 05/09/19).

[21] Mattis, P., Kimball, S. (1995). GIMP. Sitio web: http://www.gimp.org.es/

(accedido el 05/09/19).

[22] Maxim. (2005). BMP2TILE. Sitio web:

http://www.smspower.org/maxim/Software/BMP2Tile/ (accedido el 05/09/19).

[23] Maxim. (2010). SMSPOWER how to program. Sitio web:

http://www.smspower.org/maxim/HowToProgram/Index (accedido el 05/09/19).

[24] Memory Mapping. (2018). Tutorials Point. Sitio web:

https://www.youtube.com/watch?v=jkT9Bgz8PAg (accedido el 05/09/19).

[25] Memory Mapping 8-bit. Digital-circuitry. Sitio web: http://www.digital-

circuitry.com/8-bit_Memory_Mapping.htm (accedido el 05/09/19).

[26] Overflow Flag. (2019). Wikipedia: La enciclopedia libre. Sitio web:

https://en.wikipedia.org/wiki/Overflow_flag (accedido el 05/09/19).

[27] Scherrer, T. (2019). Z80 CPU Architecture. Sitio web:

http://www.z80.info/z80arki.htm (accedido el 05/09/19).

Page 165: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

165

[28] De Sega Games Company Limited. (1985). Software Reference Manual for

the SEGA Mark III Console. Recuperado de:

https://segaretro.org/images/d/d6/SoftwareReferenceManualForSegaMarkIIIEU

.pdf (accedido el 05/09/19).

[29] Sega. (2019). Wikipedia: La enciclopedia libre. Sitio web:

https://es.wikipedia.org/wiki/Sega (accedido el 05/09/19).

[30] Sega Master System Technical specifications. Sega Retro. Sitio web:

https://segaretro.org/Sega_Master_System/Technical_specifications#

(accedido el 05/09/19).

[31] SMS Programming. Sega8Bit. Sitio web:

http://www.smstributes.co.uk/view_page.asp?articleid=207 (accedido el

05/09/19).

[32] Sverx. (2014). Librería de sonido PSG. Sitio web:

https://github.com/sverx/PSGlib (accedido el 05/09/19).

[33] Talbot, R. (1998). Sega Master System Technical information. Sitio web:

http://www.smspower.org/uploads/Development/richard.txt?sid=4dce9e3d17f7f

1819f69c80237b9839a (accedido el 05/09/19).

[34] Wesker. (2010). 25 años de la Sega Master System. Sitio web:

https://www.segasaturno.com/portal/25-anos-de-sega-mark-iii-master-system-

vf7-vt5114-vp39370.html (accedido el 05/09/19).

[35] Williams, M. Sitio web: https://opengameart.org/users/bizmasterstudios.

[36] Zilog Z80. (2019). Wikipedia: La enciclopedia libre. Sitio web:

https://en.wikipedia.org/wiki/Zilog_Z80 (accedido el 05/09/19).

Recursos utilizados

1. Clrhome. (2012). Z80 Table. De The ORG Project. Sitio web:

http://clrhome.org/table/.

2. Cornut, O., Maxim. (1997). SMS POWER Documents. Sitio web:

http://www.smspower.org/Development/Documents.

3. Luis, J. (2003). Glosario Terminología Informática. De Creative Commons BY

NC SA. Sitio web: http://www.tugurium.com/gti/index.php.

Page 166: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

166

4. SUMMON PRESS, S.L. Calculadora Hexadecimal. De SUMMON PRESS, S.L.

Sitio web: https://es.calcuworld.com/calculadoras-matematicas/calculadora-

hexadecimal/.

Page 167: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

167

11. Glosario

A continuación, se procede a describir los diferentes términos que se pueden encontrar a lo

largo de este documento y cuyo significado puede no haberse comprendido:

• Banco de memoria: Área de ROM o RAM que ha sido mapeada.

• Buffer: Espacio de memoria donde se almacenan datos de forma temporal, con

el objetivo de evitar que el programa o recurso que requiera de esos datos, se

quede sin datos durante una transferencia (de entrada, o de salida) de datos

irregular o por la velocidad del proceso.

• Charcode: Índices de tile de los sprites.

• CRAM: Colour RAM. Corresponde a la memoria interna del VDP y sirve para

definir la paleta de colores en la Master System. Ocupa 32 bytes y es de solo

escritura.

• Firmware: De manera general, el firmware es un programa informático

encargado de establecer toda la lógica de bajo nivel del dispositivo.

Concretamente, existe el programa BIOS que se encarga, entre otras cosas, de

gestionar el arranque de la consola y prepararla para ser utilizada.

• GDD: Game Design Document. Documento que describe detalladamente todos

los aspectos que contienen un videojuego (diseño, mecánicas, jugabilidad,

historia, género etc.). Este documento está en continua modificación durante el

desarrollo del videojuego y su versión final no coincidirá con la inicialmente

creada.

• IA: Inteligencia Artificial.

• Mapper: Hardware encargado de realizar el mapping [20].

• Mapping: El “mapeo”, es la transformación de datos de un formato a otro.

Cuando se utilice este término, se estará haciendo referencia al acto de llevar los

datos que se encuentran en el espacio de direcciones del chip a un rango del

espacio de direcciones del Z80. Esto permite aumentar el espacio de memoria

disponible en la consola [24][25].

• Ranura de memoria (slot): Área del espacio de direcciones del Z80 dentro del

cual varias zonas de ROM o RAM pueden ser mapeadas.

• ROM: Read Only Memory. Memoria de solo lectura que se utiliza

principalmente para almacenar datos.

Page 168: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

168

• SAT: Sprite Attribute Table. Zona de datos situada en la dirección de memoria

$3F00 de la SMS. Contiene toda la información referente a los sprites que se

están dibujando por pantalla.

• SMS: Sega Master System, consola de 8 bits sobre la cual se creará el videojuego

de este proyecto.

• VDP: Video Display Processor. Chip de gráficos de la consola SMS [18].

• VRAM: Memoria de video.

Además de todos estos términos, a lo largo del documento puede aparecer escrito el símbolo

‘$’ para denotar aquellos números que están escritos en formato hexadecimal.

Page 169: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

169

12. Anexo 1. Detalles técnicos de la Sega Master System

12.1. Información general

La Sega Master System es una consola de videojuegos de 8 bits que utiliza como soporte un

cartucho ROM y una tarjeta Sega Card. Originalmente, fue lanzada en Japón en 1985 con el

nombre de Sega Mark III, siendo la segunda consola de sobremesa de Sega, tras la SG-

1000 (acrónimo de Sega Game 1000) que fue la primera que fabricaron y que tuvo muy

poco éxito en los mercados europeos y asiáticos [28][29][33].

En los años posteriores, sería rediseñada y renombrada a Master System para poder ser

lanzada internacionalmente a zonas como Norte América (1986), Europa (1987) y Brasil

(1989).

La Master System, fue lanzada en competición con la Nintendo Entertainment System

(NES), lo que produjo que tuviese poco éxito en los mercados de Japón y Norte América,

aunque tuvo mucho mayor éxito en Europa y Brasil.

Antes de comenzar a detallar de manera más específica los aspectos más importantes de la

SMS, primero se van a explicar de manera resumida para obtener así una idea general del

hardware que forma parte de la consola:

Tabla 2. Aspectos técnicos generales de la SMS.

SEGA MASTER SYSTEM

CPU Zilog Z80A (NEC D780C), 8 bits

Velocidad de Reloj 3,58 MHz

ROM 8 kb

RAM 8 kb

VRAM 16 kb

Gráficos TMS9918

Resolución 256 x 192 píxeles

Paleta de colores 64 colores (32 simultáneos)

Sonido SN76489 PSG chip (4 canales)

Dimensiones 36.2 x 17 x 7 cm

Page 170: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

170

Como información adicional, cabe destacar que las versiones japonesas integraban como

chip de sonido un Yamaha YM2413. Además, la SMS contiene 2 ranuras para la

introducción de juegos: Una para los cartuchos Mega y otra para las tarjetas SEGA CARD

que es una herencia de la Mark II.

Por otro lado, para la visualización de la pantalla se utiliza el TMS9918, que es un

controlador de visualización de pantalla (VDC – Video Display Controller), que contiene 3

bits mediante los cuales se pueden elegir diferentes modos de visualización para la consola,

llamados M1, M2 y M3. Sin embargo, solo hay 4 combinaciones documentadas:

• Modo 0 - Gráficos I

• Modo 1 – Texto

• Modo 2 – Gráficos II

• Modo 3 - Multicolor

Sin embargo, el VDP de la Master System añadió otro bit de modo de selección que

habilitaba el modo 4, el cual es específico de la consola SMS. Por lo tanto, en la SMS hay

muchos más modos de visualización gracias a que en lugar de los 3 bits iniciales que poseía

el VDC, el VDP contiene 4 bits.

A continuación, se procede a describir con mayor profundidad diferentes aspectos

pertenecientes a la consola de videojuegos SMS. Los aspectos que se van a tratar serán los

siguientes:

• CPU Zilog Z80A

• Mapa de memoria

• Sistema Básico de entrada-salida (BIOS)

• Video Display Processor (VDP)

Page 171: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

171

12.2. CPU

La CPU principal de la SMS es una Zilog Z80A, la cual es una variante del microprocesador

de 8 bits Z80. Esta versión, fue la más utilizada de todas y funcionaba a una frecuencia de

reloj de 3,58 MHz [2][27][30][36].

El Z80 fue fundado principalmente por el físico Federico Faggin, que por aquel entonces

trabajaba en Intel como diseñador jefe del Intel 8080, entre otros, hasta que dejó su puesto a

finales de 1974 para fundar Zilog y trabajar en el diseño de Z80.

El Z80 fue diseñado con el objetivo de que su conjunto de instrucciones fuese binariamente

compatible con el Intel 8080, de manera que, la mayor parte del código del 8080 pudiese ser

ejecutado sin modificarse en la nueva CPU Z80.

El Z80 ofrecía diferentes mejoras respecto al 8080, entre ellas, se destacan las siguientes:

• Un mejor conjunto de instrucciones incluyendo el direccionamiento de un solo bit,

rotaciones en memoria y en más registros a parte del registro acumulador. Además,

incorporaba dentro del registro de flags un bit para indicar cuando en una operación

se ha producido overflow, es decir, indica que el complemento a 2 del resultado no

cabe en el número de bits utilizado para la operación.

• Nuevos registros índice IX e IY, así como las instrucciones necesarias para

manejarlos.

• Un mejor sistema de interrupción debido a que se incluían dos bancos separados

de registros que podían ser cambiados de manera rápida y acelerar así la respuesta a

las interrupciones.

• Un menor requerimiento de hardware tanto para la fuente de alimentación que solo

requería una alimentación única de 5 voltios, como para la generación de la señal de

reloj.

• Una función especial de reset que permitía limpiar solamente el registro Program

Counter (PC).

Debido a estas mejoras que ofrecía, el Z80 eliminó rápidamente al Intel 8080 del mercado,

convirtiéndose así en uno de los procesadores de 8 bits más populares de los 80.

Page 172: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

172

Registros de la CPU

Los registros que contiene la CPU son memorias de alta velocidad y con poca capacidad que

vienen integrados en el microprocesador. Estos registros, permiten controlar las

instrucciones que se estén ejecutando, guardar datos temporalmente, manejar el

direccionamiento de la memoria etc [11].

Los registros pueden almacenar por sí solos hasta 1 byte (8 bits) de información. Este espacio

puede ser incrementado si se juntan dos registros, dando así lugar a un registro de 16 bits de

longitud. Esto permite, por ejemplo, almacenar una dirección de memoria que ocupe 2 bytes.

A continuación, se van a ver los registros que tienen una función específica dentro del

microprocesador:

• Program Counter (PC): Permite almacenar la dirección de memoria (16 bits)

de la siguiente instrucción que se va a ejecutar en el programa. Conjuntamente,

el PC es incrementado automáticamente una vez que su contenido es enviado a

través del bus de direcciones.

• Stack Pointer (SP): Este registro es un puntero a la pila, es decir, almacena la

dirección de memoria de la parte superior actual de la pila, que podrá estar

localizada en cualquier parte de la memoria RAM.

• Registros Índice (IX, IY): Registros de 16 bits que suelen ser utilizados para

apuntar a una región de memoria cualquiera donde los datos van a estar

almacenados o ser recuperados. Permiten acceder a tablas y estructuras de

manera sencilla, ya que sirven como índices dentro de estas estructuras. Las

instrucciones que utilizan estos registros presentan el inconveniente de que son

más lentas que el resto de registros, debido a que ocupan 2 bytes [13].

• Acumulador (A): Permite almacenar el resultado de una operación

aritmética/lógica de 8 bits. El registro A es el único que se utiliza para la mayoría

de instrucciones importantes y es el registro del cual parten y llegan la mayoría

de instrucciones.

• Flag (F): Es un registro de 8 bits, donde cada uno de estos bits se modifica cada

vez que se ejecuta una instrucción aritmética. Por ejemplo, el segundo bit se

utiliza para comprobar si la última operación aritmética ha resultado en cero o

no. Si ha dado cero, el bit se establecerá a 1. En caso contrario, a 0 [26].

Page 173: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

173

Se procede ahora a ver los registros que tienen un propósito general:

• Registros BC y DE: También pueden ser utilizados individualmente como

registros de 8 bits. Se suelen utilizar mayormente como contadores o para

almacenar resultados.

• Registro HL: Suelen ser utilizados de manera conjunta en un amplio

conjunto de instrucciones para direccionamiento indirecto. Esto quiere decir

que el registro puede ser utilizado para almacenar un valor (que suele ser una

dirección de memoria) y posteriormente usar dicho valor como una dirección.

Conjunto de instrucciones

De manera genérica, las instrucciones del Z80 se agrupan en las siguientes categorías:

• Operaciones aritméticas/lógicas de 8 bits: ADD, SUB, XOR, OR, CP etc.

• Operaciones aritméticas de 16 bits: INC, DEC, ADD etc.

• Cargas de 8 bits: LD

• Cargas de 16 bits: LD, PUSH, POP

• Saltos: JP, JR, CALL, RET etc.

• Control de CPU: HALT, WAIT, RESET, INT etc.

12.3. Mapa de memoria

Como ya se ha indicado anteriormente, la unidad base de SMS contiene 8 kb de ROM y 8

kb de RAM. La ROM, encargada de almacenar el programa principal del juego, se conecta

a la unidad utilizando una de las dos ranuras que tiene disponible la consola.

El Z80A, es capaz de direccionar hasta un total de 65.536 posiciones de memoria distintas,

gracias al tamaño de su bus de direcciones que es de 16 bits. Este espacio, es el que abarca

desde la dirección de memoria $0000 hasta la $FFFF. Este espacio de direccionamiento es

mucho más simple que el de otras consolas de videojuegos como puede ser la Game Boy. A

continuación, se muestra el mapa de memoria de la Sega Master System [19][33]:

Page 174: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

174

Tabla 3. Mapa de memoria de la SMS.

Dirección Descripción

$FFFC-$FFFF Registros de Mapeo

$E000-$FFFC 8k, espejo de la RAM en $C000-$DFFF

$C000-$DFFF 8k de RAM

$8000 - $BFFF 16k Slot 2 de la ROM ó Slot 0 de la RAM

$4000 - $7FFF 16k, Slot 1 de la ROM

$0400 - $3FFF 15k, Slot 0 de la ROM

$0000 - $03FF Primeros 1k del Slot 0 de la ROM

Tomando como referencia la figura anterior, se pueden destacar los siguientes aspectos:

• Desde la dirección $0000 hasta la $BFFF es la región del cartucho, es decir, viene

definida por el cartucho que este actualmente activo. En la mayoría de los casos,

corresponderá a la ROM o al cartucho RAM.

• Debido a que la memoria del cartucho que contiene el juego puede exceder de los 64

kb y el Z80A no puede manejar más de esos 64 kb, es necesario un sistema de gestión

de memoria en la SMS como son los “mappers” [20].

Un mapper no es más que un chip situado dentro de los cartuchos y que permite el

uso de ROM’s más grandes. Para ello, hacen uso de lo que se conoce como bancos

de memoria. Cuando se necesita más memoria, se produce un intercambio de uno

de esos bancos que se están usando por otro que esté disponible, de esta manera, se

consigue así aumentar las capacidades de la consola y obtener más espacio.

En la tabla anterior, se muestra el mapeo de la ROM asumiendo el mapper que

proporciona Sega. Como se puede observar, esto permite definir 4 ranuras (slots)

en el mapa de memoria. Cada banco de 16 kb de ROM, puede ser mapeado a

cualquiera de las 3 primeras ranuras disponibles. Cuando se mapea en la ranura 0,

los primeros 1kb no son afectados para poder preservar los vectores de interrupción

y permitir así que las interrupciones puedan ser controladas de manera correcta, solo

se mapean los 15 kb restantes. Adicionalmente, en la última ranura (slot 2) puede

ocurrir que sea mapeada también, uno de los dos bancos de 16 kb de RAM, dejando

de ser así una zona de solo lectura.

Page 175: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

175

El hardware del mapper es controlado por los registros que se encuentran en el

rango de direcciones $FFFC - $FFFF. A continuación, se procede a realizar una

visión general de los mismos:

Tabla 4. $FFFC: Control mapping de la RAM.

Bit Función

7 Habilitar "escritura ROM"

6 - 5 No usado

4 Habilitar RAM ($C000-$FFFF)

3 Habilitar RAM ($8000-$BFFF)

2 Selección del banco de RAM

1 - 0 Cambio de RAM

Si el bit 2 está a 0 entonces se selecciona el primer banco del cartucho de la RAM, si

está a 1, se selecciona el segundo banco.

Tabla 5. $FFFD - $FFFF: Control del mapping de la ROM.

Dirección Selección del slot de ROM

$FFFD Ranura 0 ($0000-$3FFF)

$FFFE Ranura 1 ($4000-$7FFF)

$FFFF Ranura 2 ($8000-$BFFF)

12.4. Sistema básico de entrada-salida (BIOS)

La BIOS o el sistema básico de entrada-salida (Basic Input-Output System en inglés) define

la interfaz de firmware cuyo propósito principal es el de activar una máquina (la Sega Master

System en este caso) desde su encendido y preparar el entorno para cargar el sistema

operativo o gestor de arranque en la memoria RAM. Es lo primero que se ejecuta cuando

iniciamos la consola [19].

Las consolas SMS incluyen una simple BIOS que es la que se encarga de: gestionar el

proceso de arranque de la consola, mostrar por pantalla la animación de logo de inicio de

Sega y de mostrar instrucciones o errores por pantalla. Además, en las SMS no japonesas, la

BIOS requiere que la ROM insertada contenga una cabecera válida para poder ejecutar el

software, si no, no funcionará el juego.

Page 176: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

176

Cabecera de la ROM

Para poder entender mejor cómo se comporta la BIOS en la SMS, primero hay que conocer

el contenido de la cabecera de la ROM. Esta tiene un tamaño total de 16 bytes y se puede

encontrar en las direcciones $7FF0, $3FF0 y $1FF0, aunque solo la primera de estas es la

que se suele utilizar. Dentro de esos 16 bytes de la cabecera se encuentra:

• TMR SEGA ($7FF0, 8 bytes): Los primeros 8 bytes de la cabecera contienen el

texto ASCII “TMR SEGA” y son los bytes que requiere la BIOS para verificar que

la cabecera de la ROM sea válida.

• Espacio reservado ($7FF8, 2 bytes).

• Checksum ($7FFA, 2 bytes): Estos 2 bytes almacenan el valor de Checksum. Este

valor es una medida de seguridad con el objetivo de prevenir a los piratas de

modificar la imagen de ROM. Este valor es comparado con el calculado por las

rutinas de la BIOS para verificar que los datos del cartucho sean válidos.

• Código del producto ($7FFC, 2.5 bytes): Contiene el código del producto.

• Código de región/versión ($7FFE, 1 byte): Los 4 bits más inferiores contienen la

versión del producto. Los otros 4 bits, proporcionan información sobre la región y

sistema del cartucho insertado. Si representan el valor $4 (0100), hace referencia a

las SMS Export, es decir, a las versiones no japonesas.

Comportamiento de la BIOS

Una vez conocido el contenido de la cabecera de la ROM, se procede ahora a explicar todo

lo que realiza la BIOS de la SMS, desde que se enciende la consola hasta que se ejecuta el

videojuego.

Lo primero que se realiza cuando se enciende una SMS es comprobar si en algunas de las

ranuras disponibles de la consola (la tarjeta, el cartucho o la expansión) hay algo adjunto en

ellas y arranca la primera que esté disponible. Esto se realiza mediante el puerto $3E (que es

el puerto por el que se accede al hardware encargado de gestionar el espacio de memoria

disponible) con el objetivo de mapear diferentes ranuras en memoria y comprobar ahí si se

ha encontrado algún dato válido en alguna de las ranuras.

Si en alguna de estas ranuras se encuentran datos válidos, entonces lo siguiente que realiza

la BIOS es mostrar por pantalla la animación de inicio del logo de SEGA.

Page 177: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

177

Tras esto, comprueba todas las ranuras una detrás de otra. Para cada ranura, 16 bytes son

copiados a la RAM de la consola para comprobar si contienen una cabecera de ROM válida,

es decir, comprueba si los primeros 8 bytes de la cabecera coinciden con el texto ACII “TMR

SEGA” y esto lo hace desde las direcciones de memoria $7FF0, $3FF0 y $1FF0

respectivamente. Si coinciden, entonces continua con más pruebas. En caso de que no

encuentre una cabecera en ninguna de las 3 localizaciones especificadas, se asume que la

ranura está vacía y por lo tanto no se ejecutará nada.

A continuación, lo que comprueba es la zona referente a la región. Si los 4 bits más altos del

último byte de la cabecera de la ROM no coinciden con el valor $4, el cartucho es rechazado

y se dibuja por pantalla un mensaje de error de software.

Finalmente, la última comprobación que realiza la BIOS es el checksum. Las rutinas de la

BIOS calculan el checksum sumando cada palabra desde la dirección $200 hasta el final de

la ROM. Si el valor calculado no coincide con el que hay situado en la cabecera de la ROM,

vuelve a mostrar por pantalla un mensaje de error de Software. Si el valor coincide, entonces

se han pasado todas las pruebas de la BIOS y se procede a ejecutar el videojuego.

12.5. Video Display Processor (VDP)

El VDP es un chip de gráficos que se puede encontrar en el interior de las videoconsolas de

Sega, donde en cada una de estas, hay diferentes versiones de este chip. El VDP contiene en

su interior una serie de registros y algo de RAM, la cual es controlada por los puertos $be y

$bf, que son utilizados para controlar los datos y para el control del VDP, respectivamente

[18].

La resolución de la pantalla del VDP es de 256 píxeles horizontales (ancho) y 192 píxeles

verticales (alto), donde cada pixel puede ser mostrado como uno de 16 colores, estos

seleccionados a partir de una paleta formada por un total de 64 colores distintos.

El fondo de la pantalla está compuesto por tiles de 8x8 píxeles, por lo que esto da lugar a

un total de 768 tiles visibles en pantalla (32 horizontales x 24 verticales). Además, hay 4

filas adicionales debajo de las 24 filas verticales visibles para poder realizar el conocido

efecto de “scroll” con la información que se encuentre abajo. De esta forma, la información

ubicada en la parte inferior pasa a ser mostrada en la parte superior de la pantalla.

Page 178: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

178

Hay dedicados un total de 16 kb de RAM para el sistema de video. Esta RAM recibe el

nombre de VRAM. Para poder escribir o leer de la Video RAM se hace a través de los

registros del VDP.

Organización de la VRAM

Estos 16kb pertenecientes a la memoria de vídeo, están divididos en 3 secciones:

• Un mapa de pantalla de bytes (1792 bytes): Determina la colocación de los tiles

sobre la pantalla de fondo de 32x24 cuadrículas, es decir, define las posiciones en

pantalla de los 896 tiles, de los cuales solo 768 son visibles.

• El Sprite Attribute Table (SAT) (256 bytes): Esta tabla establece las coordenadas

X-Y y número de tiles de hasta 64 objetos movibles o sprites.

• Generador de carácter de bytes (14.336 bytes): Estos son los tiles de 8x8 píxeles

para el fondo y/o los tiles de 8x8 píxeles (o 8x16) de los sprites.

La colocación de cada uno de las 3 partes de la VRAM es controlada por los registros del

VDP como se verá más adelante.

Programación del VDP

Para poder trabajar con el VDP se realiza el envío de una secuencia de 2 bytes al puerto de

control. Esta secuencia se utiliza para definir un desplazamiento en la VRAM o CRAM para

la posterior I/O (entrada/salida) del puerto de datos y también, para escribir en los registros

internos del VDP [18][33].

Con el fin de que el VDP pueda saber si recibe el primer o segundo byte, tiene un flag que

se establece después de que el primer byte sea enviado, y se vacía cuando el segundo byte

es escrito.

Este flag también es vaciado cuando se lee del puerto de control, o cuando se lee o se escribe

del puerto de datos. Esto principalmente se realiza para inicializar el flag a 0 después de que

este haya sido modificado impredeciblemente.

Page 179: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

179

Para poder saber a qué parte acceder del VDP, este tiene 2 componentes que permiten

indicarlo: El registro de dirección (address register) y el registro de código (code register).

El primero, es de 14 bits y define la dirección en VRAM para lecturas y escrituras, y la

dirección en CRAM para escrituras. El code register, es de 2 bits y selecciona la operación

a realizar.

Cuando se escribe el primer byte, los 8 bits más bajos de los 14 totales disponibles en el

address register, son actualizados. Cuando el segundo byte es escrito, los 6 bits restantes del

address register y los 2 bits del code register son actualizados.

Hay 4 operaciones posibles a realizar: escribir en VRAM, leer de VRAM, escribir en CRAM

y escribir en un registro del VDP. La siguiente tabla resume que operación se realiza en

función del valor del code register.

Tabla 6. Operaciones disponibles del VDP.

Valor del code register Operación que se realiza

00

Se lee un byte de VRAM desde la dirección

definida por el address register y es almacenado

en el buffer de lectura. El address register es

incrementado en 1. Lo que se escriba en el puerto

de datos va a la VRAM.

01

Lo que hay escrito en el puerto de datos ($be) se

dirige a la VRAM, es decir, se escribe en

VRAM.

02 Escribir en un registro del VDP. Solo hay 10

registros disponibles en total.

03

Lo que hay escrito en el puerto de datos ($be) se

dirige a la CRAM, es decir, se escribe en la

CRAM.

Page 180: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

180

Cuando se accede a la CRAM, los bits superiores del address register son ignorados ya que

la CRAM es más pequeña que los 16kb de la VRAM. Esto quiere decir que, si se desea

escribir en alguna dirección de la CRAM, el valor hexadecimal que se pase al registro HL

siempre empezará por $C0, ya que, los dos bits más significativos serán 11 y el resto serán

todos 0.

Vamos a observar exactamente cómo funciona todo este proceso de trabajar con el VDP con

un ejemplo. Imaginemos que tenemos el siguiente código:

Figura 116. Paso de información al puerto $bf del VDP.

Fuente: Elaboración propia.

Teniendo en cuenta el código anterior, digamos que en el registro hl, cargamos el valor

$4000. Al ejecutar el código anterior, lo que se haría sería primero enviar al puerto el valor

$00 (00000000) de $4000. Mediante este primer byte que enviamos (y los 6 bits del siguiente

byte que enviaremos a continuación) indicamos la dirección del VRAM (el address register).

Una vez enviado el primer byte al puerto $bf, enviamos el segundo. En h, se encontrará el

valor $40 (01000000) y corresponderá al segundo byte a enviar. De este, los 6 primeros bits

menos significativos hacen también referencia, junto a los bits del byte anterior, a la

dirección del VRAM. Los 2 bits restantes indican la operación a realizar (code register).

Por lo tanto, si nos fijamos en el valor que hemos pasado. Vemos que hemos pasado como

dirección de VRAM el valor 0000000000000 y como operación a realizar el valor 01, que

fijándonos en la figura 13-5, sabemos que corresponde a escribir en VRAM.

Escribir en un registro del VDP

Si se desea escribir en un registro del VDP, se tienen que enviar 2 bytes exactamente de la

misma forma a como se explica en la siguiente tabla.

Page 181: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

181

Tabla 7. Formato para poder escribir en un registro del VDP.

En el primer byte, sus 8 dígitos se utilizarán para establecer los datos del registro, es decir,

para especificar que bits del registro son los que se desean activar (1) o desactivar (0). En el

segundo byte que se envía, sus 4 primeros bits menos significativos indicarán el número de

registro al cual se quieren enviar los datos del primer byte, mientras que, en los 4 bits

restantes, los 2 primeros son ignorados y los 2 bits más significativos contendrán los valores

1 y 0.

Registros del VDP

Los registros del VDP permiten configurar los aspectos gráficos de la consola. Existen en

total unos 10 registros en el VDP, cada uno con su función específica. A continuación, se

van a ver uno por uno, que aspectos de la consola pueden ser configurados y/o modificados

en cada uno de ellos:

• Registro $00 – Modo de Control Nº 1

Los bits de este registro, cuando son activados afectan a diversos aspectos de la

visualización de la pantalla. Al ser activados, cada uno tiene las siguientes funciones:

Bit 7: Las 8 columnas de más a la derecha (24-31) de la pantalla no se ven afectadas

por el desplazamiento vertical (vertical scrolling).

Bit 6: Las dos filas superiores (0-1) no se ven afectadas por el desplazamiento

horizontal (horizontal scrolling).

Bit 5: No se visualiza la columna más a la izquierda de la pantalla.

Bit 4: Habilita la interrupción de línea (line interrupt).

Bit 3: Desplaza todos los sprites de la izquierda un carácter (8 pixeles) permitiendo

que sean dibujados correctamente cuando su lado izquierdo está a la izquierda de la

pantalla.

MSB LSB

2º byte 1 0 ? ? R03 R02 R01 R00

1º byte D07 D06 D05 D04 D03 D02 D01 D00

Rxx: Numero de registro del VDP

Dxx: Datos del registro del VDP

?: Bits ignorados

Page 182: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

182

Bit 2: Activa el Modo 4 de visualización de pantalla que es una visualización

específica del chip VDP de la SMS. Siguiendo la documentación oficial de Sega este

bit siempre debería estar activado para evitar problemas.

Bit 1: Habilita la altura extra de la pantalla. Siguiendo la documentación oficial de

Sega este bit siempre debería estar activado.

Bit 0: Desactiva la sincronización de la pantalla, la visualización es monocroma. Si

está a 0, la visualización es normal. Siguiendo la documentación oficial de Sega este

bit siempre debería estar a 0.

• Registro $01 – Modo de Control Nº 2

Más bits encargados de controlar la visualización de pantalla:

Bit 7: No se usa. Siguiendo la documentación oficial de Sega este bit siempre debería

estar activado.

Bit 6: Habilita la visualización de la pantalla. Si esta desactivado no se ve ninguna

imagen por pantalla.

Bit 5: Habilita la generación de interrupción del VSync (frame interrupt). La mayoría

de los juegos lo utilizan para controlar sus tiempos más básicos.

Bit 4: Extiende la pantalla 4 filas si el bit 1 del registro $00 está activado. Siempre

desactivado según lo documentación oficial de Sega.

Bit 3: Extiende la pantalla 8 filas si el bit 1 del registro $00 está activado. Siempre

desactivado según lo documentación oficial de Sega.

Bit 2: No se usa. Siguiendo la documentación oficial de Sega este bit siempre debería

estar desactivado.

Bit 1: Al activarse los sprites pasan a ser de 8x16. Si está a 0 se usan los sprites por

defecto (8x8).

Bit 0: Todos los pixeles de los sprites son duplicados en tamaño, obteniendo así

sprites con zoom.

• Registro $02 – Dirección base del nombre de la tabla de VRAM

Establece la dirección base del nombre de la tabla en VRAM. Este nombre tiene una

longitud de $700 bytes. Formato del registro:

Page 183: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

183

Tabla 8. Formato del registro $02.

bit 7 6 5 4 3 2 1 0

x x x x d d d M

| | |

Direcc. 13 12 11 10

VRAM d d d 0

La d representa los bits que contienen la dirección base del nombre de la tabla en VRAM.

La x representa los bits que no se usan y la M, los bits de máscara. En la mayoría de los

casos, todos los bits del registro estan establecidos con el valor 1 para otorgar al nombre de

la tabla de VRAM una dirección base de $3800.

• Registro $03 – Dirección base del color de la tabla de VRAM

Este registro, es un registro del chip original del cual deriva el VDP de la SMS.

Debido a ello, sus bits no tienen ningún efecto en la SMS. Aun así, todos sus bits

deberían estar a 1 para un funcionamiento normal.

• Registro $04 – Dirección base del generador de patrones

Al igual que el registro $03, este registro deriva del chip original de VDP. Ninguno

de sus bits tiene algún efecto en la SMS. Aun así, por lo menos los 3 bits menos

significativos deberían estar activados para un funcionamiento normal.

• Registro $05 – Dirección base de la tabla con información de los sprites (Sprite

Attribute Table en inglés)

Se utiliza para establecer la dirección base de la tabla con información de los sprites

en VRAM. Concretamente, contiene información de las coordenadas X-Y y número

de tiles de hasta 64 objetos movibles o sprites. Formato del registro:

Tabla 9. Formato del registro $05.

bit 7 6 5 4 3 2 1 0

x d d d d d d M

| | | | | |

Direcc. 13 12 11 10 9 8 7 …….

VRAM d d d d d d 0 …….

La d representa los bits que constituyen la dirección base de la tabla de VRAM, que

serían los bits 13, 12, 11, 10, 9, 8 y 7. La M representa los bits de la máscara y la x,

los bits que no se usan.

Page 184: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

184

En la mayoría de los casos, todos los bits pueden estar a 1 para dar a la tabla de

VRAM una dirección base de $3F00, que corresponde a los últimos 256 bytes de la

memoria de video.

• Registro $06 – Dirección base para la definición de los tiles

Este registro establece la dirección base de las definiciones de los tiles que se usan

para los sprites. Este registro es necesario debido a que solo se usa valor de 8 bits

para almacenar el número del tile de cada uno de los sprites en la tabla con la

información de sprites. Formato del registro:

Tabla 10. Formato del registro $06.

bit 7 6 5 4 3 2 1 0

x x x x x d M M

Si d=0, todos los sprites usan los tiles definidos desde la dirección $0000, (tiles 0-

255), si no, si d=1, todos los sprites usan los tiles definidos desde la dirección $2000

(tiles 256-511). Los 2 bits menos significativos, actúan como una máscara AND

sobre los bits 8 y 6 del índice del tile. La x representan los bits que no se usan.

• Registro $07 – Color de fondo/borde

En este registro, sus 4 bits menos significativos se utilizan para seleccionar el número

del color (de 0 a 15) de la paleta de sprites (2º paleta) para usarlo en el borde de la

pantalla. El resto de bits no tienen ningún efecto.

• Registro $08 – Desplazamiento en X del fondo

Está formado por 8 bits que definen el valor de scroll horizontal. Los 5 bits superiores

indican la columna de inicio y los 3 bits restantes, define el valor del scroll (fine

scroll value en inglés).

En cada scanline (línea de exploración horizontal) el VDP tiene un contador de

columnas que va desde 0 hasta 31. De tal manera que, el pixel exacto en el cual, el

VDP va a empezar a renderizar las columnas, es desplazado hacia la derecha en

función del valor de scroll definido en los 3 bits menos significativos del registro

actual.

Por ejemplo, si el valor del scroll fuese 7 (los 3 bits a 1), entonces las 31 columnas

serían totalmente visibles y el primer pixel de la columna 32 es mostrado en el borde

derecho de la pantalla. Después de cada columna es dibujada, el valor de la columna

inicial es incrementado en 1. Un valor de scroll $00 no produce ningún scroll.

Page 185: Trabajo Fin de Grado - RUA, Repositorio Institucional de ...rua.ua.es/dspace/bitstream/10045/96467/1/Desarrollo_de_videojueg… · Portada del videojuego Alien 3..... 12 Figura 2

185

Si el bit 6 del registro $00 estuviese activo, el scroll horizontal será arreglado a 0 para

las scanlines de 0 a 15. Este se suele utilizar para crear una barra de estado fija en la

parte superior de la pantalla para juegos de scroll horizontal.

• Registro $09 – Desplazamiento en Y del fondo

Mismo formato que el registro anterior, pero en este registro sus bits definen el valor

de scroll vertical. Sus 5 bits superiores controlan la fila de inicio y los 3 bits más

bajos, el valor de scroll.

En el modo de visualización normal de 192 líneas, el nombre de la tabla tiene un

tamaño de 32x28, por lo que el registro de scroll vertical se ajusta cuando supera 223.

El valor de scroll vertical no se puede cambiar hasta que no haya terminado el periodo

actual de visualización, todos los cambios que se realicen durante este proceso serán

almacenados en una localización temporal y serán utilizados cuando acabe el periodo

de visualización.

• Registro $0A – Contador de líneas

El haz de la televisión barre las líneas horizontales de la pantalla, que reciben el

nombre de raster lines. Este barrido, lo realiza de izquierda a derecha, moviéndose

desde la parte superior de la pantalla hasta la parte inferior. En total hay 192 raster

lines que corresponden a los 192 píxeles del alto de la pantalla.

Al final de cada raster line, se produce un pequeño intervalo en el que el barrido se

apaga hasta que retrocede desde el lado derecho de la pantalla hasta el lado izquierdo

del siguiente raster line y este intervalo, es lo que se conoce como HBlank.

Los 8 bits del registro $0A sirven para definir cada cuantas líneas (raster lines) se

desea que se produzca una interrupción, la conocida como HBlank interrupt o line

interrupt. De esta manera, si se pasa el valor $00 a este registro, se genera una

interrupción por cada línea horizontal, si se pasa el valor $01 se producirá cada 2

líneas horizontales etc. Si se pasa el valor $FF se desactiva la generación de

interrupciones, es decir, no se produce la line interrupt.