lunes, 29 de enero de 2018

Spread It Out (Global Game Jam 2018)

Videojuego realizado en 48 horas para la Game Jam On 2018 (Global Game Jam) por Imanol Barriuso, Pelayo Martinez y Fernando Olea.


Plataforma: Oculus Rift
Página del juego: https://globalgamejam.org/2018/games/spread-it-out

miércoles, 27 de diciembre de 2017

Last Crown Warriors #3: Collisions

The Last Crown Warriors DEMO has been published recently. Since the last entry on Imanolea's Games about the game has been some time, and much functionality is pending for explanation. So I decided to ask on Twitter for the feature or system of Last Crown Warriors that you would like to be clarified on the blog.
From this announce I received an email from Jonas proposing to write about the collisions.

It is not the first time that I perceive interest in reading an entry dedicated to collisions. It is something present in almost any type of game, but a lot has been written about it. So I did not have the impression that I could offer something interesting on the subject.

Now, it is also true that this routine sometimes involves a lot of processing in each of the frames. And just as in the times of the classic microcomputers a good scroll routine could make your game stand out from the others, a good collision processing can allow you (also nowadays) to maximize the number of enemies, projectiles, power ups, and other interactive objects that we see on the screen.

And naturally, managing a pixel-level collision in a modern video game engine is not the same as trying to set up the collision of multiple elements in a system like the Game Boy. So in the end I found that it could be interesting to show the way in which I try to face this challenge. My system is far from being the best, but I think it can inspire others to try to work on their own approach.

In Last Crown Warriors it is necessary to manage collisions in the following list of situations:
  • Collision of the hero with background
  • Collision of the enemies with the background
  • Collision of the hero's weapon with the enemies
  • Collision of the hero's special weapon with the enemies
  • Collision of the hero with the enemies
  • Collision of the hero with interactive elements (such as the health heart)
All these collisions occur simultaneously, and it is crucial to minimize slowdowns as much as possible. So the routines must be extremely efficient.

The collisions previously enumerated are essentially managed through two systems, which I will explain throughout this entry.
  1. Collision of a character with another character
  2. Collision of a character with the background
And although they both manage collisions they have a completely different approach, as you will see below.

As always, I will try to focus on explaining the logical concepts behind the systems. So that they can be understood and applied without having to go through lines of code.

Collision of a character with another character

Let's take as an example the collision of the hero with the enemies for this routine. Let's see, to be able to verify if the hero has collided with an specific enemy we would have the following data:
  1. X and Y coordinate of the hero
  2. X and Y coordinate of the enemy
  3. Width and height of the collision area of the hero
  4. Width and height of the collision area of the enemy
With this information we could shape two areas (the hero's and the enemy's), and check if those two areas overlap. And while it would be a valid solution, it is not by far an acceptable solution. Because although the area of the hero would only be calculated at the beginning of the routine, the enemy area should be recalculated by each of the enemy characters.

And what can we do? Let's see, we know that all enemies share the same size collision area, so why not generate a single area around the hero that combines the area lengths of both characters? That is, instead of checking if two areas overlap, check if the enemy coordinate is within the combined collision area of the hero.

Purple: Point of character position.
Orange: Character collision area.
Blue: Combined collision area of the hero.

So we would only have to calculate the blue area of the image by adding the dimensions of the collision area of the hero and the enemy's. And once we have it, and with a maximum of four simple comparisons, we check if the enemy's position point is within that area. In this way we minimize the processing to be performed by each of the additional enemies with which we want to check if our hero has collided.

This code shows the core of the check between the enemy character's position point and the combined collision area.



We can see how this routine returns the address in memory of the enemy that we have just checked. So we could place it without problems inside a loop that goes through the list of enemies on which we want to perform the collision check.

Collision of a character with the background

In this case we will always go through the same routine when checking this type of collision. Since only the hero and the enemies can collide with the background, and both share the same dimensions in the collision area.

The data to keep in mind for this routine is:
  1. X and Y coordinate of the character
  2. Horizontal and vertical movement of the character
  3. X and Y position of the background
  4. Width and height of the map
The first thing would be to get the absolute coordinates of the character, that is, the coordinates of the character within the map. For this we add the position of the background to the coordinate of the character.

Then we need to know the candidate position point to collide. For this we take into account the movement of the character, and we obtain the position of the edge of the collision area closest to colliding. If the character is not moving, we would automatically conclude that he has not collided.

Once we have the candidate position, it is important to check its relative position within the tile. I mean, if the character is aligned with the tile grid (and taking into account its collision area) we will only check the collision of a single tile, while in the rest of the cases it will be necessary to go through more than one tile.

Purple: Collision candidate position point
Orange: Character collision area
Blue: Collisionable Tiles
Red: Solid tiles

Once we have clear the absolute positions of the tiles we must place them within the map, this is where the width and height of our map come into play.

And after having obtained the value relative to the collisionable tiles, how do we know if these are solid or not? Very simple, for each map we organize the tileset so that the solid tiles are at the end of the list and the non-solid ones at the beginning. Then we have only to specify what will be the position of the list from which the tile will be considered solid.


And this would be the explanation of how Last Crown Warriors collisions work. I hope that it has been interesting and has helped you to understand how a collision system can be set up in a limited hardware.

If you want to know how a particular aspect of the game works, tell me about it and I will try to talk about it in future posts.

Last Crown Warriors #3: Colisiones

La DEMO de Last Crown Warriors ha sido publicada recientemente. Desde la última entrada de Imanolea's Games dedicada al juego ha pasado algún tiempo, y mucha funcionalidad ha quedado pendiente de ser explicada. Así que decidí preguntar por Twitter por la característica o sistema de Last Crown Warriors que os gustaría que fuera destripado en el blog.
En base a este anuncio recibí un mail del compañero Jonas proponiendo escribir sobre las colisiones.

No es la primera vez que percibo interés por ver una entrada dedicada a las colisiones. Es algo con lo que tenemos que lidiar en casi cualquier tipo de juego, pero también algo sobre lo que se ha escrito mucho. Así que no me pareció un tema sobre el cual yo pudiera ofrecer algo interesante.

Ahora bien, también es cierto que esta rutina engloba en ocasiones gran parte del procesamiento de cada uno de los frames. Y al igual que en la época de los microordenadores una buena rutina de scroll podía hacer destacar tu juego de entre los demás, un buen procesamiento de colisiones puede permitirte (también a día de hoy) maximizar el número de enemigos, proyectiles, power ups, y demás objetos interactuables que vemos en pantalla.

Y naturalmente, no es lo mismo gestionar una colisión a nivel de píxel en un motor de videojuegos moderno que intentar plantear la colisión de multiples elementos en un sistema como el de la Game Boy. Así que en última instancia me pareció interesante mostrar la manera en la que yo procuro enfrentar este reto. Que si bien se encuentra lejos de ser la mejor, creo que puede inspirar a otros a probar suerte con su propio enfoque.

Concretamente en Last Crown Warriors vemos necesario gestionar colisiones en la siguiente lista de situaciones:
  • Colisión de héroe con fondo
  • Colisión de los enemigos con el fondo
  • Colisión del arma del héroe con los enemigos
  • Colisión del arma especial del héroe con los enemigos
  • Colisión del héroe con los enemigos
  • Colisión del héroe con elementos interactuables (como el corazón de salud)
Todas estas colisiones se producen simultáneamente, y es crucial minimizar en la medida de lo posible las ralentizaciones. Por lo que las rutinas deben ser extremadamente eficientes.

Las colisiones previamente enumeradas se gestionan en esencia a través de dos sistemas, que son los que explicaré a lo largo de esta entrada.
  1. Colisión de personaje con personaje
  2. Colisión de personaje con fondo
Y aunque los dos gestionen colisiones tienen un  planteamiento completamente distinto, tal y como veréis a continuación.

Como siempre, procuraré centrarme en explicar los conceptos lógicos detrás de los sistemas. De manera que se puedan entender y aplicar sin necesidad de recorrer líneas de código.

Colisión de personaje con personaje

Vamos a tomar como ejemplo la colisión del héroe con los enemigos para esta rutina. Veamos, para poder comprobar si el héroe ha colisionado con un enemigo concreto dispondríamos de los siguientes datos:
  1. Coordenada X e Y del héroe
  2. Coordenada X e Y del enemigo
  3. Ancho y alto del área de colisión del héroe
  4. Ancho y alto del área de colisión del enemigo
Con esta información podríamos conformar dos áreas (la del héroe y la del enemigo), y comprobar si esas dos áreas se superponen. Y si bien sería una solución válida, no es ni de lejos una solución aceptable. Ya que aunque el área del héroe sólo la calcularíamos al principio de la rutina, el área enemiga debería recalcularse por cada uno de los personajes enemigos.

¿Y qué podemos hacer? A ver, sabemos que todos los enemigos comparten el mismo tamaño de área de colisión, así que, ¿por qué no generar una única área alrededor del héroe que combine las longitudes de área de ambos personajes? Es decir, en lugar de comprobar si dos áreas se superponen, comprobar si la coordenada enemiga se encuentra dentro del área de colisión combinada del héroe.

Morado: Punto de posición de personaje.
Naranja: Área de colisión de personaje.
Azul: Área de colisión combinada del héroe.

Así sólo tendríamos que calcular el área azul de la imagen sumando las dimensiones del área de colisión del héroe y de un enemigo. Y una vez la tengamos, y con un máximo de cuatro comparaciones simples, comprobamos si el punto de posición del enemigo se encuentra dentro de dicha área. De esta forma minimizamos el procesamiento a realizar por cada uno de los enemigos adicionales con los que queramos comprobar si ha colisionado nuestro héroe.

Este extracto de código muestra el núcleo de la comprobación entre el punto de posición del personaje enemigo y el área de colisión combinada.



Podemos ver como esta rutina nos devuelve la dirección en memoria del enemigo sobre el que acabamos de realizar la comprobación. Así que podríamos ubicarla sin problemas dentro de un bucle que recorra la lista de enemigos sobre los que queremos realizar la comprobación de colisión.

Colisión de personaje con fondo 

En este caso pasaremos siempre por la misma rutina a la hora de comprobar este tipo de colisión. Ya que únicamente el héroe y los enemigos pueden colisionar con el fondo, y ambos comparten las mismas dimensiones en el área de colisión.

Los datos a tener en cuenta para esta rutina son:
  1. Coordenada X e Y del personaje
  2. Desplazamiento horizontal y vertical del personaje
  3. Posición X e Y del fondo
  4. Ancho y alto del mapa
Lo primero sería conseguir las coordenadas absolutas del personaje, es decir, las coordenadas del personaje dentro del mapa. Para ello sumamos la posición del fondo a la coordenada del personaje.

Luego necesitamos saber el punto de posición candidato a colisionar. Para ello tenemos en cuenta el desplazamiento del personaje, y obtenemos la posición del extremo del área de colisión más próximo a colisionar. Si el personaje no se está desplazando, interpretaríamos automáticamente que no ha colisionado.

Una vez tenemos la posición candidata, es importante comprobar qué posición relativa ocupa dentro del tile. Me explico, si el personaje se encuentra alineado con la rejilla de tiles (y teniendo en cuenta su área de colisión) verificaremos únicamente la colisión de un tile, mientras que en el resto de casos será necesario pasar por más de uno.

Morado: Punto de posición candidato a colisión
Naranja: Aréa de colisión del personaje
Azul: Tiles colisionables
Rojo: Tiles sólidos

Pues una vez tenemos claras las posiciones absolutas de los tiles debemos ubicarlas dentro del mapa, aquí es donde entra en juego el ancho y el alto de nuestro mapeado.

Y tras haber obtenido el valor relativo a los tiles colisionables, ¿cómo sabemos si estos son sólidos o no? Muy sencillo, para cada mapa organizamos el tileset de manera que los tiles sólidos quedan al final de la lista y los no sólidos al principio. Luego no tenemos más que especificar cuál será la posición de la lista a partir del cuál se considerará que el tile es solido.


Hasta aquí la explicación de cómo funcionan las colisiones de Last Crown Warriors. Espero que os haya resultado interesante y os haya servido para entender cómo se puede plantear un sistema de colisiones en un hardware como el de la Game Boy.

Si queréis saber cómo funciona algún aspecto concreto de juego, comentádmelo e intentaré hablar sobre él en próximas entradas.

domingo, 8 de octubre de 2017

Last Crown Warriors #2: Selection screen

Yesterday Light Games published a new development of Last Crown Warriors, in which the character and scenario selection screen was shown.
In the video we can see how the movement of two backgrounds occurs simultaneously (the selection bar and the map at the top). And we can also see a non-linear movement, with accelerations and decelerations.

At logic level, achieving these features in a program should not be a big challenge. Although in the case of the Game Boy, given the limitations of the system, a little prior planning is necessary. So leaving aside the technicalities and focusing on the logical design I'll explain the key elements of this selection screen.

 

Sinusoidal movement

In order to achieve greater dynamism, it has been decided to apply a sinusoidal movement in the shifts of the bar, the top map, the arrows, and the selectable characters. Calculating the sine of a number would be an overly expensive task for a Game Boy, so a little part of the memory has been sacrificed and these shifts have been stored in data tables.

By going throught the values of a table like this, we can apply to the elements the proper movement in each iteration. This table represents the top map shift when leaving the screen.

 

Scene composition

The logic of this screen uses some of the properties of the Game Boy system, such as hardware scroll or sprites. In this image the components are differentiated by color.


In red: sprites; in blue: the main background; in purple: the secondary background or window.

As you can see, moving the top scenario is as simple as applying the Game Boy hardware scroll on the main background. Of course, you have to keep in mind that this map must be redrawn. In this case there is only one selectable scenario, but in the case of having more than one, it would be necessary to reprint the background with each selection change. In the following image we see the evolution of the main background during that change.


This is the Game Boy main background, the red box being the visible area of the screen during the scenario change. Through one limit of the visible area we first remove the current map and later we print the new one.

The window have a disadvantage. It is not possible to reduce its horizontal coordinate below zero, which would be the natural way to make it enter the left limit of the screen. So as you can see, we use an auxiliary sprite during the movement that joins the bar.

Graphical update

In addition to the shifts there are also times when we have to reload the graphics of the backgrounds. The most obvious (and costly) of these reloads is the update of the window once the selection shift ends and all the icons alter their order.

The solution is to have a strict control of the VBlank interruption, and to optimize the graphic reprint. This routine is the responsible for reloading the window, and already has the list of values to copy ready. The formula in this case is the same as the one used in the Last Crown Warriors scroll, which I explain in this entry.


Now it's time to develop the game system of Last Crown Warriors. Any functionality that deserves to be explained, will be the focus of the next blog entries.

Last Crown Warriors #2: Pantalla de selección

Ayer mismo publicaba Light Games un nuevo avance de Last Crown Warriors en el que se mostraba la pantalla de selección de personaje y escenario.
En el vídeo podemos ver cómo se produce el desplazamiento de dos fondos de manera simultánea (el de la barra de selección y el del mapa de la parte superior). Y también se puede apreciar un movimiento no lineal, con aceleraciones y deceleraciones.

A nivel lógico, conseguir plasmar estas características en un programa no debería suponer un gran reto. Aunque en el caso de la Game Boy, dadas las limitaciones del sistema, se hace necesaria una pequeña planificación previa. Así que dejando a un lado los tecnicismos y centrándome en el diseño lógico, paso a exlicaros las claves funcionales de esta pantalla de selección.

 

Movimiento sinusoidal

De cara a lograr un mayor dinamismo se ha optado por aplicar un movimiento sinusoidal en los desplazamientos de la barra, del escenario del fondo superior, de las flechas, y de los personajes seleccionables. Calcular el seno de un número sería una tarea excesivamente costosa para una Game Boy, por lo que se ha sacrificado un poco de memoria y se han almacenado estos desplazamientos en tablas de datos.

Así, recorriendo los valores de una tabla como esta, podemos aplicar al elemento el desplazamiento indicado en cada iteración. Esta tabla representa el desplazamiento del mapa superior al abandonar la pantalla.

 

Composición de la escena 

La lógica de esta pantalla se sirve de alguna de las propiedades del sistema de la Game Boy, como es el scroll por hardware o los sprites. En esta imagen están diferenciados por color los distintpos componentes en acción.


En rojo: sprites; en azul: el fondo principal; en morado: el fondo secundario o ventana.

Como veis, desplazar el escenario de la parte superior es tan sencillo como aplicar el scroll por hardware de Game Boy en el fondo principal. Eso sí, hay que tener en cuenta que dicho mapa debe ser redibujado. Ya que en este caso sólo hay un escenario seleccionable, pero en el caso de haber más sería necesario reimprimir el fondo con cada cambio de selección. En la siguiente imagen vemos la evolución del fondo principal durante dicho cambio.


Este es fondo principal de Game Boy, siendo el recuadro rojo la zona visible de la pantalla durante el cambio de escenario. Por un lateral de la zona visible eliminamos primero el mapa actual para más tarde imprimir el nuevo.

Y en cuanto a la ventana tenemos una desventaja. No es posible reducir su posición horizontal por debajo de cero, que sería la forma natural de introducirla por el límite izquierdo de la pantalla. Así que como se puede observar, nos servimos de un sprite auxiliar durante el desplazamiento que parece mimetizarse con la barra.

Actualización gráfica

Además de los desplazamientos también hay momentos en los que se deben recargar los gráficos de los fondos. La más evidente (y costosa) de estas recargas es la relativa a la actualización de la ventana una vez el cambio de selección termina, donde todos los iconos que la componen ven alterado su orden.

La solución pasa por tener un control estricto de la interrupción de VBlank, y en optimizar al máximo la reimpresión gráfica. Esta rutina es la encargada de recargar la ventana, la cual ya cuenta con la lista de valores a copiar. La fórmula en este caso respecto a la actualización gráfica es la misma que la usada en el scroll de Last Crown Warriors, la cual explico en esta entrada.


A partir de ahora toca empezar a desarrollar y profundizar en el sistema de juego de Last Crown Warriors. Cualquier funcionalidad que merezca ser explicada, será protagonista de las próximas entradas.

viernes, 7 de julio de 2017

Sheep Kaboom (BitBitJam4)

Game Boy game made during a week by Light Games for #BitBitJam4

Hold A to charge the shoot. Release it to launch the sheep.
Aim to the target and avoid the spikes.


Design and programming: Imanolea
Graphics: Seiyouh
System: Game Boy

domingo, 9 de abril de 2017

Last Crown Warriors #1: Building the scroll system

In the April 1 screenshotsaturday I posted a short video in which the Last Crown Warriors scrolling and collision system could be seen in action.
In the video we can see Lilian moving at a speed of 7 pixels per frame along a stage of 512 by 512 pixels. In this post I will explain the logic behind the scroll system, which as you will see below, allows a scrolling of up to 8 pixels per frame.

The first thing to understand when it comes to setting up a scrolling system for Game Boy is that, in this console, any graphical update that is performed with the LCD switched on must occur during the VBlank interrupt (also during the HBlank interrupt, although is not considered for the scroll). And this interrupt marks the beginning of a period of time in which the system is not using the video ram, period in which therefore we have available that memory in order to modify it.

This interruption occurs 59.7 times per second (one for each frame of the game) in a Game Boy. This graph represents the duration of the interrupt (4,560 clock cycles) with respect to the total time of the frame (70,224 clock cycles).

Based on this graph we can conclude that the graphical update must be highly optimized.

And the second thing we need to understand is how the background scrolling works in this system. The scrolling of the background works at a hardware level, that is, changing the value of a register can alter the position of the background we have loaded into memory. But the size of this background is limited, 256 by 256 pixels, so for maps that exceed those dimensions it will need to be modified.

The base of the scroll system will then be the redrawing of the background. And taking into account that the graphic modification should be as optimum as possible, it only remains to decide which part of the background should be updated during the interruption. In Last Crown Warriors, we update the visible column and row of the part of the background to which we are going. For example, if you go up, left, or up and left, we will update the background bar highlighted in the next image. The blue box represents the visible background.


For the rest of directions we will update the corresponding bar following the same principle: update the row and column closest to being visible. This full modification of column and row allows us to scroll the background up to 8 pixels per iteration.

Once we are clear about what tiles need to be replaced, it remains to answer the question of how to do it. As we have seen, the time available to modify the video memory is very limited. So in Last Crown Warriors we prepare a data structure with the following information, in order to minimize the time it takes to replace the tiles of the background:
  • byte 0: low byte of the address of the tile to be replaced;
  • byte 1: high byte of the address of the tile to be replaced;
  • byte 2: new tile value.
This structure is repeated for each of the tiles that we have to update, 41 in total. Thus, during the interruption of VBlank we must only execute the following code, pointing to the beginning of the previously prepared data structure.


We use REPT so that the indicated code is repeated for each of the tiles. Discarding a possible loop, which would consume additional processing.

Also it is important that in case we use other Game Boy interrupts we execute "ei" (enable interrupts) at the start of them. So we make sure that the VBlank interrupt is called at the right time and that no part of it will ever run outside the VBlank period.

And this would be a conceptual explanation of how the scrolling system works in this game. In future posts I will try to cover other aspects of the development. For these posts it will not be necessary to have an advanced knowledge of the system in order to understand the exposed ideas.

Last Crown Warriors #1: Creando el sistema de scroll

En el screenshotsaturday del 1 de abril publiqué un vídeo corto en el que se podía obervar el sistema de scroll y colisiones de Last Crown Warriors en acción.
En el vídeo podemos ver a Lilian desplazándose a una velocidad de 7 píxeles por frame a lo largo de un escenario de 512 por 512 píxeles. En esta entrada me dedicaré a explicar la lógica que hay detrás del sistema de scroll, que como veréis a continuación, permite un desplazamiento por frame de hasta 8 píxeles.

Lo primero que hay que entender a la hora de plantear un sistema de scroll para Game Boy es que, en esta consola, toda actualización gráfica que se realice con el LCD encendido debe producirse durante la interrupción de VBlank (o durante la interrupción de HBlank, aunque no la tenemos en cuenta para el scroll). Y es que esta interrupción marca el inicio de un periodo de tiempo en el que el sistema no está haciendo uso de la memoria de vídeo, periodo en el que por lo tanto tenemos disponible dicha memoria para modificarla.

Esta interrupción se produce 59,7 veces por segundo (una por cada frame del juego) en una Game Boy. Esta gráfica representa la duración de la interrupción (4.560 ciclos de reloj) respecto al total de tiempo que dura el frame (70.224 ciclos de reloj).

En base a estos datos podemos concluir que la actualización gráfica debe estar altamente optimizada.

Y lo segundo que necesitamos entender es cómo funciona el scroll de fondos en este sistema. El desplazamiento del fondo funciona a nivel de hardware, es decir, con cambiar el valor de un registro podremos alterar la posición del fondo que tengamos cargado en memoria. Pero el tamaño de este fondo es limitado, de 256 por 256 píxeles, así que para mapas que superen dichas dimensiones será necesario modificarlo.

La base del sistema de scroll será entonces el redibujado del fondo. Y teniendo en cuenta que la modificación gráfica debe ser lo más óptima posible, queda decidir qué parte del fondo debe actualizarse durante la interrupción. En Last Crown Warriors, actualizamos la columna y la fila visible de la parte del fondo a la que nos dirigimos. Por ejemplo, si nos dirigimos hacia arriba, hacia la izquierda, o hacia arriba y hacia la izquierda, actualizaremos la franja del fondo resaltada en la siguiente imagen. El recuadro azul respresenta en este caso el fondo visible.


Para el resto de direcciones actualizaremos la franja correspondiente siguiendo el mismo principio: actualizar la fila y columna más próxima a ser visible. Esta modificación íntegra de columna y fila nos permite un desplazamiento del fondo de hasta 8 píxeles por iteración.

Una vez tenemos claro qué tiles hay que sustituir, queda responder a la pregunta de cómo hacerlo. Como hemos visto, el tiempo disponible para modificar la memoria de vídeo es muy limitado. Por lo que en Last Crown Warriors se prepara una estructura de datos con la siguiente información, de cara a reducir al mínimo el tiempo que conlleva sustituir los tiles del fondo:
  • byte 0: parte baja de la dirección del tile a sustituir;
  • byte 1: parte alta de la dirección del tile a sustituir;
  • byte 2: nuevo valor del tile.
Esta estructura se repite por cada uno de los tiles que tenemos que actualizar, 41 en total. De esta forma, durante la interrupción de VBlank sólo debemos ejecutar el siguiente código, apuntando al inicio de la estructura de datos previamente preparada.


Usamos REPT para que el código señalado se repita por cada uno de los tiles. Desechando así un posible bucle, el cual consumiría procesamiento adicional.

Además es importante que en el caso de que usemos otras interrupciones de Game Boy ejecutemos "ei" (enable interrupts) al inicio de las mismas. De este modo nos aseguramos que la interrupción de VBlank se llama en el momento adecuado y que ninguna parte del código con el que actualizamos los gráficos se ejecuta fuera del periodo de VBlank.

Y esta sería una explicación conceptual de cómo funciona el sistema de scroll en este juego. En próximas entradas intentaré cubrir otros aspectos del desarrollo, de manera que no sea necesario tener unos conocimientos avanzados sobre el sistema para entender las ideas que se expongan.
 

miércoles, 28 de diciembre de 2016

Games aside #1: Super Game Boy development (step by step)

Throughout this post we will learn to implement some of the features offered by the Super Game Boy, specifically those used by the recently published Super Game Boy compatible Rocket Man demo. Demo exposed as a local production in the international independent video game contest AzPlay held last November in Bilbao.

Before starting I would like to clarify a number of points:
  1. the program that we will elaborate is programmed in assembler and makes use of RGBDS for the creation of the ROM;
  2. this publication does not cover the bases of the development for Game Boy in assembler, but nor does it require an advanced knowledge on the subject. It can work as an additional lesson to the numerous tutorials that exist on the web in this respect (personal recommendation, https://avivace.ovh/apps/gbdev/salvage/tutorial_de_ensamblador%20%5bLa%20decadence%5d.html);
  3. according to the second point, we will work in the implementation of additional functions related to the Super Game Boy for an existing base program.
This post will cover the use of the following exclusive features of the Super Game Boy:
  • custom frame display;
  • custom game palettes.
Giving as a result the following Game Boy ROM.

Super Game Boy visualization of the ROM
 ROM DOWNLOAD

First we should talk about what the Super Game Boy is. According to the Game Boy Programming Manual (page 124),  it is a device that allows you to enjoy Game Boy software on a television screen. This software (contained in the Game Boy cartridge) can be connected to the Super Game Boy, which operates on a Super Nintendo.

As the text emphasizes, Super Game Boy operates on a Super Nintendo. This cartridge is a replica of the Game Boy hardware capable of communicating with the SNES, it helps us inform the home console of the operations we want to perform and send the necessary data to it, but it is ultimately its hardware and not ours (the Super Game Boy one) the responsible for processing and applying the unique features offered by this technology. That is, the graphics of the frame for example, obey the limitations of the SNES and not the Game Boy ones.

Preparations

That said, we move on to the preparation of the computer where we are going to develop. This is a list of the elements that we should have ready in our PC with Windows:
  1. code editor for program editing (personal recommendation, Visual Studio Code and the Z80 Assembly extension);
  2. RGBDS correctly configured as environment variable;
  3. Game Boy Tile Designer and Game Boy Map Builder for graphics and map editing respectively;
  4. Although the program of this publication is tested in a real Super Game Boy, it is recommended to have the necessary technology to execute the resulting ROM in the final hardware. Failing this, emulators like  BGB or bsnes can serve to test the developments that are made with this device in mind, but do not guarantee a perfect emulation.
Well, it's time to get down to business. As this post is about development for Super Game Boy and not about development for Game Boy we will start from a base project whose ROM offers the following result.

A static title screen

This is the main source file for this template project.


During development it will be important to have some documentation handy. In my opinion, these would be the texts that condense all you need to know when it comes to programming for Super Game Boy:
Each time I give information about the Super Game Boy I will try to complement it with the source of this information, indicating in which part of these two documents it is.

Making the graphics

We will begin by freely designing the Super Game Boy frame that we want to apply. Of course, taking into account the following limitations:
  1. The resolution of the frame must match the resolution of the SNES, ie, 256 pixels wide by 224 pixels high;
  2. we must leave a blank space of 160 by 144 pixels in the central part, which is where the screen of the Game Boy will be displayed;
  3. the SNES has 8 palettes of 16 RGB 15 bit colors (SGB Functions> Available SNES Palettes). The first four (0-3) are used to color the Game Boy's own graphics and the last four (4-7) for the frame. In the section Frame palettes I explain in more detail this limitation;
  4. we can not use more than 256 tiles of 8 by 8 pixels at the time of designing the frame (the tiles of the SNES are 8x8 and we have a byte to map them. The good thing is that these tiles can be flipped horizontally and vertically.

Frame palettes

As we have commented we have at our disposal 4 palettes of 16 colors for the frame. Although not exactly, according to the official documentation we have the 4-6 (Game Boy Programming Manual> page 162), ie 3 palettes. Anyway we can also use the 7. But some emulators will not recognize this last palette correctly.

15 bit RGB color, what does this mean? We have 5 bits to indicate the level of red, 5 for green and 5 for blue. That is, an accuracy of 0-31 to define the intensity of each of the colors. If we have an RGB color of 24 bit (the predominant format) that we want to use on any of our palettes we would have to round up the value from 0 to 255 that we have for color to the equivalent between 0 and 31.

We have said 16 colors, must be qualified: color 0 is shared (SGB Functions> Color 0 Restriction)! This means that if the color 0 of the last palette we assign is red, the color 0 of the other graphics will change to red. And we have 16 colors just in case we want to spend the memory needed to store 4bpp graphics (4 bits per pixel) in the Game Boy. If 2bpp is enough then we have four colors per palette with the color 0 shared, but the graphics will occupy half in memory.

There are many games that accept the limitation of four colors per palette in the frame showing the color 0 to save memory. As for example it happens with the frame of Pokémon Blue.

Comparing the two red squares we see that the frame shows the color 0 shared with the rest of the palettes. Using four colors per tile (2bpp)

Our frame

This is our frame.

256 by 224 pixels frame

These our tiles.

176 tiles (including tile null one) of the 256 available

And these our palettes.

Palettes 4-6 used to represent the colors of each of the tiles

Three palettes of 16 colors (4bpp), the color 0 (first column) is not used so we don't have to share it with the palettes that we are going to use in the graphics of the Game Boy. Because of the characteristics of the development, this is the color distribution that has remained on the three palettes. Only one might as well have been used, since they have many colors in common.

And now, it's time to turn these specifications into data that we can add to our program.

Tiles file

The graphic data of the tiles will be created with Game Boy Tile Designer. In our frame some of the palettes use more than 4 colors so our graphics should be 4bpp.

If the graphics are 2bpp we simply draw the tiles with the editor taking into account that the colors we choose for the values 0, 1, 2, and 3 will correspond to the colors 0, 1, 2 and 3 of the palette that we have specified for that tile.

Left: Game Boy Tile Designer tile; center: palette specified for the tile; Right: tile display in Super Game Boy

The SNES 4bpp graphics can also be created in a simple way using the same program: drawing two consecutive Game Boy tiles for each tile of Super NES. The first for bit planes 1 and 2, and the second one for numbers 3 and 4.

We can combine two Game Boy pixels (2 bits) to create a SNES pixel (4 bits)

Although we use 4bpp graphics we should also have a .gbr file that dedicates a single Game Boy tile per SNES tile. And we will use it when elaborating the map of the frame with Game Boy Map Builder, in this way we will reference the number of the tile as it would do the SNES and not the Game Boy.

The result of the elaboration of these tilesets with Game Boy Tile Designer is the following: the tileset that will store the graphics to use in the frame (FILE DOWNLOAD) and the tileset whose only function will be to contain an index of the tiles to be mapped later (FILE DOWNLOAD). Within the project, we will place these two files in res/tiles/.

And with the following configuration, we export the 4bpp tileset to obtain the necessary data file for the project. We delete the section declaration and automatically generated tags and place the resulting file in dev/data/tiles/ as sgb_tileset.z80 (FILE DOWNLOAD).

We generate a .z80 file compatible with RGBDS

Map file

Now, with Game Boy Map Builder, we will build the frame. We must associate it with the .gbr file of the tileset with the correct indexes, the one that uses a single tile of Game Boy per tile of Super NES. Remembering the fact that the dimensions of the frame are 256 by 224 pixels, which means 32 by 28 tiles. So once we open the program we go to "File> Map properties ..." and we take care of adapting dimensions and tileset.

The tileset we use here only serves as a visual reference when it comes to mapping the tiles

As I mentioned earlier, the SNES tiles can be flipped either horizontally (X Flip) or vertically (Y Flip). Additionally each of the tiles will be associated with one of the palettes we have specified. That's why we need to define additional attributes for each tile we place on our map. We can do this in "File> Location properties ...".

We do not define the vertical flip (Y Flip) because it is not used in our frame

As we will see the flip is defined as a single bit (1 flipped, 0 not flipped) and the palette with 2 (range of 0 to 3 that corresponds respectively with the palettes from 4 to 7), these will be the 2 less significant bits of the 3 that are later assigned to the palette. The third bit, the most significant bit, will always return the value of 1. This gives us a real range of values from 4 to 7 when exporting.

Now we map the tiles of the frame, and define their location properties through the fields available in the bottom row of the editor.

We recall that we must leave a space of null tiles in the position of the Game Boy screen

As a result, we have a .gbm file with the map design that will conform the frame (FILE DOWNLOAD), we place it in res/maps/. As a point, I would like to clarify that the first thing to do when opening the map in Game Boy Map Builder is to reference the tileset of the indexes through "File> Map properties ...".

Now we are going to generate the data file relative to the map through "File> Export to ...". Let's make sure that we comply with the map format specification (SGB Functions> SGB Command 14h - PCT_TRN). In the export options we will include the location properties we defined above. This would be the official specification regarding our export configuration.

Official frame tiles format specification

In the case of the Y Flip property, we ignore it occupying its place with a 0

Once exported, we delete the section declaration and the resulting data tags and place it in dev/data/maps/ (FILE DOWNLOAD). 

We already have all the graphic files ready, only the data from the palettes is missing. To do this we will now create the file sgb.z80, which we will place it dev/data/. Here we are going to write the definitions of the packets that we will send to the SNES and in this case also the macro of the RGB of 15 bits that will be used for the colors of our palettes.


The three parameters of the macro correspond respectively to the value of red, green and blue that we want to give to each color. Well, now we have to specify palettes based on this macro.

We must place the palettes next to the map data (SGB Functions> SGB Command 14h - PCT_TRN), so we will create a file in dev/data/maps called sgborder.z80 (FILE DOWNLOAD), there we will adapt our palettes to the specification.
 
The map must be 32x32 tiles but the last 4 rows are not part of the screen and are unnecessary

  
We remember the specification of our palettes

The first color of each palette (color 0) is specified but not used

Game palettes

Finally we are going to add to sgb.z80 the declaration of the palette that we will use for the graphics of the game itself. Recall that while the 4-7 palettes are used for the frame, the 0-3 palettes are used for the graphics of the game. The Game Boy's monochrome graphics have a 4 color limitation so we can only use 4 colors in this palette.

We will only use one of the four palettes assignable to the Game Boy game

The color 0 that we define should be the one we want to share with the rest of the palettes

As we have already specified all the necessary graphic files, it is time to add them to the memory bank that we have declared in main.asm.

 

Logic programming


Auxiliary definitions

There are two ways of sending information to the Super NES (Game Boy Programming Manual> page 127), through registers P14 and P15 and through the Game Boy's own VRAM (VRAM transfer). Both involve the use of commands. So let's start by defining in the sgb.z80 file the macros of the commands we are going to use (we have a definition of what each command does in Game Boy Programming Manual> page 132 and SGB Functions> SGB Command Summary):
  1. MLT_REQ, makes a multiplayer request, it will be used to detect if the software is running on a Super Game Boy;
  2. CHR_TRN, we will use it to copy the tiles from the frame to the SNES RAM (with a VRAM transfer)
  3. PCT_TRN, we will use it to copy the frame map to the SNES RAM (with a VRAM transfer);
  4. PAL_SET, will assign the palette that we have defined for the graphics of the game;
  5. PAL_TRN, will copy the palette that we have defined for the game graphics to the SNES RAM (with a VRAM transfer);
  6. MASK_EN, will freeze the screen during communication processes with the Super Game Boy, and unfreeze it upon completion;
  7. Finally we will define eight data packets that we will send to initialize the communication as recommended in the official documentation (Game Boy Programming Manual> page 178).


And with this we finally complete the auxiliary file sgb.z80 (FILE DOWNLOAD).

As a prerequisite to logic programming, we must write a 0x03 in the 0x0146 address of our ROM to indicate that we support the functionality of Super Game Boy (Game Boy Programming Manual> page 178). We already have parameterized this address in gbhw.inc, so we modified the second parameter of the header in main.asm.


Communication implementation

Let's explain with a practical example how we have defined the macros of the packets in sgb.z80. Let's take our macro from MLT_REQ and compare it to the memory specification of the command (SGB Functions> SGB Command 11h - MLT_REQ).

As we see the Super Game Boy allows a multiplayer mode of up to 4 players with the SNES Super 5 multi-player adapter


We see how byte 1 defines the number of players requested in the Super Game Boy multiplayer mode. We parameterized this byte in our macro and specified with two labels the two variants that we will use in our program.

All commands are made up of 16 bytes (128 bits), which is the size of the data packet that we can send. So we only send one packet per command. The number of packets per transfer can be specified through the three least significant bits of byte 0 of the first packet (as we see in the specification, byte 0: Command * 8, which happens to occupy the 5 most significant bits + Length, which occupies the remaining 3 bits), so we can send from 1 to 7 packets per transfer. We will represent the possibility of sending more than one packet per command in the code, but in the resulting program we do not use this functionality.

Now we will describe the process of sending each packet as explained on page 128 of the Game Boy Programming Manual. Sending made through bits 4 and 5 of the Game Boy's P1 register (0xFF00), these bits correspond respectively to the output ports P14 and P15:
  1. we start the communication by putting to 0 P14 and P15;
  2. we send the 128 bits of the packet by putting to 0 P14 and to 1 P15 if we want to send a 0 and to 1 P14 and 0 P15 if we want to send a 1;
  3. we send a 0 in bit 129 to close the communication (P14 to 0 and P15 to 1).
The following list of rules must be taken into account:
  1. the values we write in P14 and P15 must be kept for 5μs, and between writtings we must set P14 and P15 to 1 for at least 15μs. Considering that a Game Boy clock cycle already lasts more than 23μs, we ignore the times, but note that we must write a 1 on both ports for each bit sent;
  2. between sending one packet and the next we should wait for a minimum of 60 msec (about 4 frames of Game Boy). The "sgbpackettransfer_wait" routine will take care of this wait.
With all of this in mind, this is our routine of sending packets, which we add to main.asm.


The first functionality that will use this packet sending routine is the one that recognizes whether or not we are in a Super Game Boy. We will do it the following way (which is the recommended way in SGB Functions> Detecting SGB hardware):
  1. we will read the identifier of the register P1, which in addition to serving to send information through the ports is the register that returns the information of the joypad of the Game Boy. By default, its reading returns the identifier of the joypad;
  2. we request the two player mode of Super Game Boy by sending the command packet MLT_REQ with two players as the parameter;
  3. we re-read the identifier of the register P1. If it is the same identifier, it means that the two player mode has not been selected and that therefore we are not in a SGB.
We place the following code in main.asm.


The following routine will be called "init_sgb_default", and does the sending of the initialization packets recommended by the Game Boy Programming Manual (page 178). We also add it to main.asm.


The transfer commands CHR_TRN, PCT_TRN, and PAL_TRN use the VRAM transfer to copy their data to the SNES RAM. Here are the steps we must follow to perform this type of transfer (as explained in SGB Functions> SGB VRAM Transfers):
  1. if we do not want the data to be displayed on the screen of the Game Boy we must have previously configured a mask for the LCD of the Game Boy by  making use of the command MASK_EN;
  2. we disable interrupts and turn off the LCD, to avoid that the graphics we want to send are corrupted;
  3. we assign the default values to the background palette of the Game Boy, which translates to writing 0xE4 in the palette register that is at 0xFF47 (0 white, 1 light gray, 2 dark gray, 3 black);
  4. we copy the 4KB of data that we want to send to the visible background of the Game Boy LCD, the background address begins at 0x9800;
  5. we send the transfer command, either CHR_TRN, PCT_TRN or PAL_TRN. The data displayed in the visible background of the Game Boy will be sent to the SNES.
Before showing the code of the routine, a little more information about on MASK_EN. It is usually used at the beginning of the program to hide the actual display of the Game Boy LCD during data transfer.

(SGB Functions > SGB Command 17h - MASK_EN)

There are 4 possible values for command byte 1, we use the value 1 to freeze the screen image and 0 to unfreeze it when the process is finished. As we see in the tags we have defined in sgb.z80.


And once the digression is finished, this is the data transfer routine. We will place it in main.asm.


As we can see, we have an additional routine called "parsesgbbordertiles". This routine allows us to save memory in case the data we want to copy is 2bpp, since it will fill with zeroes the bit planes 3 and 4 (which we do not use). In our case, being 4bpp tiles, we do not make use of it.

And finally, using the routines we have developed so far, this would be the description of the main initialization routine of the Super Game Boy:
  1. we check if we are in a Super Game Boy, if we are not, we return and the routine ends;
  2. we freeze the LCD display of the Game Boy throughout the process;
  3. we send the initialization packets recommended by the documentation;
  4. we copy the 256 tiles we will use in the frame to the SNES RAM. In two batches of 128;
  5. we copy the frame information to the SNES RAM;
  6. we copy the information of the game palettes to the SNES RAM;
  7. we assign the palette previously copied as a palette to be used in the game;
  8. we clear the Game Boy VRAM and unfreeze the Game Boy LCD display to complete the process.
We add this code to main.asm (FILE DOWNLOAD).


If we now execute the assemble.bat file of the project we will have the ROM with the frame and the customized palettes as a result.



I terminate the explanation of this simple prototype. Naturally, many features of the Super Game Boy remain to be applied. But I hope this article serves as a starting point for people who have been encouraged to develop for the Game Boy but have not found any minimally guided development resources for Super Game Boy.

Any note or critic of any kind will be welcome, and any part of the post is subject to change if necessary.

So I can only give encouragement. Now you have to implement that Super Game Boy frame that you have always wanted to design.