Quando realizziamo un gioco in Javascript ci troviamo spesso a dover far uso di un gran numero di immagini da caricare prima che il game loop possa iniziare.
Se i files da caricare sono molti l'utente si trova di fronte a una schermata ferma senza avere un'idea di quanto tempo dovrà attendere prima di poter giocare, e come sappiamo un'attesa troppo lunga di solito determina l'abbandono della pagina.
In questo tutorial viene realizzata una semplice funzione che permette di caricare in automatico le immagini in uso nel gioco e renderizza una progress bar al centro dello schermo richiamando il gameloop alla fine del caricamento.
La funzione che carica le immagini è il cuore della soluzione:
function loadImages(names, callback) { var n, name, perc, result = {}, count = names.length, onload = function() { count --; if (count == 0) { perc = 100; } else { perc = ((100/names.length)*(names.length-count)).toFixed(2); } callback(perc, result); }; for(n = 0 ; n < names.length ; n++) { name = names[n]; result[name] = new Image(); result[name].addEventListener('load', onload); result[name].src = '/img/' + name + 'jpg'; } }La funzione accetta in ingresso un array di nomi di immagini 'names' e la funzione di callback che verrà richiamanta per dare al programma info sullo stato del caricamento nonchè il puntatore alle risorse di tipo immagine.
Questa è la parte di invocazione del loader:
// Nel filesystem esisteranno i files: ./img/space0.jpg ecc.. var files = ['space0', 'space1', 'space2', 'space3', 'space4', 'space5', 'space6', 'space7', 'space8']; loadImages(files, loadDone);Ad ogni nuova immagine caricata in memoria verrà invocato 'loadDone' che aggiornerà lo stato di caricamento:
// Percentuale delle immagini caricate var loadedImg = 0; // Array di oggetti di tipo immagine var images; function loadDone(perc, result) { loadedImg = perc; if (loadedImg == 100) images = result; }Per disegnare le immagini sarà sufficiente accedere tramite l'array 'images' in questo modo:
buffer_context.drawImage(images.space0 , 0 , 0); // oppure buffer_context.drawImage(images['space0'] , 0 , 0);Il resto del codice si occupa di disegnare le schermate di loading oppure del gioco nel caso in cui il caricamento delle immagini raggiunga il 100%.
function draw_game() { buffer_context.clearRect(0 , 0, canvas.width, canvas.height); switch(SCREEN) { case 'playing': { drawImages(); break; } case 'loading': { drawLoading(); break; } } contesto.clearRect(0, 0, canvas.width, canvas.height); contesto.drawImage(buffer, 0, 0); } function drawImages() { for (var i=0; i<=8; i++) buffer_context.drawImage(images['space' + i] , i * 40, i * 40); } function drawLoading() { var w = (400 / 100) * loadedImg; var posx = (canvas.width / 2) - 200; var posy = (canvas.height / 2) - 20; buffer_context.fillText('Images loaded: ' + loadedImg + ' %', posx, posy - 20); buffer_context.strokeRect(posx, posy, 400, 20); buffer_context.fillRect(posx, posy, w, 20); if (loadedImg == 100) SCREEN = 'playing'; } function loadDone(perc, result) { loadedImg = perc; if (loadedImg == 100) images = result; } function gameLoop() { draw_game(); requestAnimationFrame(gameLoop); } // Dichiarazione variabili globali var canvas = document.getElementById("canvas1"); var contesto = canvas.getContext("2d"); var buffer = document.createElement('canvas'); buffer.width = canvas.width; buffer.height = canvas.height; var buffer_context = buffer.getContext('2d'); buffer_context.fillStyle="#efe"; buffer_context.strokeStyle="#0f0"; buffer_context.font = "18px Verdana"; gameLoop();