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

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

Dans ce jour 12, on va gérer des sprites d’animation et en profiter pour réorganiser la gestion du son de manière plus propre.

Ajout d’une méthode animate
On va ajouter une méthode animate qui permettra à la fois de modifier l’image affichée mais également de jouer le bon son

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
animate:function(action){
    //on commence par effacer la zone
    oLayer_perso.clearRect(((this.x-currentX)*widthCase),((this.y-currentY)*heightCase),widthCase-2,widthCase-2);

    var tmpImg;
    if(action=='attack'){
        tmpImg=this.idImg+'_attack';
        this.playSound('attack',this.action);
    }else if(action=='walking'){
        //si le précédent sprite était A
        //on prend le B (pour créer une animation)
        if(this.tmpIdImg==this.idImg+'_walking2'){
            tmpImg=this.idImg+'_walking';
        }else{
            tmpImg=this.idImg+'_walking2';
        }
        //on arrête tout son (on marche)
        this.stopSound();
        //on stoque le sprite affiché pour la prochaine animation
        this.tmpIdImg=tmpImg;
    }else if(action=='dead'){
        this.playSound('dead',this.action);
        return;
    }else if(action=='wood'){
        tmpImg=this.idImg;
        this.playSound('wood',this.action);
    }else if(action=='mining'){
        tmpImg=this.idImg;
        this.playSound('mining',this.action);
    }else if(action=='stand'){
        tmpImg=this.idImg;
    }
   
    oImages.drawImageOnLayer(tmpImg,((this.x-currentX)*widthCase),((this.y-currentY)*heightCase),widthCase-2,widthCase-2,'perso');
   
    this.action=action;
},

Pour recentrer le son sur l’unité, on a du recréé des méthodes de gestion de son que voici:
Note: on stoque la précédente animation afin lorsqu’une animation/action se déroule en plusieurs temps, on lise le son en entier plutot que de saccader celui-ci.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
playSound:function(action,lastAction){
    //si la précédente action était la même, on fait rien
    if(action==lastAction){
        return;
    }
    this.stopSound();
   
    this.oAudio=new Audio();
    this.oAudio.src=oSound.getSrc(action);
    this.oAudio.play();
   
},
stopSound:function(){
    if(!this.oAudio){
        return;
    }
    this.oAudio.pause();
},

Appelez au bon moment cette nouvelle méthode
Après avoir supprimer les précédentes méthodes appellées oSound.resetPlaying() et oSound.stopPlaying()
On doit désormais appelé les bonnes méthodes d’animation pour avoir un état cohérent de chaque unité et éviter de jouer un son inutile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(iAttack){    

    oUnit.animate('attack');

    //on decremente l'enemie de la puissance d'attaque
    oUnit2.life-=oUnit.attak;
    if(oUnit2.life <=0){
        oUnit.animate('walking');
        oUnit2.animate('dead');      
        //si unite dead, on l'efface du jeu
        oUnit2.clear();
        oGame.removeUnit(oUnit2);

    }

Le github
Le projet GitHub : https://github.com/imikado/rtshtml5

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

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

Dans ce jour 11, on va ajouter un peu de son sur ce jeux qui est bien muet pour l’instant
On ne pas va pas encore mettre l’ambiance, juste 2-3 son histoire d’ajouter un peu de vie

On va ajouté un son lorsqu’une unité coupe du bois, travaille dans la mine, construit un batiment se bat ou meure.
J’ai trouvé ces divers sons sur ce site: http://www.universal-soundbank.com/

Le son en html5
Tout d’abord comment jouer un son en HTML5, comme il y a une classe Image pour créer un objet image, il y a une classe Audio pour crée un objet son.
Vous pouvez ensuite interagir avec ce son: le lire, le mettre en pause…

Nous créons ici dans une classe Sound

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
function Sound(){
   
    this.counter=0;
    this.total=0;
    this.tSrc=Array();
    this.tOAudio=Array();
    this.tPlaying=Array();
}
Sound.prototype={
    resetPlaying:function(){
    this.tPlaying=Array();
    },
    add:function(src,id){
        this.tSrc[this.total]=Array();
        this.tSrc[this.total]['src']=src;
        this.tSrc[this.total]['id']=id;
       
        this.total++;
    },
    load:function(){

        for(var i=0;i< this.total;i++){
        var id=this.tSrc[i]['id'];

        this.tOAudio[id]=new Audio();
        this.tOAudio[id].src=this.tSrc[i]['src'];
        this.tOAudio[id].onload=function(){
           
        };
        this.tOAudio[id].load();
        oSound.counter++;
            preload2();

        }      
    },
    playSound:function(sType){
    console.log("play sound "+sType);
    this.tPlaying[sType]=1;
       //on joue le son
    this.tOAudio[sType].play();
    },
    stopSound:function(sType){
    console.log("stop sound "+sType);
   

    //on remet le son au début
    this.tOAudio[sType].pause();
    this.tOAudio[sType].currentTime=0;
   
    this.tPlaying[sType]=0;
    },
    stopPlaying:function(){
    //return;
    for(var i=0;i< this.total;i++){
        var id=this.tSrc[i]['id'];
        if( !this.tPlaying[ id ] && id!='building'){
        this.stopSound(id);
        }
       
    }
   
    }
   
};

Ajoutons un son de coupe du bois
Il y a deux choses à faire: démarrer le son au début de la coupe du bois avec oGame.playSound(‘wood’);
Et l’arretez une fois le travail terminé avec oGame.stopSound(‘wood’);

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
}else if(aBuild && aBuild.name=='wood' && oUnit.counter = 8 && oUnit.cycleToX!=''){
    //on indique à l'unité qu'elle transporte 10
    oUnit.wood=10;
   
    //a chaque iteration on decremente la ressource
    aBuild.ressource-=10;
    //si l'arbre est épuisé, on le supprime de la carte
    if(aBuild.ressource<=0){
        aBuild.clear();
        oGame.removeBuild(aBuild);
       
        //on définit un nouvel arbre à prendre en compte
        var tWood=[
            [-1,-1],
            [0,-1],
            [1,-1],
           
            [-1,0],
            [1,0],
           
            [-1,+1],
            [0,+1],
            [1,+1],
        ];
        for(var i=0;i<tWood.length;i++){
            var oBuild2=this.getBuild(aBuild.x+tWood[i][0],aBuild.y+tWood[i][1]);
            if(oBuild2 && oBuild2.name=='wood'){
                oUnit.cycleToX=oBuild2.x;
                oUnit.cycleToY=oBuild2.y;
               
                break;
            }
        }
    }
   
    //on remet le compteur à 0
    oUnit.counter=0;
   
    //on redéfinit la nouvelle cible (c'est un cycle)
    oUnit.setTarget(oUnit.cycleFromX,oUnit.cycleFromY);
                       
    oSound.stopSound('wood');

//si la cible c'est une mine d'or et que le compteur est inferieur à N
}

Faisons de même pour la mine d'or

Ajoutons un son pour le combat
Ici il y a deux sons ici à mettre en place: celui du combat et celui de la mort d’une unité.
Pour le combat à chaque fois qu’on est en position d’attaque (à proximité d’une unité enemie)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if(iAttack){
    oSound.playSound('attack');
                   
    //on decremente l'enemie de la puissance d'attaque
    oUnit2.life-=oUnit.attak;
    if(oUnit2.life <=0){
        //on joue le son d'attaque
        oSound.stopSound('attack');
                           
        //si unite dead, on l'efface du jeu
        oUnit2.clear();
        oGame.removeUnit(oUnit2);
       
        //on joue le son de mort de l'unite
        oSound.playSound('dead');
                               
    }

Dans le cas présent, si l'unité ne finit pas son combat, le son ne sera pas arrêté.
Il faudrait au minimum mettre à zéro en début de boucle de rafraichissement d'unité et stopper en fin de boucle tous les sons sauf ceux en cours
Pour cela on créé une méthode: oSound.resetPlaying();

1
2
3
resetPlaying:function(){
    this.tPlaying=Array();
},

Et en fin de boucle:
oSound.stopPlaying();

1
2
3
4
5
6
7
8
9
10
stopPlaying:function(){
    for(var i=0;i< this.total;i++){
        var id=this.tSrc[i]['id'];
        if( !this.tPlaying[ id ] && id!='building'){
        this.stopSound(id);
        }
       
    }

}

On a ainsi un jeu un peu plus vivant.

Le github
Le projet GitHub : https://github.com/imikado/rtshtml5

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

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

Dans ce jour 10, j’ai corrigé pas mal de bug notamment concernant le problème de suppression d’un arbre ou d’une unité
En effet, ces derniers étaient bien supprimés de la variable tCoordBuild/tCoordUnit mais pas du tableau tBuild/tUnit
J’ai également changé le comportement de base des unités: seule les unités enemies cherchent à se rapprocher pour attaquer: c’est d’ailleurs mieux car les ouviers n’ont pas la même capacité d’attaque que les soldats enemies qui font une ronde.

On va ici permettre à une unité qui coupe du bois, une fois que l’arbre n’existe plus d’aller en chercher un autre à proximité

note: pour rappel, le projet est toujours disponible sur github, dans les billets sur developpez je détaille des parties que je considère interressante, mais je ne détaille pas forcément certains bugfix ou réorganisation du code (pour le rendre plus propre)
N’hésitez pas à suivre le projet github (lien en bas du billet)

Trouver un autre arbre à proximité
On va dès l’instant que l’arbre est supprimé d’en trouver un autre et de mettre à jour la ronde de l’unité

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
}else if(aBuild && aBuild.name=='wood' && oUnit.counter >= 8 && oUnit.cycleToX!=''){
    //on indique à l'unité qu'elle transporte 10
    oUnit.wood=10;
   
    //a chaque iteration on decremente la ressource
    aBuild.ressource-=10;
    //si l'arbre est épuisé, on le supprime de la carte
    if(aBuild.ressource<=0){
        aBuild.clear();
        oGame.removeBuild(aBuild);
       
        //on définit un nouvel arbre à prendre en compte
        var tWood=[
            [-1,-1],
            [0,-1],
            [1,-1],
           
            [-1,0],
            [1,0],
           
            [-1,+1],
            [0,+1],
            [1,+1],
        ];
        for(var i=0;i<tWood.length;i++){
            var oBuild2=this.getBuild(aBuild.x+tWood[i][0],aBuild.y+tWood[i][1]);
            if(oBuild2 && oBuild2.name=='wood'){
                oUnit.cycleToX=oBuild2.x;
                oUnit.cycleToY=oBuild2.y;
               
                break;
            }
        }
    }
   
    //on remet le compteur à 0
    oUnit.counter=0;
   
    //on redéfinit la nouvelle cible (c'est un cycle)
    oUnit.setTarget(oUnit.cycleFromX,oUnit.cycleFromY);

Arretez son cycle bois si plus d’arbre
On a vu précédement que dans le cas où l’on a terminé de couper l’arbre il cherche à proximité pour changer d’arbre.
Nous rajoutons maintenant le cas où il n’en trouve pas pour cesser son cycle bois.

1
2
3
4
5
6
7
8
9
10
11
12
13
}else if(oUnit.cycleFromX!='' && (oUnit.targetX==oUnit.x || oUnit.targetY==oUnit.y) ){
   
    //si arrivee a destination et cycle
    if(oUnit.cycleObject=='wood'){
        //si il arrive a venir sur les coordonnées cible
        //c'est que l'arbre n'y est plus
        oUnit.clearCycle();
    }else if(oUnit.cycleFromX==oUnit.x && oUnit.cycleFromY==oUnit.y ){
        oUnit.setTarget(oUnit.cycleToX,oUnit.cycleToY);
    }else{
        oUnit.setTarget(oUnit.cycleFromX,oUnit.cycleFromY);
    }
}

Donner un prix aux unités comme pour les batiments
Jusqu’à présent on pouvait créer autant d’unité que l’on voulait, désormais chaque unité à un prix, comme pour les batiments
D’abord dans la classe Unit, on ajoute un cout dans le constructeur.

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
if(this.name=='Soldier'){
    this.shortname='Soldat';
    this.src='img3/unit-soldier.png';
    this.idImg='unit-soldier';
    this.life=150;
    this.attak=20;
   
    this.costOr=200;
   
}else if(this.name=='Archer'){
    this.shortname='Archer';
    this.src='img3/WC.png';
    this.idImg='unit-archer';
    this.life=100;
    this.attak=30; 
   
    this.costOr=500;
}else if(this.name='Worker'){
    this.shortname='Ouvrier';
    this.src='img3/unit-worker.png';
    this.idImg='unit-worker';
    this.life=50;
    this.attak=5;
   
    this.costOr=100;
   
    this.tBuildCreation.push(new Build('SoldierHouse',this.team));
    this.tBuildCreation.push(new Build('ArcherHouse',this.team));
}

Ensuite dasn la fonction de création de navigation de la classe Build, on ajoute le cout et on indique que si la ressource n’est pas suffisante, on ne peut plus créer d’unité.

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
buildNav:function(){
    var sHtml='';
   
    sHtml+='<h1>'+this.shortname+'</h1>';
    sHtml+='<p><img src="'+this.src+'"></p>';

   
    if(this.unitCreation){
       
        var sImg='';
   
        sHtml+='<h2>Cr&eacute;ation unit</h2>';
       
        sHtml+='<table><tr>';
       
        sHtml+='<td>';
       
        if(oGame.iOr &gt; this.unitCreation.costOr){

       
            sImg='';
            sColor='#fff';
           
        }else{
           
            sImg='';
           
            sColor='#333';
        }
       
        sHtml+='<td style="background:#444;color:'+sColor+';padding:4px 8px">';
                       
        sHtml+=sImg;
   
        sHtml+='<br />';
        sHtml+='<span style="border:1px solid gray;background:yellow">&nbsp;&nbsp;&nbsp;&nbsp;</span> ';
        sHtml+=this.unitCreation.costOr;
       
           
        sHtml+='</td>';
       
        sHtml+='</tr></table>';
       
    }

    getById('nav').innerHTML=sHtml;
},

rts_12janv

Le github
Le projet GitHub : https://github.com/imikado/rtshtml5