Journal de bord: création d’un bomberman-like en HTML5, jour 1

bomber1

Introduction
Lors du précédent journal de bord, j’ai développé un jeu de stratégie temps réel ou RTS multiplayer en HTML5.
J’ai beaucoup appris de ces 17 jours, et j’espère que vous aussi.
Aujourd’hui commence un nouveau journal de bord pour developper un jeu bomberman-like multiplayer également en HTML5.
L’avantage, c’est qu’on ne part pas de zéro, on a déjà une base de structure Map/unité/cycle/serveur multiplayer…
note: comme pour le RTS, ce jeu est disponible sur github, je mets des extraites de code pour exemple mais les classes entières sont disponibles sur le dépôt.

Les bases du jeu
Voici la liste des fichiers du jeu:

  • bombermanM.html
  • bombermanM.js
  • bombermanM_Bomb.js
  • bombermanM_Game.js
  • bombermanM_Map.js
  • bombermanM_Perso.js
  • bombermanM_Sound.js

+ Fichier serveur coté node.js

  • serverBomberM.js

bombermanM.html
Comme pour le RTS: une page html qui charge les différents fichiers javascripts du jeu.
Il y a toujours deux div utilisés pour indiquer le chargement et l’autre pour permettre de choisir sa team.
A une différence: il y a moins de canvas (calques) à gerer: ici layer_map, layer_bomb et layer_perso.

bombermanM.js
Script principal du jeu qui contient également la classe Images permettant de charger et d’identifier les sprites.
Une fonction de preload qui charge le sprite, puis instancie les canvas, le jeu et construit la map.
Ce script contient également la méthode appelé en continue: la fonction run()

bombermanM_Map.js
Cette classe est quasiment la même que celle du RTS à une différence près: il n’y a pas ici de gestion d’aperçu.
Cette classe est constitué d’un constructeur et de 2 méthodes build() et drawImage()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Map.prototype={
    build:function(){
        for(var y=0;y< maxY;y++){
            for(var x=0;x< maxX;x++){
                if(this.tMap[y] && this.tMap[y][x]){
                    //on dessine sur le canvas la valeur du tableau
                    this.drawImage( this.tMap[y][x] ,x,y);
                }
            }  
        }
    },
    //la methode pour dessiner sur le canvas
    drawImage:function(iImg,x,y){
        console.log(this.tImg[iImg]);
        oImages.drawImageOnLayer(this.tImg[iImg],x*widthCase,y*heightCase,widthCase,widthCase,'map');
    },
   
};

bombermanM_Game.js
Cette classe ressemble également beaucoup à celle du RTS, il y a dans le constructeur la partie écoute du socket,

1
2
3
4
5
6
7
8
9
10
11
12
13
socket=io.connect('http://localhost:1338');
   
socket.on('Game.createPerso',function(id, team,name,x,y){
    var oPerso=new Perso(name,team);
    oPerso.x=x;
    oPerso.y=y;
    oPerso.id=id;
    oPerso.build();
   
    console.log('creation perso team:'+team+' x:'+x+' y:'+y);
   
    oGame.tPerso.push(oPerso);
});

puis plusieurs méthodes du jeu comme la récupération d’un personnage,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
getPersoById:function(id){
    for(var i=0;i< this.tPerso.length;i++){
        if(this.tPerso[i].id==id){
            return this.tPerso[i];
        }
    }
},
getPersoByTeam:function(team){
    for(var i=0;i< this.tPerso.length;i++){
        if(this.tPerso[i].team==team){
            return this.tPerso[i];
        }
    }
},

d'une bombe par son id ou ses coordonnées,

1
2
3
4
5
6
7
getBombById:function(id){
    for(var i=0;i< this.tBomb.length;i++){
        if(this.tBomb[i].id==id){
            return this.tBomb[i];
        }
    }
},

La création d'un bombe sur la map

1
2
3
4
createBombBroadcast:function(team,name,x,y){
    console.log('socket create bomb'+team+' '+name+' x:'+x+' y:'+y);
    socket.emit('Game.createBombBroadcast',team,name,x,y);
},

La boucle d'affichage des joueurs:

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
refreshPerso:function(){
       
    //on boucle sur les persos existants
    for(var i=0;i< this.tPerso.length;i++){
        var oPerso= this.tPerso[i];
        if(oPerso.life <=0){ continue;}
       
            var vitesse=0.5;
           
            if(!this.tDirection[oPerso.team]){
                continue;
            }
           
            var sDirection=this.tDirection[oPerso.team];
           
            //on efface le dessin sur le calques
            oPerso.clear();
           
            //on initialise les nouvelles coordonnées
            var newX=oPerso.x;
            var newY=oPerso.y;
           
            //on fait evoluer les coordonnées en fonction de la direction
            if(sDirection=='right'){
                newX+=vitesse;
            }else if(sDirection=='left'){
                newX-=vitesse;
            }
            if(sDirection=='up'){
                newY-=vitesse;
            }else if(sDirection=='down'){
                newY+=vitesse;
            }
           
            if(this.checkCoord(newX,newY)){
                //si les coordonnées est libre
                oPerso.x=newX;
                oPerso.y=newY;
               
            }
           
            //on dessine le personnage
            oPerso.buildBroadcast('walking');
           
       
    }
   
   
},

le choix d'une team

1
2
3
4
5
6
7
8
9
10
11
12
setTeam:function(team){
    this.team=team;
           
    if(team=='blue'){
        setTimeout(run,fps);
    }
   
    getById('team').style.display='none';
           
    map.build();
    this.refresh();
},

Et une chose nouvelle: la prise en compte des touches du clavier:

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
eventKeyDown:function(e){
    var touche = e.keyCode;

    this.resetKeys();
    if(touche==37){
        this.setTeamDirectionBroadcast('left');
    }
    if(touche==38){
        this.setTeamDirectionBroadcast('up');
    }
    if(touche==39){
        this.setTeamDirectionBroadcast('right');
    }
    if(touche==40){
        this.setTeamDirectionBroadcast('down');
    }
   
    if(touche==32){ //espace
        console.log('depot bombe');
        //boucle perso pour savoir ou creer la bombe
        var oPerso=this.getPersoByTeam(this.team);
           
        this.createBombBroadcast(oPerso.team,'normal',oPerso.getX(),oPerso.getY());
           
    }
},

Qui est appelé avec un onkeydown dans la balise body

1
onkeydown="oGame.eventKeyDown(event)"

Parenthèse sur la gestion d’evenement
Dans le RTS, chaque joueur utilisait sa souris pour sélectionner une unité, un batiment puis cliquait sur le jeu pour intéragir.
Par exemple quand il cliquait gauche sur la map, on donnait l’ordre à l’unité d’aller à un endroit, et l’on broadcastait cette cible.
Mais ici on ne joue pas à la souris mais au clavier, ici on ne va pas communiquer des coordonnées cible ou autre, on va broadcaster la direction voulue pour le personnage.
Ainsi tant que l’on maintient une touche de direction, le personnage continue sa course sans accout.

Le dépôt github
Le dépôt https://github.com/imikado/bomberhtml5

Quelques images
bomber1
bomber2