Dedichiamo un po' di attenzione allo scenario e vediamo come renderlo più vario utilizzando una tilemap: una texture unica, composta da piccole porzioni dette "tiles" (mattonelle, piastrelle) che vengono assemblate per creare l'ambiente risparmiando risorse grafiche.

Per l'occasione, abbiamo creato un level editor in HTML5, che ci consente di generare un livello sfruttando questa texture (disponibile anche su GitHub). Il funzionamento è piuttosto semplice e una volta creato il livello, sarà possibile esportarlo premendo su save
.
Per comodità, utilizzeremo qualche livello già pronto, che potete trovare nel file levels.js del repository in allegato.
html5gametutorial-master\js\levels.js
Caricare le tiles
Prima di tutto, carichiamo l'immagine delle tiles:
this.imgTiles = rh.LoadSprite("img/tiles.png",1);
in ResetLevel inizializziamo l'array delle tiles
this.tiles = [];
In LoadLevel, prima della creazione del Player aggiungiamo il seguente codice:
// carica le info sul livello
var settings = levels[lev][0];
this.cellSize = settings[0];
this.spacing = settings[1];
this.areaW = settings[2]*this.cellSize;
this.areaH = settings[3]*this.cellSize;
In questo modo, otteniamo le informazioni sul livello che stiamo caricando.
Ricaviamo quindi le informazioni sui tiles, suddivisi in diversi Layer, e inseriamole nell'array this.tiles
:
// dati sui tiles
var tiles = levels[lev][1];
var cs = this.cellSize + this.spacing;
var cellsX = Math.ceil(this.imgTiles.width / cs);
var cellsY = Math.ceil(this.imgTiles.height / cs);
// tilelist
for(var j = 0; j < tiles.length; j++) {
var layer = tiles[j];
this.tiles.push([]);
for(var i = 0; i < layer.length; i++) {
// coordinate del tile relative alla texture
var cy = Math.floor(layer[i][0] / cellsX);
var cx = layer[i][0] - cy*cellsX;
// aggiunge il tile
this.tiles[j].push([layer[i][1]*this.cellSize, layer[i][2]*this.cellSize, cx*cs, cy*cs]);
}
}
Fatto ciò, istanziamo i blocchi di collisione.
// resetta l'array dei blocchi
this.blocks = [];
// dati sui blocchi di collisione
var blocks = levels[lev][2];
//creiamo le varie istanze di Block
for(var i = 0; i < blocks.length; i++) {
this.blocks.push(new Block(blocks[i][0]*this.cellSize, blocks[i][1]*this.cellSize));
}
Disegnare la tilemap
Nell'evento draw dell'oggetto Game, appena sopra il codice per disegnare il Player, aggiungiamo:
// salva le impostazioni context
this.ctx.save();
// trasla il context per mostrare gli oggetti in view
this.ctx.translate(-this.viewX,-this.viewY);
var cs = this.cellSize;
// disegna le tiles se sono dentro la view
var vx1 = this.viewX - this.cellSize;
var vy1 = this.viewY - this.cellSize;
var vx2 = this.viewX + this.canvas.width;
var vy2 = this.viewY + this.canvas.height;
// renderizza tutti i layer delle tiles
for(var i = 0; i < this.tiles.length; i ++) {
var layer = this.tiles[i];
for(var j = 0; j < layer.length; j ++) {
if(layer[j][0] > vx1 && layer[j][1] > vy1 &&
layer[j][0] < vx2 && layer[j][1] < vy2)
this.ctx.drawImage(this.imgTiles,
layer[j][2], layaer[j][3],
cs, cs,
layer[j][0], layer[j][1],
cs, cs);
}
}
// ripistina il context per eliminare la traslazione
this.ctx.restore();
Se avviamo, otterremo il seguente risultato:
