Mis à jour le jeudi 31 août 2017
  • 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Finalisez le jeu

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous y voilà : la dernière ligne droite ! Il nous reste quelques petites tâches à réaliser avant de considérer le jeu comme terminé ! Certes, ce sont des petites choses, mais elles restent importantes !

Premièrement, nous allons afficher l'arme du joueur ainsi que ses munitions sur la fenêtre en bas à droite. Puis nous ajouterons un système de messages affichés à l'écran pour annoncer les morts, les multi-kills ainsi que les bonus ramassés. Allez, on s'y met tout de suite ! :)

Afficher les munitions et l'arme sur le HUD

Actuellement, on affiche le score et l'état du joueur sur l'écran. Pour terminer le HUD, nous allons devoir ajouter deux petites choses ! En route pour Weapons.js ! Comme nous gérons nos armes là-bas, c'est dans ce fichier que nous nous occuperons de l'affichage des textes liés à notre arsenal. ;) Dans l'objet, vous allez déclarer trois variables qui vont simplifier l'accès au HTML pour écrire dedans.

Juste en dessous de la définition de actualWeapon et de isActive, écrivez les lignes suivantes :

// Nombre de munitions affichées
this.textAmmos = document.getElementById('numberAmmos');

// Nombre de munitions maximum affichées
this.totalTextAmmos = document.getElementById('totalAmmos');

// Nom de l'arme affichée
this.typeTextWeapon = document.getElementById('typeWeapon');

Maintenant que tout cela est défini, nous allons tout de suite remplir le HTML juste en dessous :

// On récupère les paramètres de l'arme actuelle
var paramsActualWeapon = this.Armory.weapons[this.inventory[this.actualWeapon].typeWeapon];

// Si l'arme a des munitions
if(paramsActualWeapon.setup.ammos){
    this.textAmmos.innerText = this.inventory[this.actualWeapon].ammos;
    this.totalTextAmmos.innerText = paramsActualWeapon.setup.ammos.maximum;
    this.typeTextWeapon.innerText = paramsActualWeapon.name;
}

Et avec cela, vous avez déjà affiché l'arme et les munitions de l'arme que le joueur a en main dès le début du jeu ! Maintenant, il faut rendre les données dynamiques.

Dans launchFire tout d'abord, il nous faut changer la valeur de textAmmos quand le joueur a tiré. Nous allons donc ajouter, en dessous de this.inventory[this.actualWeapon].ammos-- :

this.inventory[this.actualWeapon].ammos--;
this.textAmmos.innerText = this.inventory[this.actualWeapon].ammos;

Les munitions diminuent bien. Il faut maintenant que la valeur change dès que le joueur change d'arme ! Pour cela, vous allez ajouter un morceau de code un peu plus important à nextWeapon. Dans la condition qui détermine si l'arme suivante est différente de celle que le joueur a actuellement en main, vous allez ajouter à la fin ce qui suit :

if(this.actualWeapon != nextPossibleWeapon){
    // ...
    // ...
    // ...

    var actualTypeWeapon = this.Armory.weapons[this.inventory[this.actualWeapon].typeWeapon];
    // Si l'arme a des munitions
    if(actualTypeWeapon.setup.ammos){
        this.textAmmos.innerText = this.inventory[this.actualWeapon].ammos;
        this.totalTextAmmos.innerText = actualTypeWeapon.setup.ammos.maximum;
        this.typeTextWeapon.innerText = actualTypeWeapon.name;
    }else{
        // Sinon, le texte est différent
        this.typeTextWeapon.innerText = actualTypeWeapon.name;
        this.textAmmos.innerText = "Inf";
        this.totalTextAmmos.innerText = "Inf";
    }
}

Cas suivant ! Celui où on récupère des munitions !

Pour cela, il vous suffit d'aller dans la fonction reloadWeapon. Ajoutez le code suivant dans la boucle for où nous avons vérifié que l'arme est bien en la possession du joueur, à la fin :

for (var i = 0; i < this.inventory.length; i++) {
    if(this.inventory[i].typeWeapon === type){
        var existingWeapon = true;
        if((this.inventory[i].ammos + numberAmmos) > this.Armory.weapons[i].setup.ammos.maximum){
            this.inventory[i].ammos = this.Armory.weapons[i].setup.ammos.maximum
        }else{
            this.inventory[i].ammos += numberAmmos;
        }
        var actualTypeWeapon = this.Armory.weapons[this.inventory[this.actualWeapon].typeWeapon];
        this.textAmmos.innerText = this.inventory[this.actualWeapon].ammos;
        this.totalTextAmmos.innerText = actualTypeWeapon.setup.ammos.maximum;
        this.typeTextWeapon.innerText = actualTypeWeapon.name;
        break;
    }
}

Dernier cas à traiter : quand on recoit une arme depuis un props !

Dans la boucle for où nous créons une nouvelle arme depuis Arena.js (dans weaponBox), ajoutez les lignes suivantes avant pickableDestroyed :

Weapons.textAmmos.innerText = actualInventoryWeapon.ammos;

Weapons.totalTextAmmos.innerText = 
Weapons.Armory.weapons[actualInventoryWeapon.typeWeapon].setup.ammos.maximum;

Weapons.typeTextWeapon.innerText = 
Weapons.Armory.weapons[actualInventoryWeapon.typeWeapon].name;

Avec tout cela, le texte affiché est proprement affiché ! ^^ 

Plus qu'une dernière étape, un petit peu plus complexe : l'annonceur.

L'annonceur : votre meilleur allié

Dans la plupart des jeux récents, une personne vous guide à travers le jeu, que ce soit via l'affichage de texte ou par une voix, et même parfois les deux. Son intérêt est multiple : 

  • Vous renseigner quand vous récupérez un bonus en jeu

  • Vous motiver avec l'annonce de kill

Annonce de bonus

Pour commencer et avant de rentrer dans le vif du sujet, nous allons nous poser deux secondes pour que vous puissiez bien comprendre ce qui va être fait.

La div announcementKill possède dès le début une  classe nommée announcementClose. Si vous enlevez cette classe, la fenêtre apparaît. L'idée derrière ce système, c'est que nous allons afficher et faire disparaître cette fenêtre à notre envie en ajoutant et retirant cette classe.

Direction Arena.js où nous allons créer la fonction displayNewPicks !

displayNewPicks : function(typeBonus) {
    // Récupère les propriétés de la fenêtre d'annonce
    var displayAnnouncement = document.getElementById('announcementKill');
    var textDisplayAnnouncement = document.getElementById('textAnouncement');
    
    // Si la fenêtre possède announcementClose (et qu'elle est donc fermée)
    if(displayAnnouncement.classList.contains("annoucementClose")){
        displayAnnouncement.classList.remove("annoucementClose");
    }
    // On vérifie que la police est à 1 (nous verrons plus tard pourquoi)
    textDisplayAnnouncement.style.fontSize = '1rem';
    
    // On donne à textDisplayAnnouncement la valeur envoyée à displayNewPicks
    textDisplayAnnouncement.innerText = typeBonus;
    
    // Au bout de 4 secondes, si la fenêtre est ouverte, on la fait disparaître
    setTimeout(function(){ 
        if(!displayAnnouncement.classList.contains("annoucementClose")){
            displayAnnouncement.classList.add("annoucementClose");
        }
    }, 4000);
},

On voit que cette fonction prend simplement en paramètre le message à afficher. Nous allons donc passer par ici dès que le personnage prend un objet. Il vous faut donc ajouter celle-ci à tous les endroits où le joueur récupère un bonus, une arme ou des munitions, et cela se fait dans _checkProps ! ^^ 

Là-bas, dans chaque boucle for, nous allons ajouter displayNewPics avec un message, récupéré depuis Armory.

// Pour la boucle bonusBox
this.displayNewPicks(paramsBonus.message);

// Pour la boucle weaponBox
this.displayNewPicks(paramsWeapon.name);

// Pour la boucle ammosBox
this.displayNewPicks(paramsAmmos.meshAmmosName);

Les messages s'affichent désormais correctement ! Plus qu'une dernière ligne droite, les messages de multi-kills !

L'annonceur de multi-kill

Je nous ai gardé le plus fun pour la fin du cours : les messages de multi-kill. Leur portée est immense car elle récompense et enthousiasme le joueur avec des messages de plus en plus gros, de plus en plus forts. L'idée est que si vous tuez un joueur dans un laps de temps imparti, à la place de vous annoncer celui que vous avez tué, le message va annoncer "double kill", puis "multi kill", puis "ultra kill", et ainsi de suite. C'est ce que nous allons faire, dans une compilation de pas moins de 15 messages (le joueur devra donc faire 15 kills pour arriver au bout des messages que nous avons définis). Mouahaha, on va bien s'amuser. :diable:

Tout d’abord, créez cette compilation de messages délicats et subtils dans Armory.js, dans un nouveau tableau que nous appellerons multiKillAnnoucement.

this.multiKillAnnoucement=["Double Kill", "Multi Kill", "Mega Kill", "ULTRA KILL", "LUDICROUS", "TEETH SHOWER", "INSANE", "DEMONIC", "HAIL THE HELIX", "ASS CRUSHER", "DIVINE", "GENOCIDE","OMFWTFBBQ", "42" , "HOLY SHIT"];

Outre le langage ordurier quasi obligatoire dans ce genre d'annonces de FPS, l'objectif est de rendre chaque message reçu plus jouissif que le précédent (vous noterez mon choix pour quelques références geek :p).

Maintenant que les messages sont définis, vous allez pouvoir créer la fonction qui affiche ces messages. De la même façon que pour les bonus en jeu, l'objectif est d'afficher et de cacher la fenêtre d'annonce au bon moment. Dans Player.js, nous allons créer une fonction nommée newDeadEnnemy.

Les données envoyées à cette fonction proviennent de NetworkManager et contiennent en fait une seule chose : le nom du joueur tué.

Première étape, instancions une variable dans Player qui nous permettra de compter le nombre de joueurs tués à la suite ainsi que deux variables qui vont permettre dans tout le code d'accéder aux parties interactives du HTML.

// Compteur de tués à la suite
this.killStreak = 0;

// Zones de texte pour les annonces
this.displayAnnouncement = document.getElementById('announcementKill');
this.textDisplayAnnouncement = document.getElementById('textAnouncement');

Maintenant, avec ce compteur, nous allons pouvoir efficacement utiliser newDeadEnnemy. Pour cela, créez la fonction dans prototype.

newDeadEnnemy : function(nameKilled){
    var _this = this;
    // Si le nombre de kill d'affilé est à 0
    if(this.killStreak === 0){
        // On fixe la taille du texte à 1rem
        this.textDisplayAnnouncement.style.fontSize = '1rem';
        // De base, si aucun nom n'est donné, on dit que Bob a été tué
        var messageDisplay = "Vous avez tué Bob"
        if(nameKilled){
            // S'il y a un nom de donné, on affiche le nom
            var messageDisplay = "Vous avez tué " + nameKilled;
        }
    }else{
        // On va chercher les messages de kill dans Armory
        var multiKillAnouncement = this.camera.weapons.Armory.multiKillAnnoucement;
        // Si on a déja tué plus d'une personne
        // Et si on n'a pas atteint la limite des 15 messages
        if(this.killStreak<=multiKillAnouncement.length){
            // On affiche le message associé au nombre de kills
            var messageDisplay = multiKillAnouncement[this.killStreak-1];
            // On augmente la taille du texte proportionellement à la rareté du message
            this.textDisplayAnnouncement.style.fontSize = (1+(this.killStreak/1.2))+'rem';
        }else{
            // Si on a atteint la limite de messages disponibles
            // On affiche le dernier de la liste
            var messageDisplay = multiKillAnouncement[multiKillAnouncement.length-1]
        }
        
    }
    // On augmente le nombre de tués à la suite
    this.killStreak++;
},

Avec tout cela, le texte et la taille du texte sont définis. Nous devons maintenant afficher ce texte et déterminer le délai avant que le compteur de killStreak se réinitialise (l’intérêt de ce genre de compteur est qu'il faut réaliser le maximum de kills dans un minium de temps ^^).

newDeadEnnemy : function(nameKilled){
    var _this = this;
    // Si le nombre de kill d'affilé est à 0
    if(this.killStreak === 0){
        // On fixe la taille du texte à 1rem
        this.textDisplayAnnouncement.style.fontSize = '1rem';
        // De base, si aucun nom n'est donné, on dit que Bob a été tué
        var messageDisplay = "Vous avez tué Bob"
        if(nameKilled){
            // S'il y a un nom de donné, on affiche le nom
            var messageDisplay = "Vous avez tué " + nameKilled;
        }
    }else{
        // On va chercher les messages de kill dans Armory
        var multiKillAnouncement = this.camera.weapons.Armory.multiKillAnnoucement;
        // Si on a deja tué plus d'une personne
        // Et si on n'a pas atteint la limite des 15 messages
        if(this.killStreak<=multiKillAnouncement.length){
            // On affiche le message associé au nombre de kills
            var messageDisplay = multiKillAnouncement[this.killStreak-1];
            // On augmente la taille du texte proportionellement à la rareté du message
            this.textDisplayAnnouncement.style.fontSize = (1+(this.killStreak/1.2))+'rem';
        }else{
            // Si on a atteint la limite de messages disponibles
            // On affiche le dernier de la liste
            var messageDisplay = multiKillAnouncement[multiKillAnouncement.length-1]
        }
        
    }
    // On augmente le nombre de tués à la suite
    this.killStreak++;

    // Si l'annonceur est fermé
    if(this.displayAnnouncement.classList.contains("annoucementClose")){
        // On l'ouvre
        this.displayAnnouncement.classList.remove("annoucementClose");
    }
    // On affiche ce qui est contenu dans messageDisplay
    this.textDisplayAnnouncement.innerText = messageDisplay;

    // Si le compteur a été créé, on le réinitialise
    if(this.timerKillStreak){
        clearTimeout(this.timerKillStreak);
    }
    // On set le compteur à 3 secondes. 
    // Passé ce délai, le jeu fait repasser le compteur de kill à 0 
    // Et ferme la fenêtre de messages
    this.timerKillStreak = setTimeout(function(){ 
        _this.killStreak = 0;
        
        if(!_this.displayAnnouncement.classList.contains("annoucementClose")){
            _this.displayAnnouncement.classList.add("annoucementClose");

        }
    }, 3000);
},

 Avec tout cela, les messages sont correctement affichés.

Ça, c'est fait ! :) Le code est-il enfin terminé ? Non, mais presque ! Il nous reste une dernière chose à annoncer… la mort du joueur. Ajoutez quelques lignes dans playerDead, au tout début, avant tout le reste du code :

if(this.displayAnnouncement.classList.contains("annoucementClose")){
    this.displayAnnouncement.classList.remove("annoucementClose");
}
this.textDisplayAnnouncement.style.fontSize = '1rem';
this.textDisplayAnnouncement.innerText = 'Vous êtes mort';

Vous avez déjà vu ce type de code, je ne vais pas revenir dessus. Il ne nous reste plus qu'à fermer ce message quand le joueur revient à la vie, dans le setTimeout.

setTimeout(function(){ 
    newPlayer._initCamera(newPlayer.game.scene, canvas, newPlayer.spawnPoint);
    newPlayer.displayAnnouncement.classList.add("annoucementClose");
    newPlayer.launchRessurection();
}, 4000);

Une fois que tout cela est fait, décommentez les dernières lignes commentées dans NetworkManager, celles qui gèrent les messages pour les joueurs tués.

socket.on ('killGhostPlayer', function (arrayData) {
    var idArray = arrayData[0];
    var roomScore = arrayData[1];
    if(idArray[0] != personalRoomId){
        deleteGameGhost(game,idArray[0]);
    }
    if(idArray[1] == personalRoomId){
        game._PlayerData.newDeadEnnemy(idArray[2]);
    }
    game.displayScore(roomScore);
});

Ça y est ! Nos messages sont tous marqués… et le code est enfin terminé.

La fin ?

Terminé ? Pour de vrai ? Mais on pourrait encore ajouter plein de choses au jeu ! Et puis on n'a pas ajouté de plateformes, de bumper, de trous… :o 

Je suis tout à fait d'accord ! Il reste énormément de choses à faire qui pourraient rendre le jeu unique. Cependant, tout ce qui reste à ajouter serait très répétitif en termes de compétences. Pour la suite, vous allez devoir découvrir par vous-même… et c'est donc la fin de ce long cours pour créer un jeu ! Sacré travail, mais vous avez désormais un joli petit FPS qui est jouable entre amis et qui n'attend que d'être moddé et amélioré !

J'espère que ce cours vous a donné envie de faire des FPS sur navigateur, car c'est possible ! Merci d'avoir fait cette aventure avec moi, ça a été un réel plaisir de vous apprendre ma façon de développer un jeu avec Babylon. Il vous reste encore une toute dernière chose à faire… un petit quiz pour valider toutes les connaissances que vous avez apprises dans cette partie. 

Quant à moi, je vous dis à une prochaine fois et bonne chance dans vos découvertes WebGL ! ^^ ‌

Exemple de certificat de réussite
Exemple de certificat de réussite