Les applications web modernes sont pensées de façon à rendre leur contenu le plus interactif et ludique possible. Ainsi une interface peut présenter des zones qui apparaissent ou disparaissent en fonction du comportement de l’utilisateur. Celui-ci peut de plus en plus souvent déplacer des éléments d’une page pour les réarranger selon sa convenance. Dojo encore une fois dispose de très nombreux composants permettant d’obtenir ce type de comportement et ceux utilisés ici, rendre une case déplaçable par « drag and drop » et des effets d’affichage et disparition dynamiques, ne sont qu’une infime partie des possibilités proposées.

Le sujet du jour est un grand classique des jeux sur navigateurs web. Il en existe de très nombreuses variantes avec des game plays différents, la version la plus populaire, qui a fortement contribué à l’enrichissement de son éditeur « King », étant bien entendu « candy crush ». Les quelques sources proposées ici ne vous permettront pas d’obtenir un jeu aussi abouti, mais elles constitueront une base fonctionnelle à partir de laquelle vous pourrez ajouter vos règles et votre design pour ainsi créer votre propre version.

Dojo_eclateGemmes_visuel

Détails techniques

Cf. billet précédent => notepad++ et firefox suffisent…
Vous aurez aussi besoin de deux fichiers sons (ici en .mp3). Pour ma part, j’ai à nouveau puisé dans le site de bruitage libre de droits, universal-soundbank.com

Organisation des fichiers

Nous utilisons dans cet exemple seulement trois fichiers en plus des deux sons : index.html, situé à la racine du répertoire projet et qui contiendra le bouton de lancement d’une nouvelle partie, et eclateGemmes.js, positionné dans un sous-répertoire « EclateGemmes » et qui possède toute la machinerie pour générer le jeu, et enfin gemmes.json qui va contenir les URL des images représentant chaque gemme. Le projet présente donc la structure suivante :
Dojo_sudoku_structure

Le fichier index.html

Dans ce fichier se trouve, vous en avez l’habitude maintenant, la configuration et le chargement de Dojo, le script d’ajout de la fonction de lancement du jeu depuis le script externe, et le bouton.

<html>
<head>
<script>
	// Configuration dojo CND
	var dojoConfig = {
		async : true,
		// Enregistrement des modules
		packages : [ {
			name : "eclateGemmes",
			location : location.pathname.replace(/\/[^/]*$/, '') + '/eclateGemmes'
		} ]
	};
</script>
<script
	src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"></script>
<link rel="StyleSheet" type="text/css"
	href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/tundra/tundra.css">

<script>
	require([ "dojo/dom", "dojo/on", "dojo/_base/window", "dojo/parser", 
	          "eclateGemmes/eclateGemmes", "dijit/form/Button", "dojo/domReady!" ], 
          function(dom, on, win, parser, eclateGemmes) {
		parser.parse();

		// Ajout des actions sur les boutons
		on(dom.byId("startButtoneclateGemmes"), "click", eclateGemmes.play);
	});
</script>
</head>
<body class="tundra">
	<div id="cmd">
		<button id="startButtoneclateGemmes" data-dojo-type="dijit/form/Button"
			type="button">eclateGemmes !</button>
	</div>
	<div id="jeu"></div>
</body>
</html>

Le fichier eclateGemmes.js

Ce script contient la machinerie pour générer le plateau et les cases. Histoire de clarifier les différentes fonctions, je vous propose un résumé condensé du contenu de la classe :

eclateGemmes.cases = []; // cases du plateau
eclateGemmes.values = []; // valeurs lues dans json
eclateGemmes.casesJouables = [];
eclateGemmes.NBR_CASE_X = 6;
eclateGemmes.NBR_CASE_Y = 6;
eclateGemmes.dureeEffets = 500; // 0.5 seconde
eclateGemmes.effetsEnCours = new arrayList([]); // id des cases avec effet en cours
eclateGemmes.coupsJoues = 0;
eclateGemmes.points = 0;
eclateGemmes.combi = 0;
eclateGemmes.combiEnCours = 0;
eclateGemmes.pointsEnCours = 0;
eclateGemmes.refreshCasesJouables = function(cellId) {
// Redefinition des cases jouables selon case selectionnee
}
eclateGemmes.contraintesPlateau = function() {
// Definition de la zone maximum de deplacement des cases
}
eclateGemmes.isEffetsResolus = function() {
// Retourne true si tous les effets en cours ont ete resolus
}
eclateGemmes.creerGemmeAleatoire = function(caseEnCours) {
// Construit le contenu pour une case aleatoirement a partir de eclateGemmes.values
}
eclateGemmes.startNewGame = function() {
remplitEtNettoie = function() {
// remplit les cases du plateau en eliminant les combinaisons
}
// Declenchement nouvelle partie : parcours le dataStore si necessaire et remplit les cases
}
eclateGemmes.play = function() {
creerCaseSimple = function(indicator, indexY, indexX) {
// construit une case de presentation dans le plateau
}
trouverCaseCible = function(cellId) {
// Retourne id case cible si elle fait partie des deplacements autorises pour la case jouee
}
genererCase = function(cellId) {
// Construit une case deplacable sur la case selectionnee et gere les evenements associes
}
// Initialisation du plateau et des cases, puis ajout des listeners sur les cases
}
eclateGemmes.testPlateau = function() {
remplacerCase = function() {
// Efface la case et decale les valeurs depuis la case au dessus
}
checkCases = function(casesAVerifier) {
// Verifie si une combinaison de cases est correcte, et la resout si necessaire
}
trouverCasesAVerifVertical = function(caseAVerifier) {
// Trouve la combinaison de cases a tester horizontalement pour la cible
}
trouverCasesAVerifHorizontal = function(caseAVerifier) {
// Trouve la combinaison de cases a tester verticalement pour la cible
}
// Test l’ensemble des cases en combinaisons horizontales et verticales
}

Les points clés sont :
– des attributs clés qui peuvent être modifiés facilement pour agir sur le visuel : taille du plateau et durée des effets.
– les effets visuels dynamiques créant l’impression d’un déplacement vertical des cases.
– les actions permettant le « drag and drop » des cases avec contrôle du résultat du déplacement.
– l’usage de tags audio non affichés pour identifier les moments clés de la partie.
– le positionnement de certaines fonctions à l’intérieur des méthodes afin de réduire leur portée (je reviendrai dans un futur billet sur les bonnes pratiques javascript, certaines petites choses peuvent encore être améliorées dans les scripts proposés, celui-ci par exemple atteint une taille, plus de 500 lignes, qui le rend difficile à maintenir.)

define([ "dojo/dom", "dojo/on", "dojo/_base/window", "dojo/_base/fx", "dojo/_base/array",
         "dojo/_base/lang", "dojo/data/ItemFileReadStore", "dojo/number", "dojo/dnd/Moveable",
         "dojo/dnd/move", "dojo/dnd/Source", "dojo/dom-class", "dojo/dom-construct",
         "dojo/dom-style", "dojox/math/_base", "dojox/collections/ArrayList", "dijit/Menu",
         "dijit/MenuItem", "dojo/domReady!" ],
         function(dom, on, win, fx, array, lang, ItemFileReadStore, number, moveable, move,
	source, domClass, domConstruct, domStyle, math, ArrayList, menu, menuItem) {
	var eclateGemmes = {};

	eclateGemmes.cases = []; // cases du plateau
	eclateGemmes.values = []; // valeurs lues dans json
	eclateGemmes.NBR_CASE_X = 7;
	eclateGemmes.NBR_CASE_Y = 7;
	eclateGemmes.dureeEffets = 500; // 0.5 seconde
	eclateGemmes.effetsEnCours = new ArrayList([]); // id effet en cours
	eclateGemmes.coupsJoues = 0;
	eclateGemmes.points = 0;
	eclateGemmes.combi = 0;
	eclateGemmes.combiEnCours = 0;
	eclateGemmes.pointsEnCours = 0;
	eclateGemmes.largeurCase = 50;
	eclateGemmes.hauteurCase = 50;
	eclateGemmes.sonEclateCase = "eclateGemmes/audio/eclateCase.mp3";
	eclateGemmes.sonVictoire = "eclateGemmes/audio/victoire.mp3";
	eclateGemmes.data = "eclateGemmes/data/gemmes.json";

	/*
	 * Definition des cases jouables
	 */
	eclateGemmes.casesJouables = [];
	eclateGemmes.refreshCasesJouables = function(cellId) {
		var indexY = cellId.split('_')[0];
		var indexX = cellId.split('_')[1];
		// YX -> YX-1, YX+1, Y-1X, Y+1X
		var indexYMoins = parseInt(indexY, 10) - 1;
		var indexYPlus = parseInt(indexY, 10) + 1;
		var indexXMoins = parseInt(indexX, 10) - 1;
		var indexXPlus = parseInt(indexX, 10) + 1;
		eclateGemmes.casesJouables = [
          indexYMoins + "_" + indexX,
          indexYPlus + "_" + indexX,
          indexY + "_" + indexXMoins,
          indexY + "_" + indexXPlus ];
	};

	/*
	 * Definition de la zone maximum de deplacement des cases
	 */
	eclateGemmes.rafraichitScore = function() {
		dom.byId("idScore").firstChild.nodeValue = eclateGemmes.points;
		dom.byId("idCombinaisons").firstChild.nodeValue = eclateGemmes.combi;
		dom.byId("idCoups").firstChild.nodeValue = eclateGemmes.coupsJoues;
	};

	/*
	 * Definition de la zone maximum de deplacement des cases
	 */
	eclateGemmes.contraintesPlateau = function() {
		var mb2 = dojo.marginBox("plateau");
		var b = {};
		b.t = 0;
		b.l = 0;
		b.w = mb2.l + mb2.w + 20;
		b.h = mb2.h + mb2.t + 20;
		return b;
	};

	/*
	 * Retourne true si tous les effets en cours ont ete resolus
	 */
	eclateGemmes.isEffetsResolus = function() {
		var result = false;
		if (eclateGemmes.effetsEnCours.toArray().length === 0) {
			result = true;
		}
		return result;
	};

	/*
	 * Construit le contenu pour une case aleatoirement a partir de
	 * eclateGemmes.values
	 */
	eclateGemmes.creerGemmeAleatoire = function(caseEnCours) {
		// determination aleatoire de la gemme
		var random = math.gaussian();
		while (random < 0 || random > 1) {
			random = math.gaussian();
		}
		var dataGemmes = eclateGemmes.values[Math.floor(random * eclateGemmes.values.length)];

		// suppression du contenu de la case
		while (caseEnCours.firstChild) {
			caseEnCours.removeChild(caseEnCours.firstChild);
		}

		// insertion du contenu : image + id dans un span pour les tests de
		// valeurs
		var nodeId = win.doc.createElement("span");
		nodeId.appendChild(win.doc.createTextNode(dataGemmes.idGemme[0]));
		nodeId.style.cssText += "display:none;";
		caseEnCours.appendChild(nodeId);
		caseEnCours.style.cssText += "background-image:url(" + dataGemmes.img[0] + ");";
		caseEnCours.style.cssText += "background-repeat:no-repeat;";
		caseEnCours.style.cssText += "background-size: 100% 100%;";
	};

	/*
	 * Declenchement nouvelle partie
	 */
	eclateGemmes.startNewGame = function() {
		/*
		 * Remplit les cases du plateau en eliminant les combinaisons
		 */
		var remplitEtNettoie = function() {
			array.forEach(eclateGemmes.cases, function(caseEnCours) {
				eclateGemmes.creerGemmeAleatoire(caseEnCours);
			});
			eclateGemmes.testPlateau();
		};

		// reset scores
		eclateGemmes.coupsJoues = 0;
		eclateGemmes.points = 0;
		eclateGemmes.combi = 0;

		// parcours le dataStore si necessaire et remplit les cases
		if (eclateGemmes.values.length === 0) {
			// DataStore contenant les gemmes
			var dataeclateGemmes = new ItemFileReadStore({
				url : require.toUrl(eclateGemmes.data)
			});

			dataeclateGemmes.fetch({
				onItem : function(item) {
					eclateGemmes.values.push(item);
				},
				onComplete : function() {
					remplitEtNettoie();
				}
			});
		} else {
			remplitEtNettoie();
		}
	};

	/*
	 * Regenere le plateau et toutes ses cases, puis ajoute les listeners sur
	 * les cases
	 */
	eclateGemmes.play = function() {
		/*
		 * construit une case de presentation dans le plateau
		 */
		var creerCaseSimple = function(indicator, id) {
			var cell = win.doc.createElement("td");
			cell.width = cell.width = eclateGemmes.largeurCase;
			cell.width = cell.height = eclateGemmes.hauteurCase;
			cell.align = cell.valign = 'center';
			cell.indicator = indicator;
			cell.id = id;

			return cell;
		};

		/*
		 * Retourne id case cible si elle fait partie des deplacements autorises
		 * pour la case jouee
		 */
		var trouverCaseCible = function(cellId) {
			var idCaseCible = 0;
			var positionXY = dojo.position(cellId, true);

			// parcours des cases jouables pour retrouver par coordonnees XY
			array.forEach(eclateGemmes.casesJouables, function(IdCaseEnCours) {
				if (dom.byId(IdCaseEnCours)) {
					var positionEnCours = dojo.position(IdCaseEnCours, true);
					var positionEnCoursXMax = parseInt(positionEnCours.x + positionEnCours.h, 10);
					var positionEnCoursYMax = parseInt(positionEnCours.y + positionEnCours.w, 10);
					if (positionEnCours.x < positionXY.x
							&& positionXY.x < positionEnCoursXMax
							&& positionEnCours.y < positionXY.y
							&& positionXY.y < positionEnCoursYMax) {
						idCaseCible = IdCaseEnCours;
					}
				}
			});

			return idCaseCible;
		};

		/*
		 * Construit une case deplacable sur la case selectionnee et gere les
		 * evenements associes
		 */
		var genererCase = function(cellId) {
			var movingCell = new move.constrainedMoveable(cellId, {
				handle : "movingCellHandler1",
				constraints : eclateGemmes.contraintesPlateau,
				within : true
			});

			movingCell.onMoveStart = function(mover) {
				// mouvements interdits pendants la resolution des effets
				if (eclateGemmes.isEffetsResolus()) {
					// determination des cases jouables
					eclateGemmes.refreshCasesJouables(cellId);
				} else {
					// pas de case jouable, pas de mouvement
					eclateGemmes.casesJouables = [];
				}
			};

			movingCell.onMoveStop = function(mover) {
				// la case jouee va soit remplacer la cible, soit revenir en
				// place
				var caseSource = dom.byId(cellId);

				// test si deplacement ok
				var idCaseIdentifiee = trouverCaseCible(cellId);
				if (idCaseIdentifiee !== 0) {
					// inversion des background
					var caseCible = dom.byId(idCaseIdentifiee);
					var styleTemp = domStyle.get(caseCible, "backgroundImage");
					domStyle.set(caseCible, "backgroundImage",
							domStyle.get(caseSource, "backgroundImage"));
					domStyle.set(caseSource, "backgroundImage", styleTemp);

					// inversion des valeurs
					caseSource.appendChild(caseCible.firstChild);
					caseCible.appendChild(caseSource.firstChild);

					// incrementation nombre de coups joues
					eclateGemmes.coupsJoues++;
					eclateGemmes.rafraichitScore();
				}

				// case jouee replacee correctement
				domStyle.set(caseSource, "position", "");
				domStyle.set(caseSource, "left", "");
				domStyle.set(caseSource, "top", "");

				// test les combinaisons sur le plateau
				eclateGemmes.testPlateau();
			};
		};

		// Creation du plateau de jeu
		var plateau = win.doc.createElement("table"), indicator = 1, row, cell, parent;
		plateau.id = "plateau";
		eclateGemmes.cases = [];

		// Creation cases simples et insertions dans plateau
		for (var indexY = 0; indexY < eclateGemmes.NBR_CASE_Y; indexY++) {
			row = win.doc.createElement("tr");
			plateau.appendChild(row);
			for (var indexX = 0; indexX < eclateGemmes.NBR_CASE_X; indexX++) {
				cell = creerCaseSimple(indicator, indexY + "_" + indexX);
				row.appendChild(cell);
				eclateGemmes.cases.push(cell);
				indicator += indicator;
			}
		}

		// Rattachement plateau au jeu
		parent = dom.byId("jeu") || win.doc.body;
		parent.innerHTML = ''; // nettoie jeu
		parent.appendChild(plateau);

		// Ajout de son d'ambiance sans controles
		var divAudio = win.doc.createElement("div");
		divAudio.style.cssText += "visibility:none";
		var tagAudioEclateCase = win.doc.createElement("audio");
		tagAudioEclateCase.controls = false; // affiche pas les controles du
		// son
		tagAudioEclateCase.play(); // joue le morceau
		tagAudioEclateCase.loop = false; // en boucle
		tagAudioEclateCase.id = "tagAudioEclateCase";
		tagAudioEclateCase.src = eclateGemmes.sonEclateCase;
		divAudio.appendChild(tagAudioEclateCase);
		var tagAudioVictoire = win.doc.createElement("audio");
		tagAudioVictoire.controls = false; // affiche pas les controles du son
		tagAudioVictoire.play(); // joue le morceau
		tagAudioVictoire.loop = false; // en boucle
		tagAudioVictoire.id = "tagAudioVictoire";
		tagAudioVictoire.src = eclateGemmes.sonVictoire;
		divAudio.appendChild(tagAudioVictoire);
		domConstruct.place(divAudio, dom.byId("jeu"), "after");

		// Creation du tableau de score
		var scoreBoard = win.doc.createElement("table");
		var widthPlateau = domStyle.get(dom.byId("plateau"), "width");
		scoreBoard.border = 1;
		indicator = 1;
		var flagCouleur = 1;
		for (i = 0; i < 2; i += 1) {
			row = win.doc.createElement("tr");
			scoreBoard.appendChild(row);
			for (var j = 0; j < 3; j += 1) {
				cell = creerCaseSimple(indicator, '');
				cell.width = (widthPlateau - 20) / 3;
				cell.appendChild(win.doc.createTextNode(""));
				var textNode = cell.firstChild;
				if (j === 0) {
					if (i === 0) {
						textNode.nodeValue = "Score";
						cell.id = "idScoreLabel";
					} else {
						textNode.nodeValue = "0";
						cell.id = "idScore";
					}
				} else if (j === 1) {
					if (i === 0) {
						textNode.nodeValue = "Combinaisons";
						cell.id = "idCombinaisonsLabel";
					} else {
						textNode.nodeValue = "0";
						cell.id = "idCombinaisons";
					}
				} else if (j === 2) {
					if (i === 0) {
						textNode.nodeValue = "Coups";
						cell.id = "idCoupsLabel";
					} else {
						textNode.nodeValue = "0";
						cell.id = "idCoups";
					}
				}
				// Ajout de style sur les cases
				if ((flagCouleur % 2) === 0) {
					domClass.add(cell, "classA");
				} else {
					domClass.add(cell, "classB");
				}
				cell.appendChild(textNode);
				row.appendChild(cell);
				indicator += indicator;
				flagCouleur++;
			}
		}

		// Rattachement scores au jeu
		parent.appendChild(scoreBoard);
		domConstruct.place(scoreBoard, parent.firstChild, "before");

		// Ajout des actions sur chaque case
		array.forEach(eclateGemmes.cases, function(caseEnCours) {
			on(dom.byId(caseEnCours.id), "click", genererCase(caseEnCours.id));
		});

		// Lancement de la partie
		eclateGemmes.startNewGame();
	};

	/*
	 * Test l'ensemble des cases en combinaison horizontal et vertical
	 */
	eclateGemmes.testPlateau = function() {
		/*
		 * Efface la case et decale les valeurs depuis la case au dessus
		 */
		var remplacerCase = function(caseARemplacerCible) {
			// soit la source existe, soit il faut generer une nouvelle valeur
			// effet de disparition
			fx.fadeOut({
				node : caseARemplacerCible,
				duration : eclateGemmes.dureeEffets,
				beforeBegin : function() {
					// marquage de la case en cours effets
					eclateGemmes.effetsEnCours.add(caseARemplacerCible.id);
				},
				onEnd : function() {
					// effet de reapparition
					fx.fadeIn({
						node : caseARemplacerCible,
						duration : eclateGemmes.dureeEffets,
						onEnd : function() {
							// suppression marquage effets sur case en cours
							eclateGemmes.effetsEnCours.remove(caseARemplacerCible.id);
							if (eclateGemmes.isEffetsResolus()) {
								// incrementation points en cours
								eclateGemmes.pointsEnCours++;
								eclateGemmes.pointsEnCours +=
									eclateGemmes.pointsEnCours * eclateGemmes.combiEnCours - 1;
								eclateGemmes.combi += eclateGemmes.combiEnCours;
								eclateGemmes.combiEnCours = 0;
								eclateGemmes.rafraichitScore();

								// si dernier effet en cours, retest plateau
								eclateGemmes.testPlateau();
							}
						}
					}).play();

					// determination de la source
					var idARemplacerYCible = caseARemplacerCible.id.split('_')[0];
					var idARemplacerXCible = caseARemplacerCible.id.split('_')[1];
					var idARemplacerYSource = parseInt(idARemplacerYCible, 10) - 1;
					var caseARemplacerSource = dom.byId(
							idARemplacerYSource + "_" + idARemplacerXCible);
					if (caseARemplacerSource) {
						// inverse les background de deux cases
						domStyle.set(caseARemplacerCible, "backgroundColor",
								domStyle.get(caseARemplacerSource, "backgroundColor"));
						domStyle.set(caseARemplacerCible, "backgroundImage",
								domStyle.get(caseARemplacerSource, "backgroundImage"));

						// si case source trouvee -> decalage de valeur
						caseARemplacerCible.innerHTML = caseARemplacerSource.innerHTML;

						// appel recursif pour determination nouvelle valeur de
						// caseARemplacerSource
						remplacerCase(caseARemplacerSource);
					} else {
						// sinon generation nouvelle valeur
						eclateGemmes.creerGemmeAleatoire(caseARemplacerCible);
					}
				}
			}).play();
		};

		/*
		 * Verifie si une combinaison de cases est correcte, et la resout si
		 * necessaire
		 */
		var checkCases = function(casesAVerifier) {
			if (casesAVerifier[0] && casesAVerifier[1] && casesAVerifier[2]) {
				var valueAVerifier = casesAVerifier[0].firstChild.firstChild.nodeValue;
				var valueAVerifier1 = casesAVerifier[1].firstChild.firstChild.nodeValue;
				var valueAVerifier2 = casesAVerifier[2].firstChild.firstChild.nodeValue;
				if (valueAVerifier === valueAVerifier1
						&& valueAVerifier1 === valueAVerifier2) {
					// incrementation nombre de combi trouvees
					eclateGemmes.combiEnCours++;

					// petit son eclate case
					dom.byId("tagAudioEclateCase").play();

					// remplacement des cases a eclater
					remplacerCase(casesAVerifier[0]);
					remplacerCase(casesAVerifier[1]);
					remplacerCase(casesAVerifier[2]);
				}
			}
		};

		/*
		 * Trouve la combinaison de cases a tester horizontalement pour la cible
		 */
		var trouverCasesAVerifVertical = function(caseAVerifier) {
			var casesAVerifier = [];
			var idAVerifierY = caseAVerifier.id.split('_')[0];
			var idAVerifierX = caseAVerifier.id.split('_')[1];
			var idAVerifierX1 = parseInt(idAVerifierX, 10) + 1;
			var idAVerifierX2 = parseInt(idAVerifierX, 10) + 2;
			var caseAVerifier1 = dom.byId(idAVerifierY + "_" + idAVerifierX1);
			var caseAVerifier2 = dom.byId(idAVerifierY + "_" + idAVerifierX2);

			casesAVerifier.push(caseAVerifier);
			casesAVerifier.push(caseAVerifier1);
			casesAVerifier.push(caseAVerifier2);

			return casesAVerifier;
		};

		/*
		 * Trouve la combinaison de cases a tester verticalement pour la cible
		 */
		var trouverCasesAVerifHorizontal = function(caseAVerifier) {
			var casesAVerifier = [];
			var idAVerifierY = caseAVerifier.id.split('_')[0];
			var idAVerifierX = caseAVerifier.id.split('_')[1];
			var idAVerifierY1 = parseInt(idAVerifierY, 10) + 1;
			var idAVerifierY2 = parseInt(idAVerifierY, 10) + 2;
			var caseAVerifier1 = dom.byId(idAVerifierY1 + "_" + idAVerifierX);
			var caseAVerifier2 = dom.byId(idAVerifierY2 + "_" + idAVerifierX);

			casesAVerifier.push(caseAVerifier);
			casesAVerifier.push(caseAVerifier1);
			casesAVerifier.push(caseAVerifier2);

			return casesAVerifier;
		};

		// verifie les combi possibles pour chaque case
		array.forEach(eclateGemmes.cases, function(caseEnCours) {
			checkCases(trouverCasesAVerifVertical(caseEnCours));
			checkCases(trouverCasesAVerifHorizontal(caseEnCours));
		});

		// si tout a ete traite, incrementation points, refresh score
		if (eclateGemmes.isEffetsResolus()) {
			// incrementation point en cours
			eclateGemmes.points += eclateGemmes.pointsEnCours;
			eclateGemmes.pointsEnCours = 0; // reset pointsEnCours
			if (eclateGemmes.coupsJoues === 0) {
				eclateGemmes.points = 0;
				eclateGemmes.combi = 0;
				eclateGemmes.combiEnCours = 0;
				eclateGemmes.pointsEnCours = 0;
			} else if (eclateGemmes.coupsJoues === 10 || eclateGemmes.coupsJoues === 20
					|| eclateGemmes.coupsJoues === 50
					|| eclateGemmes.coupsJoues === 100) {
				// quelques statistiques
				moyPts = eclateGemmes.points / eclateGemmes.coupsJoues;
				moyCombi = eclateGemmes.combi / eclateGemmes.coupsJoues;
				dom.byId("tagAudioVictoire").play();
				alert("Vous avez joue "
						+ eclateGemmes.coupsJoues + " coups pour une moyenne par coup de "
						+ moyPts + " points et "
						+ moyCombi + " combinaisons");
			}
			eclateGemmes.rafraichitScore();
		}
	};

	return eclateGemmes;
});

Le fichier gemmes.json

Enfin, le datastore, gemmes.json, contient les objets à afficher. Pour des raisons de facilité, j’ai utilisé ici des icônes tirés du thème « tundra » de la bibliothèque Dijit.

{'items' : [
		{	
			'idGemme' : '0',
			'img' : 'http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/tundra/images/dndNoCopy.png'
		},
		{	
			'idGemme' : '1',
			'img' : 'http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/tundra/images/dndNoMove.png'
		},
		{	
			'idGemme' : '2',
			'img' : 'http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/tundra/images/dndCopy.png'
		},
		{	
			'idGemme' : '3',
			'img' : 'http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/tundra/images/dndMove.png'
		},
		{	
			'idGemme' : '4',
			'img' : 'http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dijit/themes/nihilo/images/no.gif'
		}
	]
}

Conclusion

Si ici l’exemple se limite à un jeu simple, le potentiel de ces quelques effets pour l’enrichissement d’interfaces web est évident. Qu’il s’agisse de jeux ou d’applications de gestion, il est facile d’imaginer des solutions à l’interactivité renforcée pour lesquelles chaque utilisateur aurait son propre arrangement des fonctionnalités et donc sa propre expérience applicative. Un petit bémol néanmoins, après quelques tests, je me rends compte que la compatibilité avec les différents navigateurs web est assez variable. J’essaierai de creuser la question et d’y apporter quelques solutions dans un futur billet…

Laisser un commentaire

Aucun commentaire pour le moment

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *


*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>