Le mapping

A la conquête du jeu vidéo partie 1

Start

Et voilà, c’est parti, je me lance dans cette folle aventure qu’est le développement d’un jeu et j’ai décidé de commencer par le « mapping ». Alors à vrai dire, je ne sais pas si c’est le terme exact. En tout cas là mon but, c’est d’attaquer par la carte/map du jeu. Pour moi c’est essentiel car tout va partir de là, les mécaniques, les déplacements, les animations, etc.. En plus, en commençant par la map, je vais déjà pouvoir réfléchir à la structure du code HTML et CSS des différents éléments du jeu.

La grille

Je me pose déjà plusieurs questions par rapport à la construction de cette map. Quelle technique et quel élément HTML je vais utiliser ? J’avais eu l’idée de partir sur un tableau en HTML, étant donné que tout le jeu repose sur une grille. Mais je pense qu’il ne sera pas assez souple, je viens de me rendre compte qu’il faudra sans doute superposer plusieurs grilles, peut-être une avec les décors et une autre avec les unités mobiles par dessus. Je veux également avoir la possibilité de modifier facilement la taille des carrés de la map, au cas où il fraudait rendre le jeu plus accessible. J’ai donc décidé de partir sur une structure en div car ça me parait plus souple et moins contraignant. J’ai utilisé des classes tr et td afin d’avoir une bonne visualisation des lignes et des colonnes. Les td sont placés en « float:left; » histoire de les avoir sur la même ligne.

<div id="map">
	<div class="tr">
		<div class="td"></div>
		<div class="td"></div>
	</div>
	<div class="tr">
		<div class="td"></div>
		<div class="td"></div>
	</div>
</div>

J’ai ajouté un background-image à certaines div avec des éléments que j’ai pu récupérer sur le web et le rendu est bien. Enfin, c’est un rendu qui marche quoi…

Sprite

Comme je vais utiliser plusieurs dizaines d’images pour ajouter des décors à la map, j’ai décidé d’utiliser un sprite. Alors non, je ne parle pas du soda (c’est pas encore l’heure de boire) mais bel et bien d’un terme technique utilisé pour le web. Le sprite, c’est une image qui contient plusieurs autres images, en général petites. Il est intéressant de l’utiliser quand il y a beaucoup d’images, notamment pour optimiser le temps de chargement des pages. Pour obtenir l’image que je veux, l’idée c’est d’utiliser les propriétés css pour n’afficher que la partie dont j’ai besoin.

Et là je galère déjà ! Après plusieurs heures de recherche, je suis enfin tombé sur un fichier qui contient tous les éléments graphiques du jeu, ou du moins, une grosse partie. J’ai également trouvé un logiciel en ligne qui permet de construire des maps assez facilement, et ça c’est chouette, car si je suis bloqué je pourrais toujours jeter un coup d’œil sur le projet du logiciel. J’ai aussi eu la chance de trouver une communauté de fans fort sympathiques qui ont documenté plein de trucs sur Advance Wars, comme les règles du jeu, les déplacements des unités, la liste des généraux, bref, une mine d’or pour la suite !

Voici donc le Saint Graal du mapping, j’ai nommé le sprite.

Je remercie le fondateur de advancewarsmaps.com de m’avoir autorisé à utiliser ses sprites.

J’ai eu l’idée d’utiliser un tableau PHP en deux dimensions pour générer ma carte plus facilement. Chaque tableau représente une ligne et chaque valeur une colonne. La valeur correspond à une pièce dans le sprite, ici j’ai choisi des valeurs numériques pour simplifier le boulot, 1 vaut une plaine, 2 une montage, 3 la mer, etc… J’encode ces nombres sous trois « bits » étant donné qu’il y aura plus de cent pièces dans le sprite. Grâce à ça, j’aurais la possibilité d’avoir un maximum de 999 pièces, ça me parait largement suffisant pour un début.

//map ou chaque nombre représente une pièce particulière
$map = array(
	0 => array("003","003","003","003","003"),
	1 => array("003","001","001","001","003"),
	2 => array("003","001","001","001","003"),
	3 => array("003","001","001","001","003"),
	4 => array("003","003","003","003","003"),
);

Pour afficher la map plus facilement, je boucle dans ce tableau avec deux for imbriqués et je place une classe spécifique sur chaque div (pour la mer ça sera par exemple la classe « map-003 »). Cela va me permettre d’afficher l’image correspondant à chaque ID dans le tableau.

//largeur et hauteur de ma map
$largeur = 5;
$hauteur = 5;

//boucle dans le tableau pour affiché le bon code html
for($tmp_hauteur = 0; $tmp_hauteur < $hauteur; $tmp_hauteur++){
	echo '<div class="tr">';
	for($tmp_largeur = 0; $tmp_largeur < $largeur; $tmp_largeur++){
		echo '<div class="td map-'.$map[$tmp_hauteur][$tmp_largeur].'"></div>';	
	}
	echo '</div>';
}	

Et bien sûr la touche finale, un peu de CSS pour dire que telle ID doit être tel élément dans le sprite. Ici je me sers de deux propriétés, le background et le background-position.

#map .td{
  width: 32px;
  height: 32px;
  background: url("/img/sprite.png") transparent center center no-repeat;
  background-size: 512px 512px;
}
#map .td.map-001{
  background-position: 0px 0px;
}
#map .td.map-002{
  background-position: -32px 0px;
}
#map .td.map-003{
  background-position: -64px 0px;
}

Voila un exemple que j’obtiens avec une map un poil plus grand.

Le souci du détail

J’ai pensé que c’était tout bête de faire une map et cela fait déjà plusieurs heures que je cherche à avoir une petite île (j’essaye de refaire une des premières map du jeu), et ce n’est pas évident ! En fait il y a plein de pièces comme des angles ou des coins et sans eux, le rendu est dégueulasse. Ce qui est difficile aussi, c’est de chercher dans le sprite les bons éléments et de calculer leur position, comme ils sont tous collés, ce n’est pas facile. Je commence à comprendre l’intérêt d’un éditeur de carte.

Débordement

Bon là il y a un hic, je viens de jeter un coup d’œil aux différents éléments de mon sprite avant de me lancer à corps perdu dans la création d’une map et je me suis aperçu qu’il y a des éléments qui ne sont pas carrés, certaines pièces sont deux fois plus grandes que les casses de ma grille, comme les villes par exemple !

Mais en même temps, je viens d’avoir une super idée ! Je vais mettre dans chaque div.td une autre div qui aura la classe bg et la particularité de ce bloc, c’est qu’elle sera placée en position absolue, collée en bas et à gauche de son parent, comme ça je peux augmenter sa taille en hauteur assez facilement.

J’ai illustré volontairement le problème en image avec un avant/après.

Mais maintenant, si j’ajoute une route juste au dessus de ma tour par exemple ? La route vient par dessus… J’ai donc ajouté des z-index à chaque cellule en donnant la priorité aux éléments positionnés en dessous, pour chaque ligne de haut en bas, il y a un z-index supérieur, et également pour les colonnes de gauche à droite, mais cela a peu d’importance car aucune pièce n’est plus large qu’une cellule, mais dans la version 2 du jeu, c’est le cas, j’ai donc préféré gérer ce cas.

Constructeur de map

Je me rends compte que la création d’une map est fastidieuse et prend énormément de temps. J’ai donc décidé de développer un petit utilitaire assez simple pour générer ma carte.

Mon idée est la suivante, je dispose à gauche l’ensemble des pièces et à droite une map. Ensuite, je sélectionne une de ces pièces que je vais placer sur la map.

Step 1, préparer le tableau qui va contenir toutes les informations sur les pièces. Encore une fois, c’est un tableau à deux dimensions. La clé étant l’ID de la pièce et le contenu, un nouveau tableau reprenant ses propriétés (position x, position y, width et height).

Je vous épargne tout le tableau qui contient 169 éléments à l’heure oû j’écris ces lignes.

$map_decor = array(
	//ID	array(x,y,width,height)
	"001" => array(0   , 0   , 1, 1),
	"002" => array(-1  , 0   , 1, 1),
	"003" => array(-2  , 0   , 1, 1),
	"004" => array(-3  , 0   , 1, 1),
	"005" => array(-4  , 0   , 1, 1),
	"006" => array(-5  , 0   , 1, 1),
	"007" => array(-6  , 0   , 1, 1),
	"008" => array(-7  , 0   , 1, 1),
	"010" => array(-9  , 0   , 1, 1),
	"011" => array(-10 , 0   , 1, 1),
	"012" => array(-11 , 0   , 1, 1),
	"013" => array(-12 , 0   , 1, 1),
	"014" => array(-13 , 0   , 1, 1),
	"015" => array(-14 , 0   , 1, 1),
	"016" => array(-15 , 0   , 1, 1)
);

Ensuite je fais un foreach pour chaque élément du tableau et je construis une image, rien de bien sorcier. Je prends juste le soins d’ajouter une propriété data-id afin de pouvoir récupérer l’ID en JavaScript.

echo '<div class="panel" id="piece">';
foreach($map_decor as $key => $value){
	echo '<div data-id="'.$key.'" style="float:left; width:'.($value[2]*32).'px; height:'.($value[3]*32).'px; background:url(../img/sprite_v1.png) no-repeat left top transparent; background-size:512px 512px; background-position:'.($value[0]*32).'px '.($value[1]*32).'px;"></div>';
}
echo '<div style="clear:both;"></div></div>';

Je construis ensuite une map vierge de 15 cases sur 10.

<div id="map">
	<?php
		$largeur = 15;
		$hauteur = 10;
		
		$zindex = 0;
		
		for($tmp_hauteur = 0; $tmp_hauteur < $hauteur; $tmp_hauteur++){
			echo '<div class="tr">';
			for($tmp_largeur = 0; $tmp_largeur < $largeur; $tmp_largeur++){
				echo '<div class="td" style="z-index:'.$zindex.';"><div class="bg"></div></div>';	
				$zindex++;
			}
			echo '</div>';
		}	
	?>
</div>

Maintenant, un peu de JavaScript ! J’ai pris soins de charger jQuery pour aller plus vite.

J’ai défini une variable current_piece. Chaque fois que je vais cliquer sur l’image d’une pièce, je vais récupérer son attribut data-id et je vais le stocker. Ensuite, je crée un événement sur le clic d’une cellule de la carte et je lui ajoute la classe « map- » suivi de l’ID que j’ai récupérée.

Pour finir, j’ai ajouté un bouton export qui rempli un textarea avec le code que je dois mettre dans ma variable $map au niveau de mon code php. Pour le moment c’est une solution qui fera l’affaire.

$(function(){
	//piece courante
	var current_piece;
	
	$("#piece div").click(function(){
		$("#piece div").removeClass("active");
		$(this).addClass("active");
		current_piece = $(this).attr("data-id");
	});
	
	//met de l'herbe partour
	$("#map .td").addClass("map-022").attr("data-id","022");
	
	//click ajout classe pour ajout decor
	$("#map .td").click(function(){
		$(this).removeClass().addClass("td map-"+current_piece);
		$(this).attr("data-id",current_piece);
	});
	
	//export map
	$("#export").click(function(){
		$(".tr").each(function(key_ligne, val_ligne){
			$("#text_map").append(key_ligne + ' => array(');
			$(val_ligne).find(".td").each(function(key_col, val_col){
				$("#text_map").append('"' + $(val_col).attr("data-id") + '",');
			});
			$("#text_map").append('),\n');
		});
		return false;
	});
});

J’ai dû également définir tous les backgrounds dans le CSS. Pour faire simple, j’ai repris mon immense tableau (celui qui contient toutes les pièces) et je l’ai adapté et ajouté dans un fichier SASS afin de me faciliter la vie pour générer les backgrounds.

Grâce à SASS, je peux faire une boucle avec @each et c’est beaucoup plus facile. Je peux dynamiquement définir la position du background et je peux même détecter quand la pièce est deux fois plus haute, via @if, et modifier sa taille.

J’espère que tu me suis toujours, surtout pour une première partie, mais je suis obligé de travailler avec ce genre d’outil sinon le projet deviendrait vite ingérable.

$width_cellule:32px;
$height_cellule:32px;
$factor_cellule:32;

.bg{
	position: absolute;
	bottom: 0;
	left: 0;
	
	width: $width_cellule;
	height: $height_cellule;
	background: url("/img/sprite_v1.png") transparent center center no-repeat;
	background-size: 512px 512px;
	position: absolute;
	bottom: 0;
	left: 0;
}

//factor_cellue = 32
@each $value in $map {
	&.map-#{nth($value, 1)}{
		.bg{
			background-position: ($factor_cellule * nth($value, 2) * 1px)  ($factor_cellule * nth($value, 3) * 1px);
		}
		@if nth($value, 4) > 1 or nth($value, 5) > 1  {
			.bg{
				width: ($factor_cellule * nth($value, 4) * 1px);
				height: ($factor_cellule * nth($value, 5) * 1px);
			}
		}
	}
}

J’ai finalement construit ma map avec beaucoup de facilité, même si chercher les pièces reste toujours aussi chiant, mais c’est super cool d’avoir réussi à faire quelque chose de fidèle au jeu.

10/04/2016

Yann Vangampelaere - nouslesdevs -

Sinon jete un coup d’oeil aux autres catégories

Ma boîte aux lettres

Tu veux me demander quelque chose ?