Juego estrategia - Parte 3 - Clase Recursos
En este ejercicio voy a explicar como funciona la clase Recursos y como el juego hace uso de ella para cargar las imágenes y sonidos al iniciar el juego. Este ejercicio es un anexo del ejercicio 2 de este tutorial: Parte 2 - Base del proyecto
La demo y los archivos que adjunto son exactamente los mismos. En ellos podrás encontrar el código que aquí explico.
Enlace a la DEMO aquí.
Enlace para la descarga de los archivos de este ejercicio aquí.
Estructura del proyecto
La clase Recursos está contenida en el archivo Recursos.js, que ya incluí junto el resto de ficheros javascript desde el index.html.
Recursos es una clase que espera una lista de archivos de sonido e imagen, los precarga y cuando ya están todos disponibles avisa a la clase principal. Funciona completamente de manera síncrona, que es el propósito, de manera que el juego no avance durante la carga de recursos, hasta que todos los archivos estén disponibles.
Durante el desarrollo del juego esta clase se consulta para obtener recursos almacenados en ella. Tanto las imágenes como sonidos, están disponibles para su uso inmediato en cualquier momento.
Vamos a ver como trabaja la clase Recursos()
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 |
/** * Constructor */ function Recursos(){ this.lista = []; // matríz que almacena recursos this.esperados = 0; // contador de recursos que se reciben this.cargados = 0; // contador de recursos que se van cargando } /** * Usaremos este método en la precarga del juego y se pasarán en un arreglo todos * los recursos necesarios. */ Recursos.prototype.cargar = function(lista){ var self = this; this.esperados = lista.length; for (var i=0;i<this.esperados;i++){ var id = lista[i][0]; var origen = lista[i][1]; var tipo = lista[i][2]; // se recorre la matríz de recursos y según el tipo se procesan de una // manera u otra. Tanto las imágenes como sonidos, no aumentarán el // contador de 'cargados' hasta que no se hayan cargado en memoria. switch(tipo){ case 'imagen': var img = new Image(); img.src = origen; img.onload = function(){ // básicamente se incrementa el contador de this.cargados self.recursoCargado(); }; this.lista[id] = img; // se añade el recurso a la lista break; case 'sonido': var audio = new Audio(); audio.src = origen; audio.addEventListener('loadeddata', function () { self.recursoCargado(); }, false); this.lista[id] = audio; // se añade el recurso a la lista break; } } }; /** * No se completa la carga hasta que todos los recursos están disponibles 100% * en memoria, será entonces cuando se llame a completado() */ Recursos.prototype.recursoCargado = function(){ this.cargados++; if (this.cargados == this.esperados){ this.completado(); } }; Recursos.prototype.get = function(id){ return this.lista[id]; }; /** * Este método se ejecuta finalmente cuando todos los recursos que se han pasado * a cargar() ya están disponibles. Este método está sobreescrito en Base() */ Recursos.prototype.completado = function(){}; |
La clase Recursos consta del constructor y cuatro métodos. En el constructor se declaran las propiedades necesarias para la clase; una matriz que contendrá todos los recursos y dos contadores que se usarán únicamente durante la carga de recursos al iniciar el juego.
El método cargar() recibe una lista de recursos y los añade a la lista una vez los ha cargado, no antes. Cada vez que se añade un recurso a la lista llama al método recursoCargado().
A su vez, recursoCargado(), se encarga de comprobar si ya se han procesado todos los recursos enviados en la matriz. Finalmente, cuando esto ha sucedido, llama al método completado().
El método completado() está sobreescrito en la clase principal Base, justo después de crear la instancia de la clase Recursos, veremos esto enseguida. De esta manera conseguimos fácilmente que la clase recursos avise a la clase principal de que la carga de recursos ha finalizado.
Como podemos observar la mayoría de los métodos sólo se emplean cuando se van a cargar los recursos, y esto normalmente sólo sucede una vez y es durante la precarga del juego.
El método get() lo usaremos para obtener recursos durante el desarrollo del juego. Sólo necesitaremos indicar el identificador del recurso y ya lo tendremos disponible para pintarlo en el canvas si es una imagen o para reproducirlo si es un sonido.
Veamos como desde la clase principal Base se cargan los recursos
Este método pertenece a la clase Base en el archivo Base.js
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(); }; } |
La ventana de log, que he creado justamente debajo del canvas, es realidad una consola de debug (depuración). Javascript ya incluye una, pero he creído conveniente usar una en pantalla. En la línea 3 hago uso del método log(), que tan solo imprime 'Iniciada precarga...'.
Es en la línea 6 donde se realiza la acción importante de este método: la carga síncrona de recursos.
Un juego, por muy sencillo que sea, debe tener todos los recursos disponibles antes de iniciarse (sonidos, imágenes..), de no ser así, no funcionaría correctamente. Por lo tanto, es necesario precargar los recursos antes de nada; esta fase mostraría en pantalla el típico mensaje de 'Cargando juego..', aunque yo no lo he hecho.
En la línea 6 estoy creando un nuevo objeto que va a contener todo lo relacionado con la clase Recursos(), que hemos visto hace un rato. El objeto lo almaceno en la propiedad de la clase Base() que he llamado igualmente recursos. De esta manera, tendremos siempre disponible en la clase principal del juego una referencia a este objeto recién creado.
Como he comentado, en la línea 6 asigno a la propiedad this.recursos de la clase Base(), una instancia de la clase Recursos(), esta propiedad almacenará durante todo el juego el objeto que gestiona los recursos.
En la línea 7 hago uso de esta propiedad para llamar al método cargar() , este método necesita una matríz con los recursos que va a necesitar el juego. Cada elemento de la matríz contendrá un identificador, la ruta donde se aloja físicamente y el tipo de recurso.
Como hemos visto en la clase Recursos(), el método completado(), que está sobreescrito aquí, no se dispara hasta que no se han cargado todos los recursos. Cuando se ha completado la carga, en la línea 15 se imprime un mensaje de aviso, en la línea 16 se asigna el valor TRUE a la propiedad notificaciones.preCargaAcabada de la clase Base() y en la línea 17 se avanza hasta el siguiente método: iniciarEntidades().
Hablaré largo y tendido de las Notificaciones, indispensables para el correcto funcionamiento de juegos de este tipo. En la línea 16 he creado una notificación.