Journal de bord: création d’un RTS en HTML5, jour 8

Introduction
Ce billet fait suite au billet: http://blog.developpez.com/ducodeetdulibre/p12416/developpement/journal-de-bord-creation-dun-rts-en-html5-jour-7

Ajourd’hui nous allons passer aux sprites
L’idée est d’éviter d’avoir une image par éléments, mais 2-3 images qui contiendrait l’ensemble afin d’avoir peu de fichiers à télécharger
On va créer 2 images: une qui contiendra les images 1×1 (map, unités, arbres…) cases et l’autre les images 2×2 (batiment, mine d’or…)

gimp-sprites2x2
rts_sprite

Ensuite on va créer une classe qui permettra dans un premier temps de charger les 2 images, et dans un second temps d’identifier chaque élément du sprite

Création de la classe de gestion de sprites

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
function Images(){
    this.tOImg=new Array();
    this.tDetail=new Array();
    this.counter=0;
}
Images.prototype={
    //methode qui permet de charger une image
    load:function(src,idImg){
        this.tOImg[idImg]=new Image();
        this.tOImg[idImg].src=src;
        this.tOImg[idImg].onload=function(){
            oImages.counter++;
            preload2();
        }
    },
    //methode qui permet d'identifier un élément du sprite
    setDetailOnId:function(id,y,x,width,height,idImg){
        this.tDetail[id]=new Array();
        this.tDetail[id]['x']=x;
        this.tDetail[id]['y']=y;
        this.tDetail[id]['width']=width;
        this.tDetail[id]['height']=height;
        this.tDetail[id]['idImg']=idImg;
    },
    //methode qui permet de dessiner un element sur un des canvas
    drawImageOnLayer:function(id,x,y,width,height,sLayer){
        var oCanvasTmp;
        if(sLayer=='map'){
            oCanvasTmp=oLayer_map;
        }else if(sLayer=='apercu'){
            oCanvasTmp=oLayer_apercu;
        }else if(sLayer=='perso'){
            oCanvasTmp=oLayer_perso;
        }else if(sLayer=='building'){
            oCanvasTmp=oLayer_building;
        }
       
        oCanvasTmp.drawImage2(this.tOImg[ this.tDetail[id]['idImg'] ],this.tDetail[id]['x'],this.tDetail[id]['y'],this.tDetail[id]['width'],this.tDetail[id]['height'],x,y,width,height);
       
    },
};

Chargement des sprites et identification
Il faut ensuite identifier chaque éléments:
On créé un tableau qui représente virtuellement les éléments sur le sprite
En bouclant dessus, on identifie dans la classe Images ceux-ci

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
oImages=new Images();
   
var tDetailTmp=new Array();
tDetailTmp=[
    ['case-beige2','case-water','case-beige','case-wood'],
    ['unit-worker'],
    ['unit-soldier'],
    ['unit-archer'],
];
for(var y=0;y<tDetailTmp.length;y++){
    for(var x=0;x<tDetailTmp[y].length;x++){
        oImages.setDetailOnId(tDetailTmp[y][x],y*40,x*40,40,40,'1x1');
    }
}
var tDetailTmp=new Array();
tDetailTmp=[
    ['build-SoldierHouse','build-SoldierHouse_-2','build-SoldierHouse_-1'],
    ['build-QG','build-QG_-2','build-QG_-1'],
    ['build-ArcherHouse','build-ArcherHouse_-2','build-ArcherHouse_-1'],
   
    ['build-mineOr'],
];
for(var y=0;y<tDetailTmp.length;y++){
    for(var x=0;x<tDetailTmp[y].length;x++){
        oImages.setDetailOnId(tDetailTmp[y][x],y*80,x*80,80,80,'2x2');
    }
}

oImages.load('img3/sprite1x1.png','1x1');
oImages.load('img3/sprite2x2.png','2x2');

Dessin d’une image issu du sprite
Pour dessiner, il suffit d’appeler l’objet ainsi.
Exemple pour la classe Build: on dessine l’id « this.idImg sur le canvas « building »

1
oImages.drawImageOnLayer(this.idImg+this.sSprite,(this.x-currentX)*widthCase,(this.y-currentY)*heightCase,widthCase*2,widthCase*2,'building');

Animation de construction d’un batiment
Pour feter l’arrivé des sprites j’ai ajouté un cycle de construction pour les batiments:
Dans la classe Game, ajout de la méthode:

1
2
3
4
5
6
7
8
9
refreshBuild:function(){
    for(var i=0;i< this.tBuild.length;i++){
        var oBuild= this.tBuild[i];
        if(oBuild.level  3){
    oGame.refreshBuild();
    iRefreshBuild=0;
}

iRefreshBuild++;

note: je gère un compteur pour que le batiment ne se construise pas trop vite.

Optimisation du code
En passant au sprite, il a y a eu une autre optimisation de faite: comme on ne charge que 2 images en début de script, avec un mécanimes d’attente de chargement, on n’a plus besoin de se demander/ de coder de méthode onload asynchrone
Avant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
build:function(){
    if(this.oImage==''){
        this.oImage=new Image(this);
        this.oImage.src=this.src;
        this.oImage._x=this.x;
        this.oImage._y=this.y;
        this.oImage.onload=this.drawImage;
    }else{
        oLayer_building.drawImage(this.oImage ,(this.x-currentX)*widthCase,(this.y-currentY)*heightCase,widthCase*2,widthCase*2);
    }
    (...)
},
drawImage:function(){
    oLayer_building.drawImage(this ,(this._x-currentX)*widthCase,(this._y-currentY)*heightCase,widthCase*2,widthCase*2);
},

Après:

1
2
build:function(){
    oImages.drawImageOnLayer(this.idImg+this.sSprite,(this.x-currentX)*widthCase,(this.y-currentY)*heightCase,widthCase*2,widthCase*2,'building');

Plus besoin de gérer deux modes: si l’image n’existe pas , avec gestion asynchrone via onload + si l’image existe
On affiche simplement l’image via l’objet oImages (via les sprites)
Le github
Le projet GitHub : https://github.com/imikado/rtshtml5