CLICK HERE FOR ENGLISH VERSION
Lección 13 – El plano WINDOW
Con lo aprendido hasta ahora, podríamos situar el HUD o marcador en la zona superior de la pantalla y la acción justo debajo. Si no queremos usar sprites, podemos pintarlo en un plano. Pero hay un problema. ¿Qué ocurre si hacemos scroll sobre dicho plano?
Efectivamente, el HUD se mueve junto con el plano, al fin y al cabo el HUD se pinta en el plano, no es un ente independiente. ¿Hay alguna forma de evitar este problema?
EL PLANO WINDOW
Existe un «plano» del que no he hablado hasta ahora llamado WINDOW o plano W. Recordemos los planos del VDP:
No, no aparece, y es que el plano WINDOW no es un plano real, si no un plano virtual, una zona del plano A que no va a tener scroll ninguno.
Es muy sencillo utilizar el plano W siguiendo estos pasos:
- En primer lugar elegimos la zona para el plano W con estas funciones:
void VDP_setWindowVPos(u16 down, u16 pos); void VDP_setWindowHPos(u16 right, u16 pos);
El uso de ambas funciones lo vemos en el siguiente apartado.
- En segundo lugar tendremos que asegurarnos que nuestro código respecto al marcador pinta en el plano W. Si pintamos cualquier cosa en el plano A y lo situamos en posiciones de plano W, no se verán.
- En tercer lugar, tendremos que tener en cuenta que el plano W por defecto es transparente ya que está vacío, así que salvo que pintemos tiles en él, se verá el plano B que el que está detrás del plano W=A.
- Al terminar su uso, si es necesario, hemos de desactivar el plano W.
PRIMER PASO: FUNCIONES WINDOW
La forma de utilizar estas funciones puede parecer un poco confusa, veamos todas las posibilidades. Vamos a ver un seguir un ejemplo muy sencillo que podréis encontrar en mi github, como siempre os animo a descargarlo y seguir el código.
Utilizaremos uno de los ejemplos de Sonic anteriores. 1 sprite y 2 planos con scroll horizontal. Lo único que he modificado es que si pulsas arriba/abajo Sonic se moverá en consecuencia, para poder ver como queda su sprite con respecto al plano W.
PLANO WINDOW SUPERIOR
VDP_setWindowVPos(FALSE, pos);
El plano W va desde la fila 0 hasta la fila ‘pos’. Este es el ejemplo más habitual, el marcador queda arriba y el resto del plano A/B debajo de ‘pos’.
En este caso he creado un plano W de 2 filas (filas de tiles 0, 1) y he pintado ahí el marcador y las puntuaciones. Como se puede ver, la parte superior de las palmeras (plano A) no aparecen porque el plano W impiden que se pinten.
VDP_setWindowVPos(FALSE, 2);
Se ve dentro del plano W el plano B (el cielo) pero al ser de un solo color plano (azul oscuro), no se aprecia ningún scroll del plano B y no desentona.
PLANO WINDOW INFERIOR
VDP_setWindowVPos(TRUE, pos);
El plano W va desde la fila ‘pos’ hasta el final del plano, esto es, la fila 27 en modo H40 (320×224). Nótese como la parte superior de las palmeras aquí se ven perfectamente.
Ahora es el marcador inferior el que tapa el suelo (plano A). A diferencia del ejemplo anterior, el plano B de fondo no es un color plano, en este caso se ve el agua y se aprecia el scroll del plano B.
Es importante remarcar que los sprites son independientes y se mueven en su propio plano, así que pueden tapar el plano W, al fin y al cabo el plano W es realmente una parte del plano A, por tanto si no queremos que un sprite tape el marcador, hemos de controlarlo nosotros a mano, jugando con la prioridad o bien evitando que los sprites se sitúen encima.
PLANO WINDOW IZQUIERDO
VDP_setWindowHPos(FALSE, pos);
El plano W va desde la columna 0 hasta la columna ‘pos’. El marcador queda a la izquierda y el resto del plano A/B a la derecha de ‘pos’.
A diferencia de VDP_setWindowVPos(..), con VDP_setWindowHPos(..) la variable ‘pos’ cuenta en columnas de 16 píxels (2 tiles). Si pos=1, significa que el plano W ocupará los 16 primeros píxels (del 0 al 15), si pos=2, el plano W ocupará 32 px, … así hasta pos=20, que son las 40 columnas de ancho del modo 320px.
En este diagrama se entiende mejor, pos va desde 1 a 20:
Veamos el ejemplo de github:
Por tanto es recomendable que el ancho del HUD sea múltiplo de 16. Si no lo hacemos, la parte sin pintar será transparente y se verá el plano B. En el ejemplo he creado un HUD que tiene un ancho de 4 tiles (4 * 8 =32px), por tanto utilizaré la función
VDP_setWindowHPos(FALSE, 2);
PLANO WINDOW DERECHO
VDP_setWindowHPos(TRUE, pos);
El plano W va desde la columna ‘pos’ hasta el final. El marcador queda a la derecha y el resto del plano A/B a la izquierda de ‘pos’. Recordar que pos = columna x 2 (ver apartado anterior).
Por ejemplo:
VDP_setWindowHPos(TRUE, 18);
El plano W irá desde la columna 36 hasta la columna 39 (son 4 columnas: 36,37,38,39).
Recordar que son 40 columnas y partimos de la columna 0 para contar.
SEGUNDO PASO: PINTANDO EN EL PLANO W.
Hemos de pintar específicamente en el plano WINDOW. Éste es el código que yo usaba para pintar una fila entera del HUD en una fase sin scroll:
VDP_setMapEx(PLAN_A, Game.IU, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, Game.id_tile_final_planA), 0, 0, 0, 0, 40, 1);
Por tanto tengo que cambiar PLAN_A por PLAN_WINDOW:
VDP_setMapEx(PLAN_WINDOW, Game.IU, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, Game.id_tile_final_planA), 0, 0, 0, 0, 40, 1);
¿Recordáis el ejemplo del principio? Este es el resultado al crear un plano W superior y pintar el marcador en él:
Como se puede ver, las puntuaciones del marcador siguen teniendo scroll, hemos de modificar todas las líneas de código que sean necesarias y cambiar del plano A al plano W. En mi caso, y sabiendo que en mi código, el nivel 3 es el nivel que tiene scroll (niveles 1 y 2 no tienen):
//dónde vamos a pintar las puntuaciones if(Game.level==3) VDP_setTextPlan(PLAN_WINDOW); else VDP_setTextPlan(PLAN_B); //qué vamos a pintar intToStr(Game.score, txt_marcador1, 0); VDP_drawText(txt_marcador1, 06, 0);
Ya se no se mueven ni la puntuación ni el resto de valores numéricos del HUD, ya que se pintan en el plano W y no se aplica scroll sobre ellas. El color es mejorable. Lo solucionaré más tarde con un cambio en la paleta.
TERCER PASO: ARREGLANDO EL FONDO.
Por defecto el plano Window está vacío, por tanto su fondo será transparente y se «ve» el plano B, en este caso podemos ver las montañas. Puedo hacer dos cosas, pintar todo el plano W o lo más sencillo=lo más efectivo: pintar de negro la primera fila de tiles del plano B:
De esta forma, y corrigiendo además la paleta de colores (color 15 de la paleta):
¡Ahora es perfecto!
No obstante puede interesarnos dejarnos partes transparentes si nos conviene.
Depende de como diseñemos el HUD.
CUARTO PASO: ELIMINAR EL PLANO WINDOW.
Basta con llamar a la misma función especificando 0 filas/columnas.
Por ejemplo, si hemos creado un plano W en la zona superior, así:
VDP_setWindowVPos(FALSE, 3);
Para eliminarlo basta con hacer:
VDP_setWindowVPos(FALSE, 0);
Si hemos creado un plano W en la zona inferior, así:
VDP_setWindowVPos(TRUE, 25);
Para eliminarlo basta con hacer, de nuevo:
VDP_setWindowVPos(FALSE, 0);
Con VDP_setWindowHPos(..) se hace de un modo similar.
PRIORIDAD y SHADOW & HIGHLIGHT
La prioridad del plano W se trata como la del plano A, al fin y al cabo es el mismo plano.
Por tanto el plano W podrá tener baja o alta prioridad, pudiendo un sprite ser tapado por los tiles del plano W o bien ser el sprite el que los tape. Su prioridad no tiene porqué ser la misma que la del plano A.
Recuerda el esquema y aplica al plano W el mismo tratamiento que al plano A:
Dicho esto, podemos controlar si los sprites tapan o no el HUD.
Pero además la prioridad es importante si utilizamos Shadow & Highlight:
El texto no cambia ¿verdad? Esto es porque al pintarlo lo hemos hecho con prioridad, por tanto esos tiles tienen prioridad y se pintan normalmente. No se aprecia bien en el GIF, pero el azul alrededor de los números, toda la parte que está dentro del tile, es azul normal, y no azul oscuro=shadow.
Lo mismo sucede con el texto de ayuda en mitad de la pantalla.
Podemos cambiar esto con VDP_setTextPriority(..).
DOS PLANOS WINDOW AL MISMO TIEMPO
Podemos crear dos planos W siempre que uno sea horizontal y otro vertical. Por ejemplo podemos tener un plano W superior y otro plano W a la izquierda.
SÓLO PODEMOS TENER UN PLANO W ACTIVO HORIZONTAL Y VERTICAL.
No pueden existir dos planos W horizontales o dos planos W verticales.
Al crear uno nuevo, se desactiva el anterior.
Aunque parezca muy sofisticado, no hay mucho misterio, solamente tenemos que configurarlos según las funciones anteriores:
VDP_setWindowVPos(FALSE, 2); //plano W desde 0 a la fila '2' (3 filas: 0, 1 y 2) VDP_setWindowHPos(FALSE, 2); //plano W desde la columna 0 hasta la columna 2 (columnas 0, 1 y 2).
MÁS ALLÁ
Utilizar el plano WINDOW implica más cosas de las que pensamos.
Por un lado, hemos de tener en cuenta que se ha de reservar parte de la memoria del VDP para gestionarlo, esto es razonable si nos sobra memoria, si estamos poniendo al límite el sistema será mejor buscar una alternativa.
Éste es el peor defecto y el motivo principal por el que no es usado, los programadores prefieren usar sprites para el HUD.
El SGDK nos permite manipular las regiones de memoria donde van a estar los tiles de los planos, en este caso podemos cambiar dónde se sitúan los tiles del plano Window.
Si no sabes lo que haces no te metas en estos berenjenales… No obstante si tienes curiosidad investiga VDP_setWindowAddress(..).
Si usas un debugger, puedes ver el uso del plano W en los registros 17 y 18 del VDP. Aquí puedes verlo junto con la memoria del VDP y las zonas de memoria de los planos A/B y del plano W:
Espero que hayas disfrutado de la explicación del plano Window.
GITHUB
El código de esta lección, y de todas las demás, lo podrás encontrar en mi github:
https://github.com/danibusvlc/aventuras-en-megadrive
English Version
Lesson 13 – THE WINDOW PLANE
With all learned until now, we could place a HUD at top position, leaving action just below. If we don’t want to use sprites, we can just print HUD in a plane.
But there is a problem doing this. What happens if plane is a scroll plane?
Indeed, HUD moves together with plane, after all HUD is painted on the plane.
Is there a way to avoid this problem?
THE WINDOW PLANE
There is a «plane» we didn’t speak until now called WINDOW or plane W. Let’s remember VDP planes:
No, there isn’t, Window plane is not a real plane, just a virtual plane, it’s part of plane A with no scroll.
It’s very easy to use W plane following these steps:
- First, choose an area for W plane using these functions:
void VDP_setWindowVPos(u16 down, u16 pos); void VDP_setWindowHPos(u16 right, u16 pos);
We’ll use how-to-use in next section.
- Second, make sure our HUD code prints in W plan. Anything printed in A plane in W plane area won’t be visible.
- Third, take note W plane is transparent, because is empty by default, so unless we put some tiles on it, plane B will be seen as it’s the one behind plane W = A.
- When finishing, just remember to disable W plane.
FIRST STEP: WINDOW FUNCTIONS
These functions are a bit confusing, let’s explore all the possibilities. We will follow a very simple example, hosted in my github. As always I encourage you to download and follow the code.
We are using a previous Sonic example (scroll examples). I added Sonic movement up/down to play with sprite and see what happens (Sprite vs W plan).
TOP WINDOW PLANE
VDP_setWindowVPos(FALSE, pos);
W plane goes from row 0 to row ‘pos’. Probably is the most common use for window plane, W plane is just at top part and below there is A/B plane (just below ‘pos’ row).
In this exampe W plane has 2 rows (rows 0 and 1), then I printed there HUD and scores. As you can see, top part of palm trees (A plane) are not visible, as W plane just avoid A plan tiles to exist in this area.
VDP_setWindowVPos(FALSE, 2);
But also you can see through W plan, blue sky (B plan) is visible. Luckily, it has a uniform color, so B plan scroll can’t be appreciate and the no problem at all.
DOWN WINDOW PLANE
VDP_setWindowVPos(TRUE, pos);
W plane goes from row ‘pos’ until the last row, that is, row 27 in H40 mode (320×224). Now palm trees are all visible.
But now floor is not visible, W plane avoids this. Unlike previous example, we can see water as B plan there is not a uniform color, so we can see B plan scrolling. This is a problem to be solved in next sections.
Very important. Sprites are moving in its plane, so they can cover HUD. To avoid this we need to play with priority of avoid them to be in this area manually.
LEFT WINDOW PLANE
VDP_setWindowHPos(FALSE, pos);
Window Plane goes from column 0 to column ‘pos’. HUD is on the left part, the rest is A/B plane .
Unlike last examples, that are using VDP_setWindowVPos(..), now with VDP_setWindowHPos(..) ‘pos’ variable counts in 16pixel-column. So, if pos=1, means that W plane area will be 16 pixels wide (from pixel 0 to pixel 15), if pos=2, W plane area will be 32 pixels wide…. until pos=20, that is, all 40 columns (in H40 mode).
Better to see that explain it:
Let’s see github example:
So I recommend make a HUD wide 16/32/48/64… just multiple of 16. If not, simply not used space will be transparent, but with no plane A/B tiles. In my example, I made HUD with 4 tiles wide (4*8=32 pixels). so I need to use:
VDP_setWindowHPos(FALSE, 2);
RIGHT WINDOW PLANE
VDP_setWindowHPos(TRUE, pos);
Window Plane goes from column ‘pos’ until the last column. HUD is on the right part, the rest is A/B plane . Please remember pos = column x 2 (check last section).
In example:
VDP_setWindowHPos(TRUE, 18);
W plane goes from column 36 until last one, 39 ( total 4 columns: 36,37,38,39).
Remember in H40 mode, there are 40 columns, first one is 0.
SECOND STEP: PAINTING W PLANE.
We have to paint specifically in W plane. This was my code before using W plane, in a non-scroll level, just using first row for HUD:
VDP_setMapEx(PLAN_A, Game.IU, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, Game.id_tile_final_planA), 0, 0, 0, 0, 40, 1);
So I have to change PLAN_A with PLAN_WINDOW:
VDP_setMapEx(PLAN_WINDOW, Game.IU, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, Game.id_tile_final_planA), 0, 0, 0, 0, 40, 1);
Do you remember first GIF at the beginning? After change VDP_setMapEx(..):
HUD not moving now. But score numbers still moving. So I have to change more parts of my code. In this example, only level 3 is the one with scroll (level 1 and 2 not):
//Where to put text if(Game.level==3) VDP_setTextPlan(PLAN_WINDOW); else VDP_setTextPlan(PLAN_B); //what to print in TV intToStr(Game.score, txt_marcador1, 0); VDP_drawText(txt_marcador1, 06, 0);
Better now! Score no longer moves. I will change color in next steps, just changing palette.
THIRD STEP: FIXING PLANE B.
By default, W plane is empty, so we can see tiles of B plane. In this case (see just down), mountains. Two solutions, just full the HUD (paint all W plane) or paint with black color 1 row of plane B. This is simple = best.:
This way, and fixing palette problem for score (last color of palette, 15):
Perfect!
This is only necessary if our HUD need this design, so it depends of our HUD.
LAST STEP: DISABLE WINDOW PLANE.
Just call same function you use to create with 0 rows/columns.
In example, we made a top W plane with these parameters:
VDP_setWindowVPos(FALSE, 3);
To disable it we will use:
VDP_setWindowVPos(FALSE, 0);
In example, we made a down W plane with these parameters:
VDP_setWindowVPos(TRUE, 25);
To disable it we will use:
VDP_setWindowVPos(FALSE, 0);
Similar steps with VDP_setWindowHPos(..).
PRIORITY and SHADOW & HIGHLIGHT
Window plane priority will be treated like plane A one. After all, it’s same plane.
So W plane could be with priority or not. Sprites could be covered by plane W tiles and viceversa. Take note W plane priority can be different from A plane one.
Remember VDP scheme, plane W works like plane A:
Then we can use priority to avoid sprites to cover HUD. More than this, remember priority matters if we activate Shadow & Highlight:
Text color is not changing, isn’t it? We are paiting text with priority on, so its tiles are paiting with normal colors. Same with text in the middle of screen. You can change this with VDP_setTextPriority(..).
TWO WINDOW PLANES AT SAME TIME
We can create two Window planes as long as one is horizontal and the other vertical.
ONLY ONE W PLANE HORIZONTAL / VERTICAL at same time.
Can’t exist two horizontal planes or two vertical planes.
When creating second one, first one is disabled.
Nothing extraordinary, no mistery, only do a proper configuration using W functions:
VDP_setWindowVPos(FALSE, 2); VDP_setWindowHPos(FALSE, 2);
Then:
BEYOND
Using Window Plane is more complicated than expected behind the curtain.
Part of VDP memory has to be arranged to manage W planes, this is ok if memory is not a problem, but if system is on the limit, better to look for an alternative.
This is the worst flaw and the main reason why developers use to use sprites for the HUD instead use Window Plane.
SGDK allow us to change memory map but it’s not a good practice if you don’t know exactly what are you doing, that is, change were scroll and window plane store its tiles, to optimize memory space. If you have curiosity, then check VDP_setWindowAddress(..).
When using a debugger, you can check W plane VPD #17 and #18 register.
This is an example where you can find A/B/W Plane in VDP memory and register:
Here ends basic use for Window Plane, hope you enjoyed it.
GITHUB
The code for this lesson, and all other ones, can be find in my github: