Este site no usa ni cookies de analítica ni de marketing. Cumple con la GDPR y con ePrivacity. OK

Juego estrategia - Parte 2 - Base del proyecto

En este ejercicio voy a enseñar la arquitectura que empleo para desarrollar mini juegos en el canvas 2d de Html5.

Lo que haré en este y futuros ejercicios, es además de tratar de explicar el funcionamiento, acompañarlo con la demo y la posibilidad de descargar los archivos del ejercicio.


Enlace a la DEMO aquí.

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


Estructura del proyecto

Index.html es el archivo de entrada a la aplicación y es donde se va a definir el canvas, donde correrá el juego. Para la ejecución de esta aplicación no va a ser necesario ni servidor web ni bases de datos. Todo funcionará perfectamente en el navegador web de manera local, ni siquiera la necesidad de conexión a Internet.

Analizando index.html

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Juego Estrategia - Parte 2 - Base del proyecto</title>       
        <script type="text/javascript" src="js/Base.js"></script>
        <script type="text/javascript" src="js/Recursos.js"></script>
        <script type="text/javascript" src="js/Entidad.js"></script>
        <script type="text/javascript" src="js/EntidadJugador.js"></script>   
        <style>
            body{
                margin:0;
                font-family: verdana,arial;
                font-size:14px;
                text-align:center;
            }
            canvas{
                background:#C2C2C2;
            }
            #log{
                height:200px;
                background: #EEE;
                font-size:9px;
                margin:auto;
                text-align: left;
                overflow:auto;
            }
            #log p{
                margin:0;
            }            
            #log p span{
                font-weight:bold;
            }
            #log p b{
                color:blue;
            }            
        </style>
        <script>                                  
            var fpAnimationFrame;
            
            window.onload = function(){                              
                var getRequestAnimationFrame = function () {
                    return window.requestAnimationFrame ||
                        window.webkitRequestAnimationFrame ||
                        window.mozRequestAnimationFrame ||
                        window.oRequestAnimationFrame ||
                        window.msRequestAnimationFrame ||
                        function ( callback ){
                             window.setTimeout(callback , 1 / 60 * 1000);
                        };
                };            
                fpAnimationFrame = getRequestAnimationFrame();
                new Base();
            }                  
        </script>
    </head>
    <body>
        <canvas id="canvas"></canvas>   
        <div id="log"></div>
        <button id="parar">Detener loop</button>
        <button id="clear">Limpiar log</button>
    </body>
</html>

Es una estructura de documento de HTML básica, con un poco de CSS, JavaScript y con tres elementos de HTML (canvas, button, div). En el script incrustado en el HEAD es donde se inicia la lógica del juego. Una vez se ha cargado completamente la página (window.onload) se crea una instancia de la clase Base contenida en Base.js y entra en acción requestAnimationFrame.

Canvas

Canvas traducido al español es lienzo, es por lo tanto un marco de trabajo donde podemos pintar:
http://www.w3schools.com/html/html5_canvas.asp

Canvas dispone de varios métodos para dibujar formas, añadir textos e incrustar imágenes. Canvas trabaja en un contexto de 2 dimensiones, por lo tanto dos ejes (x,y), aunque existen técnicas para simular vistas en 3D, como la proyección isométrica.

¿Qué hace requestAnimationFrame?

¿Cómo podemos crear un juego si sólo disponemos de un lienzo para pintar?
Lo que haremos es modificar el lienzo continuamente crendo un bucle y en cada vuelta seguiremos una lógica parecida a esta: mover - borrar - dibujar.
Quizás ahora mismo el concepto más difícil de entender es el de mover. Veamos las siguientes instrucciones:

  • Vuelta 1
  • Cambia posición a x=11, y=10
  • Borra todo el canvas
  • Pinta la nueva posición del circulo
  • Vuelta 2
  • Cambia posición a x=12, y=10
  • Borra todo el canvas
  • Pinta la nueva posición del circulo
  • Vuelta 3
  • Cambia posición a x=13, y=10
  • Borra todo el canvas
  • Pinta la nueva posición del circulo

Aunque parezca aleatoria la lógica aquí aplicada, tiene su sentido y explicaré más adelante. Está directamente relacionada con la detección de colisiones.

El bucle lo podemos generar con setInterval:
http://www.w3schools.com/jsref/met_win_setinterval.asp

O mejor aún, con requestAnimationFrame que presenta algunas mejoras:
https://developer.mozilla.org/es/docs/Web/API/Window/requestAnimationFrame

Es en el archivo index.html donde asigno requestAnimationFrame a la variable fpAnimationFrame, que veremos como se inicia en Base.js.

Vamos a ver ahora el siguiente archivo implicado: Base.js. En index.html hago uso de la clase que contiene este archivo, llamada con el mismo nombre: Base. El resto de archivos insertados en index.html son dependientes de Base.js, pues los veremos cuando entren en acción.

Base.js, motor del juego

En la primera parte se define la clase con su respectivo constructor. No voy añadir explicaciones para esta parte ya que creo que queda bastante claro con los comentarios que ya incorpora.

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
 * Constructor
 */
function Base(){    
           
    // referencia al lienzo de dibujo
    this.canvas = document.getElementById('canvas');
    
    // contexto del canvas    
    this.contexto = this.canvas.getContext('2d'); 
    
    // ventana log
    this.logwin = document.getElementById('log');
    this.logwin.style.width = window.innerWidth - 500 + "px";
    
    // la anchura del canvas la determino mediante la anchura de la ventana del
    // navegador menos 500 píxeles
    this.canvas.width = window.innerWidth - 500;
    // la altura de la ventana interna del navegador menos 40 píxeles
    this.canvas.height = window.innerHeight - 300;   
    
    // centramos el canvas verticalmente. La hoja de estilo ya lo centra horizontalmente
    // body{text-align:center;}
    //var marginTop = (window.innerHeight - this.canvas.height)/2;    
    this.canvas.style.marginTop = "40px";
    
    // refencia al objeto para usar en contextos especiales        
    var self = this;
    
    // boton detener juego    
    this.boton = document.getElementById('parar');
    this.boton.onclick = function(){
        self.stopLoop();
    };    
        
    // boton limpiar log
    this.botonClear = document.getElementById('clear');
    this.botonClear.onclick = function(){
        self.clearLog();
    };        
        
    // no mostrar el menú contextual al hacer clic con el botón secundario del mouse
    this.canvas.oncontextmenu = function (e) {
        e.preventDefault();
    };          
        
    // detección de eventos de teclado y creación de notificaciones
    document.onkeydown = function(e){
        self.pulsarTecla(e);
    };
    document.onkeyup = function(e){
        self.soltarTecla(e);
    };                       
        
    // detección de eventos del ratón en el canvas y creación de notificaciones
    this.canvas.addEventListener("mousedown", function (e) {
	self.eventoRaton(e,'down');
    }, true);
    this.canvas.addEventListener("mouseup", function (e) {
	self.eventoRaton(e,'up');
    }, true);    
    this.canvas.addEventListener("mouseout", function (e) {        
	self.eventoRaton(e,'mouseout');
    }, true);
    this.canvas.addEventListener("mousemove", function (e) {        
	self.eventoRaton(e,'move');
    }, true);    
    
    this.preCarga();
}

Después de crear unas cuantas propiedades con referencias al canvas, los botones, ventana de log, de iniciar algunos escuchadores de eventos para el ratón y teclado, el constructor llama al método preCarga().

Todo lo que sucede en preCarga(), se puede ver en la tercera parte de este tutorial:
Parte 3 - Clase recursos

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    preCarga : function(){
        
        this.log('Base.js',(new Error).lineNumber, 'Iniciada precarga...');
        
        //carga inicial de los recursos
        this.recursos = new Recursos();
        this.recursos.cargar(
            [
                // identificador, origen recurso, tipo (imagen|sonido)
                ["person","img/person.png","imagen"],
                ["net","audio/net.mp3","sonido"]
            ]);
        var self = this;
        this.recursos.completado = function(){
            self.log('Base.js',(new Error).lineNumber, 'Precarga finalizada');
            self.notificaciones.preCargaAcabada = true;
            self.iniciarEntidades();
        };        
    },

En preCarga(), se cargan los recursos necesarios (imágenes y sonidos) y se llama a iniciarEntidades().

Todo lo que sucede en iniciarEntidades(), se puede ver en la cuarta parte de este tutorial:
Parte 4 - Entidades

Resumen...

Accedemos al juego iniciando un archivo HTML básico, index.html. Este archivo incluye todo el código javascript necesario, añade el canvas, una ventana de depuración y botones. Se declara la función encargada de realizar el bucle, pero aún no se inicia.

Se instancia a la clase Base y vemos que lo primero que hace esta clase es crear algunas propiedades que contienen la referencia al canvas y el contexto, declarar escuchadores de eventos (Event Listener) e inmediatamente cargar los recursos del juego (imágenes y vídeos), no continuará hasta que todo esté disponible.

Cuando ya todo está disponible, se inician las entidades del juego, que veremos ahora.
Por el momento, seguimos en la fase de 'preparación' del juego, todavía no han entrado en escena los actores principales...