Vistas dentro de otra vista.GM:S

Este tutorial responde a la duda planteada por el makero "eltitobarte" de comunidad GM. Él solicita ayuda para tener una imagen que sirva como marco para una vista. Más que un marco, lo requerido es tener una ventana de juego, y dentro de la ventana una vista que ocupe parcialmente la ventana, y el área restante sea una especie de fondo o interfaz (textura). Bueno, el propio eltitobarte proporciona la siguiente imagen para explicar el efecto deseado, misma que me permito reproducir para que la idea resulte clara.

Vistas dentro de vista

Desde hace tiempo tenía curiosidad por hacer algo similar: dividir la ventana en varias zonas donde cada una muestre algo distinto, más nunca me había decidido a hacerlo (y tampoco tenía idea en ese entonces). Al ver la imagen que posteó nuestro compañero, lo primero que me dije fue "ésto se ve como dos vistas dentro de una tercera", y ese fue el enfoque que emplee para implementar el proyecto. La manera en que GM:S nos permite manipular las vistas es bastante conveniente en este caso. Sin más preámbulo, así definí mi plan:

Tener tres vistas, como se ve en la imagen anterior: Vista 0 para el lugar donde ocurre el juego, Vista 1 para la zona pequeña de la izquierda, en este caso podría ser una especie de HUD o información similar. Una última vista: Vista 2, para mostrar el marco o interfaz . A grandes rasgos, el proyecto consta de:

  • Una habitación de 640X480
  • Un fondo para la habitación, un sprite para el jugador, un sprite para el hud y un sprite para el marco
  • El objeto jugador, que dibuja en la vista 0 tanto el fondo de la habitación como a si mismo (sprite del jugador)
  • Un objeto controlador, que dibuja el HUD en la vista 1 y el marco en la vista 2

El proyecto requiere poco código, menos de lo que pensé al comienzo. Considero importante mencionar que en cada paso, el evento DRAW se llama una vez por cada vista que se tenga activa en el juego, es decir, si hay dos vistas, las cosas se dibujan dos veces, una por cada vista. GM:S (y GM8) disponen de la view_current, que nos permite consultar en qué vista se dibuja actualmente. view_current es una variable de solo lectura y únicamente se puede usar en el evento DRAW. Usaremos esta variable para especificar qué cosa se debe dibujar en qué vista.

Lo primero que haremos será configurar las vistas en la habitación yo usé las siguientes medidas para crear el ejemplo:

Dimensiones
Vista 1

Para la vista 1 (HUD) usé un ancho de 200 y una altura de 140, para la vista 2, 640X480. Sólo la vista cero está configurada para seguir al objeto jugador. Usé eventos del teclado para que el objeto jugador responda a las cuatro teclas de dirección y nada más, en [STEP] sólo uso código para evitar que el jugador salga de la habitación. No usé evento [CREATE]. Ahora sí, lo importante, evento [DRAW] del jugador:

if (view_current == 0) {
draw_background(bkg1, 0, 0);
draw_self();
}

view_surface_id[0] = sv1        //'mandar' lo dibujado en vista 1 a la superficie sv1

Se deibuja el fondo de la habitación y el sprite del jugador en la vista 0. La variable view_surface_id[] nos permite asignar una superficie (sv1) a una de las siete vistas disponibles, con la particularidad de que esa vista no se dibuja en pantalla, por lo que más adelante debemos dibujarla de manera explícita. La superficie sv1 se crea en el objeto controlador. Pasemos entonces al evento [CREATE] del objeto controlador:

globalvar sv1, sv2, sv3;

sv1 = surface_create(350, 350)       //tamaño de la vista 0
sv2 = surface_create(200, 140)       //tamaño de la vista 1 (sprite hud)
sv3 = surface_create(room_width, room_height)        //tamaño de la vista 2 (marco)

surface_set_target(sv3)
draw_sprite(spr_textura, 0, 0, 0)        //Dibujar el marco en la superficie sv3
surface_reset_target()

Aquí creo tres superficies (una por cada vista) y dibujo la textura o marco (es un sprite común y corriente) en la superficie sv3. Luego viene el Evento [DRAW] del obj controlador:

//dibujar HUD
if (view_current == 1)
      draw_sprite(spr_hud, 0, 0, 0)

view_surface_id[1] = sv2          //'mandar' la vista 1 a la superficie sv1

view_surface_id[3] = sv3          //'mandar' la vista 3 a la superficie sv3

Aquí hay algo raro, que no atino a descifrar. No sé si es un bug o algo que se escapa a mi entendimiento. Recuerden que activamos las vistas 0, 1 y 2 en la habitación. Pues si configuré la vista 2 al tamaño de la habitación (640X480, mismo tamaño que el sprite del marco), se me hace lógico indicar view_surface_id[2], ¡pero cuando uso un 2, la textura no me aparece dibujada en pantalla! Lo que no alcanzo a comprender es por qué motivo, al poner un número mayor a 2, ya sea 3, 4, 5, etc... la textura se dibuja correctamente, aunque en la habitación no tenga configurada (activa) ninguna de esas vistas. Si a ti te ocurre distinto o no tienes este problema, por favor avísame.

Ya casi hemos terminado, así que continuemos. Como se mencionó antes, al asignar una superficie a una vista, esa vista no se dibuja automáticamente en pantalla. Hay dos maneras de dibujar esa vista "deshabilitada": en otra vista o en el evento Draw GUI que es independiente de las vistas. Por comodidad decidí usar DRAW GUI (los ejemplos que he visto en la comunidad oficial GMC usan este método). Este es el código de dicho evento:

//Dibujar superficie sv3, correspondiente a la vista 3 (marco)
if surface_exists(sv3)
      draw_surface(sv3, 0, 0);

//Dibujar superficie sv1, correspondiente a la vista 0 (jugador y fondo)
if surface_exists(sv1)
      draw_surface(sv1, 250, 50)

//Dibujar superficie sv1, correspondiente a la vista 1 (HUD)
if surface_exists(sv2)
      draw_surface(sv2, 30, 50)

Y aquí termina el código necesario. Puedes descargar el proyecto yendo al siguiente link:

Proyecto Multivistas

Gif animado


Cualquier duda o sugerencia, contáctame ¡Hasta la próxima estupidez artificial!

Penumbra.