Admin/Dev

25
Août
2021

Développer un nouveau mini-jeu en Javascript - Partie 2

Publié par sky

Après un premier article préparatoire, rentrons dans le vif du sujet, avec le développement concret du jeu. Comme d'habitude, avant de se lancer dans le code, il est nécessaire de réfléchir de quoi nous avons besoin.

Dans le cadre de ce jeu, ce n'est pas bien compliqué. Nous aurons d'abord besoin de définir les fonctions d'initialisation du jeu, à savoir la mise à zéro des variables ainsi que le dessin le terrain, avec l'ensemble des cases. Puis, nous devrons gérer l'interactivité lors du clic sur une case. Tout le reste découlera de cette dernière action. Nous pourrons donc le faire au sur et à mesure tout en segmentant par fonctionnalité.

Attaquons avec la fonction resetGame qui va réinitialiser toutes les variables.

resetGame: function() {
/* Creons le plateau, vide */ this.game.field = new Array(); for (i = 1; i <= this.settings['lines']; i++) {
this.game.field[i] = new Array(); for (j = 1; j <= this.settings['columns']; j++) {
this.game.field[i][j] = 0;
}
} /* On definit le status au mode jeu */ this.game.status = 1;
},

La fonction contient deux sections, la première initialise le tableau qui correspond au terrain. Toutes les cases y sont représentées, et la valeur par défaut est 0. Toutes les lumières sont éteintes.

La seconde section est tout simplement l'état de partie qui passe à 1, indiquant qu'une partie est en cours.

Maintenant que le terrain est enregistré en mémoire, regardons la fonction qui réalise l'affichage permettant au joueur de visualiser le terrain et donc de jouer.

drawGameBoard: function() {
/* Initialise l'affichage du resultat */ document.getElementById('result').innerHTML = ''; /* Dessine le plateau */ board = document.getElementById('plateau'); board.innerHTML = ''; border = document.createElement('table'); border.setAttribute('oncontextmenu', 'return false;'); field = document.createElement('tbody'); border.appendChild(field); border.className = 'field'; board.appendChild(border); /* Dessine les cellules */ for (i = 1; i <= this.settings['lines']; i++) {
line = document.createElement('tr'); for (j = 1; j <= this.settings['columns']; j++) {
cell = document.createElement('td'); cell.id = 'cell-'+i+'-'+j; cell.className = 'cell'; cell.setAttribute('onclick', this.name+'.updatePosition('+i+', '+j+');'); line.appendChild(cell);
} field.appendChild(line);
}
},

Cette fonction est aussi séparée en plusieurs sections, délimitées ici par les commentaires. Dans la première, elle efface tout ce qui pouvait se trouver sur le terrain. Après avoir fait table rase, la seconde section va dessiner un nouveau champ, en utilisant un tableau HTML. Si l'on rentre un peu dans les détails, nous voyons que l'on y désactive le clic droit, et on lui applique le style CSS "field", comme nous avions fait pour le démineur.

Enfin, la dernière section va dessiner toutes les cellules. Une première boucle s'occupe d'ouvrir les lignes, tandis qu'une seconde va ajouter toutes les colonnes. Evidemment, il ne faut oublier de donner un identifiant unique à chaque cellule, ainsi que de lui ajouter une interaction. Dans notre cas, nous avons ajouté une fonction "updatePosition" lors de l'événement "click". Cette fonction va prendre en paramètre les coordonnées de la cellule.

Tout est désormais prêt pour initialiser une partie. Nous devons désormais nous occuper des interactions, et en particulier de la fonction "updatePosition" que nous avons appliqué à chaque cellule.

Prenons quelques secondes pour définir ce que cette fonction doit faire... Encore une fois ce n'est pas bien compliqué. En premier lieu, nous vérifions que le jeu est bien actif. Ensuite, elle doit inverser les lumières concernées, que ce soit visuellement, ou dans les données du jeu. Puis, elle doit simplement vérifier si le joueur a gagné.

Nous devons inverser jusqu'à 5 lumières, qui correspondent à la cellule cliquée, ainsi que les 4 cases adjacentes. Attention, lorsque l'on clique sur une case placée sur le bord du champ, il ne faut allumer ou éteindre la case qui est virtuellement en dehors du tableau.
Dans la même idée, en cliquant une case dans le coin, il n'y aura que 3 cases à switcher.

Pour éviter de faire le même travail 5 fois, nous avons choisi de "factoriser" cette action en tant que fonction dédiée. Ainsi, il suffit de l'appeler autant de fois que nécessaire.

Voici donc la fonction "updatePosition"

updatePosition: function(x, y) {
/* Verifie que le jeu est actif */ if (this.game.status == 0)
return;
             /* Met a jour la cellule selectionnee */ this.switchCell(x, y); /* Met a jour la cellule de gauche */ this.switchCell(x-1, y); /* Met a jour la cellule de droite */ this.switchCell(x+1, y); /* Met a jour la cellule de dessus */ this.switchCell(x, y-1); /* Met a jour la cellule de dessous */ this.switchCell(x, y+1); /* Verifie si la partie est gagnee */ this.checkWin();
},

Comme prévu, on y voit l'appel à notre sous-fonction pour chacune des cellules qui doit changer d'état, ainsi que le test de réussite qu'il ne faut pas oublier.

Voici le contenu de la fonction switchCell

switchCell: function(x, y) {
/* Verifie que la cellule existe */ if (x >= 1 && x <= this.settings['lines'] && y >= 1 && y <= this.settings['columns']) {
/* Inverse la valeur de la case en question */ this.game.field[x][y] = Math.abs(this.game.field[x][y]-1); /* Met a jour l'affichage de la cellule */ document.getElementById('cell-'+x+'-'+y).classList.toggle('marked');
}
},

Elle prend les coordonnées de la cellule à changer. Si l'on suit le cheminement depuis la fonction précédente, on remarque que les coordonnées sont identiques pour le premier appel à la fonction switchCell, mais qu'ensuite, elles sont différentes pour reprendre le cellule formant la croix autour de la cellule cliquée.

Concrètement, cette fonction commence par faire un test pour vérifier que la case souhaitée existe bien, car nous avons vu précédemment, que dans certains cas, la croix ne rentre pas complètement dans le tableau.

Ensuite, elle inverse la valeur dans le tableau des données de jeu, avec un petit subterfuge que j'affectionne. Pour passer de 0 à 1 ou de 1 à 0 avec une seule ligne de commande de manière arithmétique (pas de manière logique).

x = Math.abs(x-1);

Ainsi si x est égal à 1, on lui retire 1, il devient égal à 0, ce qui ne change pas en valeur absolue. Si x est égal à 0, on lui retire 1 aussi, il devient -1, et se transforme en 1 avec la valeur absolue.

Enfin, on modifie l'affichage de la cellule. Si elle était éteinte, nous l'allumons graphiquement, et vice-versa.

Dernière fonction à aborder, il faut désormais s'intéresser à la fonction "checkWin". Cette fonction va vérifier à la fin de chaque coup joué, si le joueur a complété le puzzle.

checkWin: function() {
/* On verifie toutes les cases */ for (var i = 1; i <= this.settings['lines']; i++) {
for (var j = 1; j <= this.settings['columns']; j++) {
/* On sort de la boucle, des que l'on trouve une case a 0 */ if (this.game.field[i][j] == 0)
return false;
}
} /* Aucune case trouvee avec la valeur 0, la partie est gagnee */ this.game.status = 0; /* On affiche la victoire */ this.displayWin();
},

Pour réaliser cette tâche, le plus simple est de tester toutes les cases pour vérifier si elles sont allumées. C'est ce que fait la première section de la fonction. Cependant, dès qu'une case est trouvée éteinte, il n'est pas nécessaire de continuer. Dans ce cas, nous  effectuons un "return", qui nous fera sortir de la fonction. Les deux boucles sont, de ce fait, coupées, et aucune des actions placées ensuite ne seront exécutées.

Dans le cas contraire où toutes les cases sont allumées, et que donc le joueur a gagné, les deux boucles sont parcourues entièrement, ne sont jamais coupées, et les deux dernières petites sections sont exécutées. Elles sont simplement le fait de marquer l'état du jeu comme non actif, et d'afficher la victoire, avec une fonction reprise directement depuis le jeu de démineur.

Comme vous pouvez le voir, le jeu en lui même n'est pas bien compliqué techniquement parlant. L'initialisation de ce dernier est finalement plus complexe que le fait de jouer. Comme d'habitude, l'étape la moins aisée est de trouver une idée de jeu, reproduire un jeu est une chose, trouver une idée originale en est une autre. La seconde tâche la plus compliquée est de bien réfléchir à la manière d'aborder et de réaliser le jeu. Cela permettra d'éviter bien des erreurs lors du développement du jeu à proprement parlé. Cette dernière étape est finalement la plus simple, et n'est qu'une question de temps pour réaliser tout ce qui a été imaginé en amont.

Comme pour les créations précédentes, voici les sources complètes du jeu, avec les commentaires explicatifs.

 
Sommaire de la série
 
 
Commentaires
Aucun commentaire pour le moment.

 

Poster un commentaire
En postant sur skymac.org, je m'engage à être courtois et à ce que mon message soit pertinent avec le sujet de l'article.
En outre, j'accepte, sans condition, que mon message soit refusé et supprimé si ces règles ne sont pas appliquées.
Ouvrir le panneau de gestion des cookies
Fermer le panneau
Ce site utilise des cookies pour assurer son bon fonctionnement. Il utilise aussi des cookies issues de services tiers permettant de proposer des fonctionnalités avancées. À tout moment, vous pouvez choisir quels services vous souhaitez activer ou refuser, afin de retirer votre consentement quant à l'utilisation des cookies.
 
Personnalisation des services
Vous êtes libre de choisir quels services vous souhaitez activer. En autorisant ces services tiers, vous acceptez le dépôt et la lecture de cookies et l'utilisation de technologies de suivi nécessaires à leur bon fonctionnement. En retirant votre consentement pour certains de ces services, certaines fonctionnalités du site peuvent ne plus fonctionner.
Navigation du site  En savoir plus
Le site écrit un cookie de session permettant son bon fonctionnement et aidant à la navigation. Il ne peut être désactivé.
Utilisation : 1 cookie, enregistre l'identifiant de la session.
Durée de vie : Le cookie est présent pendant toute la session sur le site. Il devient obsolète après 24 minutes d'inactivité.
Obligatoire
Popup Média
Afficher des vidéos depuis Yoube ou Dailymotion.
 
Tout accepter Tout refuser Gérer