Mis à jour le mardi 1 mars 2016
  • Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Gestion des tilesets

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

Dans cette partie, nous allons voir ce qu'est un tileset, à quoi ca sert et comment l'utiliser dans notre RPG.

Présentation

Un tileset, c'est quoi ?

Dans un RPG, une carte (map) est constituée de cases que l'on nomme généralement tiles (tuiles en français). C'est en plaçant plusieurs tiles les uns à côté des autres que l'on va générer notre monde.

Chaque tile est une image carrée. On utilise généralement une taille de 32 x 32 pixels.
Dans ce tuto, c'est également la taille que j'utiliserai car vous pourrez trouver une énorme quantité de ressources graphiques libres sur internet basées sur ce modèle (pour ceux qui connaissent RPG Maker, sachez que c'est le même format).

Si vous avez une carte très basique avec une étendue d'herbe et un chemin qui la traverse, vous aurez globalement deux tiles différents : un avec une texture d'herbe et un avec une texture de chemin. Dans la pratique c'est un peu plus compliqué. On utilisera généralement une dizaine de tiles afin de gérer les bordures du chemin (sinon il sera totalement carré, ce qui n'est pas très beau).

Pourquoi un tel système ?

La première chose qui nous vient à l'esprit quand on a pas l'expérience du développement de jeux en 2D est de tout simplement avoir un fichier image de 32 x 32 pixels par tile.

L'objectif du tileset est de combiner plusieurs tiles sur une seule image. Ainsi, même si cela n'a pas d'impact en terme de taille, ça en aura en performances car il n'y aura qu'une seule image à charger.

Pour cette partie, nous allons utiliser un tileset très simple qui est celui-ci :

Tileset de base

Vous comprenez mieux ce qu'est un tileset à présent ?

Mais comment allons-nous l'utiliser dans notre jeu puisqu'il n'est pas découpé ?

Vous vous souvenez de la méthode drawImage de l'objet context vu dans la partie précédente ? Nous avons vu qu'elle pouvait nous permettre de ne dessiner qu'une partie de l'image en sélectionnant un "rectangle" source par ses coordonnées (x, y) et par sa taille.

C'est ce que nous allons maintenant étudier.

Découpage et utilisation

Structure de la classe

Avant de commencer le codage du côté des tilesets, nous allons-nous mettre d'accord sur la manière de les appeler. Comment allons nous choisir de dessiner tel ou tel tile ? Plusieurs solutions se présentent à nous. La première qui nous vient à l'esprit (et qui est la plus facile à calculer) est de les appeler par des coordonnées (x, y), x et y étant non pas des pixels mais des tiles.

Ce n'est pas la convention de numérotation que j'ai choisie. J'ai choisi de n'utiliser qu'un seul entier. Ce sera moins simple à calculer, mais ça nous simplifiera la vie plus tard. Voici la manière dont ce sera numéroté pour notre tileset :

Tileset de base numéroté

Bien. Maintenant commençons un peu à coder.

Premièrement, enregistrez l'image de tileset dans le répertoire "tilesets" que nous avons créé dans la première partie. Je l'ai nommée "basique.png". Si vous choisissez un nom différent, pensez bien à reporter vos modifications dans le code.

Programme de test

Reprenez le fichier rpg.js et faites un peu de ménage (si vous avez fait des tests que vous souhaitez garder, n'oubliez pas de les sauvegarder dans un coin) :

window.onload = function() {
	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');

	// Nous allons insérer nos tests ici
}

Pour gérer nos tilesets, nous allons utiliser une classe. Nous aurons besoin des méthodes suivantes :

  • Constructeur(String)

  • dessinerTile(int, Context, int, int)

Le constructeur prendra en paramètres le nom de l'image contenant le tileset.
La méthode dessinerTile nous permettra de facilement dessiner un tile (dont le numéro est passé en premier paramètre) sur un context passé en second paramètre, aux coordonnées x et y (troisième et quatrième paramètre).

Maintenant que nous avons défini notre classe, nous allons mettre en place nos tests, comme ça ce sera fait. Remplacez le commentaire que j'ai laissé à cet effet dans rpg.js par le code suivant :

ts.dessinerTile(1, ctx, 10, 10);
ts.dessinerTile(5, ctx, 50, 10);
ts.dessinerTile(6, ctx, 90, 10);
ts.dessinerTile(7, ctx, 130, 10);

Et ajoutez la ligne suivante tout en haut (en dehors de la fonction) :

var ts = new Tileset("basique.png");

Vous l'aurez sans doute compris, notre test va dessiner côte à côte les tiles numéros 1, 5, 6 et 7. Ce jeu d'essai teste principalement les "coins" du tileset, car c'est souvent les coins qui posent problème.

Création de notre classe

Nous allons maintenant passer au codage de notre classe. Créez donc un fichier Tileset.js dans le dossier js/classes, et liez-le à votre fichier HTML :

<script type="text/javascript" src="js/classes/Tileset.js"></script>

Nous pouvons dès lors poser les bases de notre classe sans trop de difficulté :

function Tileset(url) {
	// Chargement de l'image dans l'attribut image
	this.image = new Image();
	this.image.referenceDuTileset = this;
	this.image.onload = function() {
		if(!this.complete) 
			throw new Error("Erreur de chargement du tileset nommé \"" + url + "\".");
	}
	this.image.src = "tilesets/" + url;
}

// Méthode de dessin du tile numéro "numero" dans le contexte 2D "context" aux coordonnées x et y
Tileset.prototype.dessinerTile = function(numero, context, xDestination, yDestination) {
	
}

Dans le constructeur, vous voyez qu'on ajoute un attribut spécial à notre objet image afin qu'il sache à quel objet Tileset il appartient. Ainsi, nous pourrons terminer le constructeur de notre Tileset de manière asynchrone, c'est à dire quand l'image sera chargée.

Dessin d'un tile

Ici, le constructeur n'est pas terminé. Pour pouvoir déterminer les coordonnées (x, y) d'un tile précis dans le tileset à partir de son numéro, nous allons avoir besoin de connaître la largeur en tiles de notre tileset. Il faut donc ajouter le code suivant dans la fonction onload de l'image :

// Largeur du tileset en tiles
this.referenceDuTileset.largeur = this.width / 32;

Nous allons pouvoir maintenant attaquer notre méthode de dessin.
Voici un tableau récapitulatif des paramètres dont nous avons besoin pour utiliser la méthode drawImage du context, ainsi que leur description et leur valeur si elle est déjà connue :

Paramètre

Description

Valeur

image

L'image source

this.image

sx

Coordonnée x du tile dans le tileset

???

sy

Coordonnée y du tile dans le tileset

???

sw

Largeur de l'image source

32

sh

Hauteur de l'image source

32

dx

Coordonnée x de destination

xDestination

dy

Coordonnée y de destination

yDestination

dw

Largeur de l'image a dessiner

32

dh

Hauteur de l'image a dessiner

32

Nous avons donc à peu près ce qu'il nous faut, sauf les coordonnées x et y du tile demandé dans le tileset. Nous allons donc déterminer sa position (x, y), en nombre de tiles.

Tout d'abord, la coordonnée x :

var xSourceEnTiles = numero % this.largeur;
if(xSourceEnTiles == 0) xSourceEnTiles = this.largeur;

Ici, il n'y a que des maths. On prend le reste de la division entière du numéro du tile par la largeur (en tiles) du tileset. On obtient alors le numéro de la colonne où est situé le tile. Par contre si on obtient 0, c'est qu'on est sur un tile tout à droite du tileset.

Pour la coordonnée y :

var ySourceEnTiles = Math.ceil(numero / this.largeur);

Ici on divise le numéro demandé par la largeur en tiles. Si nous obtenons un nombre entier, pas de problème. Dans le cas contraire c'est que nous ne sommes plus sur la ligne indiquée par la partie entière, mais sur la ligne suivante.

Maintenant, c'est plutôt facile :

var xSource = (xSourceEnTiles - 1) * 32;
var ySource = (ySourceEnTiles - 1) * 32;

Il ne nous reste plus qu'à utiliser la méthode de dessin :

context.drawImage(this.image, xSource, ySource, 32, 32, xDestination, yDestination, 32, 32);

Si tout va bien vous devriez obtenir ceci, qui correspond bien a nos attentes :

Résultat de l'affichage des tiles 1, 5, 6 et 7

Nous sommes maintenant capables d'afficher facilement n'importe quel tile de notre tileset !

Vous verrez que ca sera bien plus facile pour afficher notre carte dans la partie suivante.

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