Cómo pausar el juego. GM:S

Este tutorial muestra cómo realizar un sistema de pausa en Game Maker:Studio. En el foro de CGM varios he visto varios hilos de usuarios solicitando ayuda para implementar la pausa en su juego. Uno de los "inconvenientes" mencionados es que en GM:S se han vuelto obsoletas las funciones background_create_from_screen() y sprite_create_from_screen(), mismas que era común usar en GM 8.1 y anteriores para crear una captura en pantalla justo antes de pausar el juego, la captura se mostraba mientras la pausa estuviera activa, dando la impresión precísamente de que el juego se había pausado. Ahora que a partir de GM:S 1.3 es posible manejar la application surface como una superficie más, podemos implementar la misma funcionalidad de manera sencilla.

El método descrito se basa en la desactivación de instancias. Al desactivar las instancias en GM, sus eventos así como su dibujo se suspende, lo que es similar a entrar en modo pausa, con el detalle de que si todo se desactiva, nada se dibuja en pantalla, y por supuesto esto no es lo que queremos. Por lo tanto, la solución pasa por dejar AL MENOS UN objeto activo al desactivar las instancias. El objeto que queda activo (puede ser un objeto controlador u otro objeto) toma el control de la pausa, y se puede usar para crear un menú o símplemente para terminar la pausa y volver al juego (reactivando las instancias).

En este proyecto, además de la habitación principal donde está la instancia del jugador, hay otra habitación que se usa como menú de opciones cuando el juego ha sido pausado. Esta manera de hacer un menú de opciones es distinta a mi idea de implementar uno (sin ir a otra habitación), pero el "esqueleto" de proyecto me fue enviado por un usuario tal como el lo diseñó, yo he respetado los objetos existentes y me he limitado a agregar el código de pausa, por lo que me enfoco en un solo objeto.

El proyecto consta de una habitación en donde hay varias instancias del objeto jugador que se mueven hacia arriba y abajo. En la parte derecha de la habitación hay un objeto reloj que al inicio realiza una cuenta regresiva de 20 segundos, tras la cual se genera otro objeto que muestra un aviso en pantalla con la leyenda "Game Over" (es sólo una leyenda, el juego no finaliza). En la parte superior de la pantalla se encuentra el objeto obj_boton_pausa que se encarga de pausar el juego.

Pantalla del juego

Para el evento [CREATE] del obj_boton_pausa se ha dispuesto lo siguiente:

pausa = 0
pantallazo = 0
depth = -1000

//Posicionar el botón de pausa
x = 256
y = 0

choice = 0          // Al estar pausado el juego, 0 = volver; 1 = ir a menu de opciones
pila_inst = ds_stack_create()

La variable pausa sirve como bandera para indicar si el juego está o no pausado. pantallazo es la variable donde guardamos el indice del sprite que contiene la captura de pantalla. Posicionamos el botón de pausa en la parte superior de la habitacupin. Al entrar en la pausa, se presentan dos opciones: Regresar al juego (0) o ir al menú de opciones (1), es decir, a la otra habitación. pila_inst es una ds_stack en donde se almacenan las instancias a desactivar

A continuación tenemos el script pausar_juego() que es donde realmente ocurre el proceso de pausa (este script se llama en un evento de alarma, como se verá después).

pantallazo = sprite_create_from_surface(application_surface, 0, 0, room_width, room_height, false, false, 0,0)
var stack = ds_stack_create()
with (all)
{
    ds_stack_push(stack,id);

}
ds_stack_copy(pila_inst, stack}
instance_deactivate_all(true);

En primer lugar se crea una captura de pantalla. La application surface es una superficie en donde GM:S dibuja automáticamente todo, por lo que sólo tenemos que tomar esa superficie y convertirla en un sprite para tener nuestra captura de pantalla, así de simple. creamos una variable local para manejar una estructura ds_stack. ¿Por que usar una variable local si ya declaramos una pila en CREATE? La variable stack es local al script, por lo que es posible usarla incluso dentro de la construcción with, si usamos la variable declarada en CREATE, tendríamos que usar other. al llamarla, para evitar un error por variable desconocida. Lo que hacemos con la construcción with es que por cada instancia activa en el juego, tomamos su id y lo guardamos en la pila. El uso de una pila no es estrictamente necesario, pero es muy conveniente: de esta manera sabemos qué instancias se deben reactivar al volver al juego. Por ejemplo, puede darse el caso de que antes de pausar el juego, existan instancias que ya se encuentran desactivadas, por lo que al regresar de una pausa (mediante instance_reactivate_all() ) se activarán todas las instancias, incluyendo las que estaban desactivadas antes de la pausa.

El juego entra en pausa cuando se hace click sobre el botón de pausa, por lo tanto vamos al código del evento [MOUSE LEFT PRESSED]:

//Si el juego no está pausado, y se presiona el botón de pausa...
if (pausa == false) {
    alarm[0] = 1                   //PAUSAR
}

//Si el juego ya está pausado....
if (pausa == true) {
if choice = 0
{
     pausa = 0
     while (ds_stack_size(pila_inst) > 0)            //recorrer la pila de objetos desactivados
     {
        instance_activate_object( ds_stack_pop(pila_inst) );        //reactivar el objeto
     }
}

    if choice = 1                    // ir al menu de opciones
    {
        room_goto(room_menu)
    }

}

Cuando el juego no está pausado, y se hace click en el botón de pausa, se activa la alarma (un solo paso) que llama al script de pausa. Si el juego ya se encuentra pausado (recordemos que el objeto obj_boton_pausa se mantiene activo siempre) puede haber dos opciones: que el usuario haga click en regresar al uegose (choice = 0) o que elija ir al menú (habitación) de opciones (choice = 1). Los valores para choice se calculan en el evento [STEP], y dependen de la posición del puntero del mouse. Por comodidad en esta ocasión usaré una imagen para presentar el código del evento [STEP]:

Código en STEP

El código de [STEP] cambia la apariencia del botón de pausa: Si no hay pausa, es un botón de pausa ubicado en la parte superior de la habitación. Si el juego se pausa, es un botón con dos opciones (regresar e ir a menú de opciones) y además está ubicado en el centro (más o menos) de la pantalla. En la pausa, si el puntero se ubica en la mitad izquierda del botón, se considera que la opción a elegir es regresar (choice = 0); si por el contrario, el puntero del ratón se ubica en la mitad derecha del botón, entonces al hacer click se iría a la habitación o menú de opciones (choice = 1)

No hay que olvidar que el script de pausa se ejecuta en el evento de alarma[0], en donde:

pausa = true
pausar_juego()

El último bloque de código corresponde al evento [DRAW] del objeto obj_boton_pausa. En este evento se dibuja el sprite del botón así como la captura de pantalla (pantallazo) cuando el juego está pausado:

if (pausa)
     draw_sprite(pantallazo, 0, 0, 0)
draw_self()

Así termina este tutorial, el cual espero que resulte útil. Quizás parezca algo enredado a primera vista, pero en verdad es sencillo. El código para pausar consiste en sólo 6 líneas; lo que más codigo se lleva es codificar el comportamiento de menús y botones. Si ayuda a aclarar dudas, el ejemplo queda disponible en el link más abajo. Por ahora termina esta Estupidez Artificial, pero ya vendrá la próxima...porque siempre hay una próxima estupidez Artificial... ¡hasta entonces!

Ejemplo de pausa

Penumbra.