Développer un nouveau mini-jeu en Javascript - Partie 1
Récemment, à quelques jours d'intervale, je suis tombé sur le même mini jeu, deux fois. La première, c'était dans LEGO Super Heroes 2, le jeu pour Playstation 4, tandis que la seconde c'était dans un Unlock, ces escapes games à faire à la maison.
Lors de la partie d'escape game, nous étions nombreux, et évidemment, lorsque l'un d'entre nous a réussi le puzzle, il n'était plus possible pour les autres d'y accéder. Histoire de contenter les joueurs frustrés, je me suis demandé si je ne pouvais pas le reproduire rapidement. En me basant sur le code source du démineur, réalisé précédemment, j'ai pu créer ma propre version du jeu, en une petite dizaine de minutes. Chacun a alors pu tenter de résoudre le puzzle.
Le but du jeu est simple, nous disposons d'un rectangle découpé en case. Chaque case est éteinte, et le but est d'arriver à allumer toutes les cases en même temps. Sauf que, lorsque l'on clique sur une case, cela n'allume pas que la case cliquée, mais aussi toutes les cases adjacentes. Aussi, une case déjà allumée s'éteindra, si l'on tente de l'allumer à nouveau.
Le jeu vous parait simple ? Il ne vous reste plus qu'à le re-créer pour tenter de le finir.
D'ailleurs, si vous aviez déjà suivi et bien compris le tutoriel sur le démineur, vous disposez de toute la technique nécessaire pour reproduire le jeu. Il vous faut simplement réfléchir aux algorithmes qui composent le jeu, et ils sont bien moins complexes que ceux du jeu que nous avions vu précédemment.
Ne connaissant pas le nom du jeu, je l'ai nommé Lighter, si l'un d'entre vous connait le nom exact de ce jeu, les commentaires restent ouverts.
Voici les explications pour la création du jeu. Toujours basé sur une page web, nous allons reprendre la même structure que pour les jeux précédents.
Ainsi, je crée un dossier nommé Lighter, dans lequel je trouve les 3 fichiers suivants.
- Lighter.css
- Lighter.htm
- Lighter.js
Commençons par la structure HTML de notre jeu, elle est strictement identique à celle du démineur, seuls les appels Javascript diffèrent, car les noms des jeux sont différents.
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Lighter</title>
<meta charset="UTF-8" />
<script src="Lighter.js"></script>
<script>
/* on load scripts */
window.onload = function() {
Lighter.initialise();
}
</script>
<link href="Lighter.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div class="page">
<h1>
Lighter
</h1>
<div>
</div>
<div class="game" id="plateau">
</div>
<div class="options">
<div id="result" class="result">
</div>
<div class="menus">
<div class="menu" onclick="Lighter.startGame('easy');">
Jouer "Facile"
</div>
<div class="menu" onclick="Lighter.startGame('normal');">
Jouer "Normal"
</div>
<div class="menu" onclick="Lighter.startGame('hard');">
Jouer "Difficile"
</div>
<div class="menu" onclick="Lighter.startGame('extreme');">
Jouer "Extrême"
</div>
</div>
</div>
</div>
</body>
</html>
Les styles réalisant le design du jeu, sont, quant à eux, légèrement différents. Ils sont surtout bien plus simples que pour le démineur. Ainsi, si l'on occulte toute la partie décrivant la page HTML en tant que telle, qui est totalement identique, il ne reste que quelques lignes décrivant :
- le tableau des lumières
- les cellules éteintes
- les cellules allumées
- les cellules survolées (pour ajouter un peu d'interaction)
/* global */ * {font-family:Arial, sans-serif; font-size:14px; font-weight:normal; font-style:normal; color:#454550; padding:0; margin:0; box-sizing:border-box;} body {background:white; margin:100px;} p {padding:8px 0;} a { text-decoration:none; color:#ff9600; transition:color 0.3s; } a:hover {color:#00378B;} ol { margin:0px 28px; padding:0;} li { margin:4px 4px 8px; } b, strong {font-weight:bold;} i, em {font-style:italic;} blockquote { margin:8px 20px; } input, textarea, select { border:none; padding:0; background:#fdea91; color:#5A5A5A; font-weight:bold; font-size:12px; border-radius:4px;} input, textarea, option { padding:4px 4px; font-weight:bold; color:#5A5A5A; font-size:12px;} textarea {resize:none;} .inputs {width:200px;} .linputs {width:400px;} .sinputs {width:150px;} .vsinputs {width:75px;} .vlinputs {width:100%;} form {margin:0;} .img {font-size:0} sup, sub {font-size:9px; color:inherit;} img {border:0; max-width:100%; height:auto;} /* titre */ h1 {font-size:20px; color:#0005c2;} /* page */ .page {font-size:0;} .page .game {width:50%; display:inline-block; vertical-align:top;} .page .options {width:50%; display:inline-block; vertical-align:top;} /* menu */ .menus .menu {padding:10px 25px; margin:8px; background:#4366b4; color:white; cursor:pointer; width:200px;} /* result */ .result {text-align:center; font-size:30px;} /* lighter field */ .field {background:#888888; } .field .cell {width:30px; height:30px; background:#888888; cursor:pointer; border-left:solid 1px #666666; border-top:solid 1px #666666; border-right:solid 1px #aaaaaa; border-bottom:solid 1px #aaaaaa;} .field .cell:hover { background:#338833;} .field .cell.marked {background:yellow;} .field .cell.marked:hover {background:#333388;}
Enfin, le moteur du jeu reprend lui aussi la même structure. Il est inutile de tout changer, puisque cela fonctionne, et avec l'habitude, on devient beaucoup plus performant pour développer mais aussi pour débugguer un code source.
var Lighter = {name: 'Lighter', difficulties: {};}, settings: {}, game: {}, initialise: function() {this.startGame('easy');}, startGame: function(difficulty) {this.settings = this.difficulties[difficulty]; this.drawGameBoard(); this.resetGame();}, drawGameBoard: function() {}, resetGame: function() {}, checkWin: function() {}, displayWin: function() {/* Affiche le resultat dans l'espace dedie, en couleur */ document.getElementById('result').innerHTML = 'Gagné'; document.getElementById('result').style.color = '#43b456'; /* Defini l'etat de la partie a termine */ this.game.status = 0;},
Comme nous avions déjà vu une grande partie de cette structure avec le démineur, j'ai même laissé quelques lignes de code remplissant certaines fonctions qui sont strictement identiques. Ainsi vous pouvez voir que l'on retrouve les valeurs d'initialisation, de paramètres et de jeu, puis les fonctions initialise, startGame, drawGameBoard, resetGame, checkWin, displayWin.
Vous pouvez remarquer que l'on dispose de la fonction displayWin, mais pas de la fonction displayLose. Dans ce jeu il n'est pas possible de perdre, à proprement parler. Tant que le joueur n'est pas arrivé au bout, il peut continuer à essayer de résoudre le puzzle.
Nous pourrions imaginer qu'une fonction displayLose soit utile, dans le cas par exemple où, l'on ajouterait un chronomètre et qu'il faille résoudre le jeu dans le délai imparti. Mais cela sera pour un autre jour.
Maintenant, il ne reste plus qu'à remplir toutes les petites boites vides de notre structure. Commençons par les paramètres.
Comme pour le démineur, les données de jeu seront assez simples (et toujours identiques)
game: {status: 0, field: null,},
Nous avons 2 variables, la première indique l'état du jeu. La seconde garde l'état de chacune des cases du plateau de jeu. Par simplicité, j'ai même conservé le même nom, qui faisait référence au "champ" de mines.
Définissons maintenant les difficultés
difficulties: {easy: {},lines: 3, columns: 4,}, normal: {lines: 4, columns: 5,}, hard: {lines: 4, columns: 6,}, extreme: {lines: 4, columns: 8,},
Le mode facile reprend la taille du tableau tel qu'il était défini dans les 2 jeux ou nous avions pu voir le puzzle. Les difficultés suivantes sont le fruit de mon imagination, et vous pouvez les définir telles que vous le souhaitez.
Pour être honnête, elles ont été ajoutées car il était plus simple de les définir que de les supprimer du code source du démineur. Aussi, si nous arrivons sans soucis à finir le puzzle original, nous n'avons jamais réussi à le terminer avec une autre taille. Peut être un challenge pour vous ?
Enfin, il reste les réglages du jeu. Pour cette partie, cela va être vite vu, car, en l'état actuel, nous n'avons besoin d'aucun réglage. La variable restera donc vide. Vous pourrez éventuellement vous en servir dans le cadre du développement d'un version alternative.
A très vite pour la suite de l'article.