Usamos cookies propias y de terceros para mostrar publicidad. Si continua navegando consideramos que acepta el uso de cookies. OK Más información

Juego estrategia - Parte 5 - Notificaciones

Publicado por Israel Noguera

Este ejercicio es un anexo del ejercicio parte 2, por lo que comparte exactamente los mismos archivos.


Enlace a la DEMO aquí.

Enlace para la descarga de los archivos de este ejercicio aquí.


¿Qué es una notificación?

Las notificaciones es un concepto, no es un tipo de variable, ni un tipo de objeto de javascript, ni nada por el estilo. Igual que he usado el termino notificación, podría haber usado cualquier otro, como alertas, avisos, etc.

Veamos cómo defino las notificaciones:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Base.prototype = {
        ...
        //notificaciones varias
        notificacionJugadorFoco : false,
        gameRunning : false,
        preCargaAcabada : false,
        partidaDetenida : false,  
        //eventos teclado
        espacioPulsado : false,
        enterPulsado : false,                
        izquierdoPulsado : false,
        derechoPulsado : false,
        arribaPulsado : false,
        abajoPulsado : false, 
        //eventos ratón
        botonIzquierdoRatonPulsado : false,
        botonDerechoRatonPulsado : false,        
        get : function(atributo){
            return this[atributo];
        },
        set : function(atributo, valor){
            this[atributo] = valor;
        }        
    },
    ...
}

He creado una propiedad con el nombre 'notificaciones' y contiene un objeto literal. Este objeto literal tiene definidas todas las notificaciones. La propiedad 'notificaciones', en lugar de un objeto literal podría contener una matriz, pero si queremos almacenar información localizable mediante nombre, por ejemplo: notificaciones.derechoPulsado, la mejor opción es un objeto, además en cuanto a eficiencia, el objeto literal es el más rápido.

Seguramente habrás observado que he agrupado los métodos y algunas propiedades más dentro de otro objeto asignado a la clase Base: Base.prototype = { ... } Bien, pues la única razón por la cual lo hice así, fue para tener el código más compactado, por ninguna otra razón más.. si esto aporta alguna ventaja o desventaja no lo sé.

Las notificaciones son imprescindibles para notificar al motor del juego (Base.js) la detección de eventos del ratón o del teclado, ya que en un juego que sigue un orden específico dentro de un intervalo de tiempo, no se puede realizar acciones inmediatas sin seguir ese orden y el tiempo establecido en cada bucle (tiempo Delta que ya veremos).

Por ejemplo: si pulsamos la barra espaciadora para disparar, no podemos ejecutar la función dispara() y lanzar un proyectil por la buenas. Hay que notificarle al juego que se ha lanzado un proyectil y este en su debido momento lo lanzará. Como en un segundo se producen entre 20 y 60 ciclos por segundo, no va a haber demora alguna entre la pulsación de la tecla y el lanzamiento. De esta manera, el lanzamiento del proyectil va a seguir el procedimiento adecuado, como veremos en un momento.

Yo también me pregunté ¿bueno, y porqué no puedo mover un personaje a la derecha en el momento de pulsar el cursor derecho? El cursor derecho, como cualquier otra tecla, puede desencadenar tres eventos: onKeyPress, onKeyDown y onKeyUp. Lo normal sería detectar el evento onKeyPress, que informa en todo momento de que la tecla se está pulsando y ante este evento le decimos al jugador que se mueva a la derecha. El problema es que este evento lanza un montón de eventos continuados que no guardan absolutamente ninguna relación con el tiempo de ejecución del juego y en la práctica nos encontraríamos con un movimiento totalmente fuera de control.

La manera correcta es detectar cuando se lanza el evento onKeyDown y activar una notificación, esta notificación podría ser notificaciones.derechoPulsado = true. Si esta notificación está activada, pues se procede a mover al jugador hasta el momento que se suelta la tecla, donde se lanzaría el evento onKeyUp que pondría de nuevo notificaciones.derechoPulsado = false.

Vamos a ver como sería más o menos una simulación de loop:

Vuelta 1:
derechoPulsado = false;
SI derechoPulsado ENTONCES moverJugador();
borrarCanvas();
pintarCanvas();

Vuelta 2:
derechoPulsado = false;
SI derechoPulsado ENTONCES moverJugador();
borrarCanvas();
->Detectado evento onKeyDown para tecla cursor derecho: derechoPulsado = true;
pintarCanvas();

Vuelta 3:
derechoPulsado = true;
SI derechoPulsado ENTONCES moverJugador();
borrarCanvas();
pintarCanvas();

Vuelta 3:
derechoPulsado = true;
SI derechoPulsado ENTONCES moverJugador();
borrarCanvas();
pintarCanvas();
->Detectado evento onKeyUp para tecla cursor derecho: derechoPulsado = false;

Vuelta 4:
derechoPulsado = false;
SI derechoPulsado ENTONCES moverJugador();
borrarCanvas();
pintarCanvas();

En este pequeño ejemplo, podemos observar como un evento se puede lanzar en cualquier momento del loop, pero no realizaremos ninguna acción salvo la de cambiar el estado de la notificación pertinente. Es en la siguiente vuelta del loop, cuando se decidirá que hacer con esa notificación.

He adaptado ligeramente el código de la clase Base, para ver como actúa una notificación en concreto, veamos el código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
pulsarTecla : function(e){  
    e.preventDefault();//Anulamos las acciones por defecto de la tecla

    if(e.keyCode==13){ //Enter           
        // ejemplo de como usar las notificaciones.
        if(!this.notificaciones.enterPulsado){
            this.notificaciones.enterPulsado = true;
            this.log('Base.js',(new Error).lineNumber, 'Enter pulsado');
        }
    }else if(e.keyCode==37){ //Cursor izquierdo
        this.notificaciones.izquierdoPulsado = true;   
        this.log('Base.js',(new Error).lineNumber, 'Cursor izquierdo pulsado');
    }else if (e.keyCode==39){ //Cursor derecho
        this.notificaciones.derechoPulsado = true;
        this.log('Base.js',(new Error).lineNumber, 'Cursor derecho pulsado');
    }else if (e.keyCode==38){ //Cursor arriba
        this.notificaciones.arribaPulsado = true;
        this.log('Base.js',(new Error).lineNumber, 'Cursor arriba pulsado');
    }else if (e.keyCode==40){ //Cursor abajo
        this.notificaciones.abajoPulsado = true;
        this.log('Base.js',(new Error).lineNumber, 'Cursor abajo pulsado');
    }else if (e.keyCode==32){ //espacio
        this.notificaciones.espacioPulsado = true;
        this.log('Base.js',(new Error).lineNumber, 'Espacio pulsado');
    }
},
...

Este es uno de los métodos de la clase Base y entra en acción mediante el escuchador de eventos definido en el constructor:

1
2
3
4
5
6
...        
// detección de eventos de teclado y creación de notificaciones
document.onkeydown = function(e){
    self.pulsarTecla(e);
};                       
...  

Podemos observar como he manipulado el código para la pulsación de la tecla 'Enter'. Por defecto la notificación notificaciones.enterPulsado es siempre 'false'. Si pulsamos esa tecla, la notificación cambiará de estado para definirse como 'true'. ¿qué pasará en el siguiente loop? pues como estará en true, no se ejecutará el condicional; no se asignará de nuevo 'true' a la notificación ni se imprimirá el mensaje del log. ¿cuándo pasaría de nuevo la notificación a el estado 'false'?, pues al soltar la tecla:

Código del constructor:

1
2
3
4
5
...        
document.onkeyup = function(e){
    self.soltarTecla(e);
};                       
... 

Método:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
...
soltarTecla : function(e){        
    e.preventDefault();//Anulamos las acciones por defecto de la tecla
    if(e.keyCode==13){ //ENTER
        this.notificaciones.enterPulsado = false;
        this.log('Base.js',(new Error).lineNumber, 'Enter soltado');
    }else if(e.keyCode==37){ //Cursor izquierdo
        this.notificaciones.izquierdoPulsado = false;
        this.log('Base.js',(new Error).lineNumber, 'Cursor izquierdo soltado');
    }else if (e.keyCode==39){ //Cursor derecho
        ...
},  
...

En la demo que adjunto lo podemos ver en acción. Hacemos clic en el canvas para que tenga el foco y pulsamos la barra espaciadora sin soltarla, veremos que se imprime un montón de veces el mensaje. En cambio, si pulsamos la tecla Enter, podemos ver como sólo si imprime el mensaje de pulsar y el de soltar una sola vez.

Bueno, si esto resulta un poco confuso, se entenderá mucho mejor viendo el uso real dentro del juego.

Comentarios 0 comentarios