Tuto 7 : Décors et scrolling

Une tilemap ( = carte de tuiles ) est une grille sur laquelle on place des images, et qui représente un décor ou un fond d'écran. Une tuile fait 8 x 8 pixels.

Une tilemap de Pyxel à une taille de 256 x 256 tuiles ( soit 2048 x 2048 pixels ), ce qui est donc généralement beaucoup plus grand que la taille de la fenêtre de jeu, permettant ainsi un déplacement ou un scrolling du décor en n'affichant qu'une partie de la tilemap.

7 tilemaps ( numérotées de 0 à 7 ) différentes peuvent être utilisées, permettant des jeux se déroulant sur plusieurs niveaux.

Édition et création d'une tilemap

L'utilisation de l'éditeur Pyxel permet d'afficher et/ou de créer/modifier une tilemap; les tuiles utilisées sont les images stockées dans une des 3 banques d'images disponibles.

Editeur de tilemap

Affichage d'une tilemap

Après avoir bien entendu charger la ressource contenant la tilemap, le principe est le même que pour l'affichage d'une image seule, mais la méthode à utiliser est pyxel.bltm() :


pyxel.bltm(x, y, numéro tilemap, x(tilemap), y(tilemap), taille horizontale, taille verticale, [couleur de transparence])
			

(x, y) représentent là aussi la coordonnée du pixel supérieur gauche de la fenêtre à partir duquel s'affichera la ( portion de ) tilemap.

x(tilemap) et y(tilemap) représentent les coordonnées ( en pixels ), dans la tilemap, du pixel situé dans le coin supérieur gauche de la zone à afficher.

Les tailles horizontales et verticales représentent alors le nombre de pixels horizontaux/verticaux de la tilemap à afficher.

Exemple : pour afficher un fond étoilé à partir de la tilemap présente dans le fichier ressource ressources.pyxres :


pyxel.bltm(0, 0, 0, 192, 0, 128, 128)
			

Où placer cette instruction dans le script de notre jeu ? Compléter ce script.

On peut bien entendu superposer les portions de tilemap à afficher pour donner une illusion de profondeur ( une portion fixe et une défilante par exemple, voir ci-dessous ).

Scrolling d'un décor

Le scrolling d'un décor est le fait de le faire défiler, verticalement ou horizontalement.

Le principe que l'on va utiliser pour le scrolling d'un décor est le même que celui utilisé pour animer les images : on "parcourt" une zone donnée de la tilemap où se trouve plusieurs "images" successives et "décalées" du décor, de façon à n'en afficher qu'une portion.

Scrolling automatique

Ce parcours peut se faire "automatiquement", en utilisant une variable qui sera modifiée "en boucle" à chaque frame.

Par exemple, pour animer le fond étoilé de façon à le faire défiler vers le bas de la fenêtre, simulant ainsi le déplacement du vaisseau, on peut utiliser la zone de la tilemap du fichier ressources.pyxres située entre les pixels (192, 0) et (192, 192); en modifiant la coordonnée verticale y(tilemap) pour la faire évoluer en boucle entre 0 et 192, on "balayera" ainsi cette zone pour simuler le déplacement apparent des étoiles :


pyxel.bltm(0, 0, 0, 192 - pyxel.frame_count % 192, 0, 128, 128)
			

L'utilisation de l'opérateur modulo %, qui calcule le reste d'une division, permet à la valeur pixel.frame_count % 192 d'évoluer entre 0 et 192 ( 192 - pixel.frame_count % 192 évoluera donc entre 192 et 0 ), en fonction du nombre de frames écoulées; cette formule peut être ajustée pour moduler la vitesse du scrolling.

Modifier l'instruction dans le script pour réaliser le scrolling du décor. Modifier la formule pour expérimenter des vitesses de défilement différentes.

Rajouter un autre décor, qui fait défiler des météorites ( voir dans l'éditeur quelle portion de la tilemap utiliser ), et lui donner une vitesse de scrolling différente.

Scrolling commandé

Le défilement du décor peut également se faire en fonction, par exemple des déplacements d'un personnage : à tout instant, on n'affichera une portion de la tilemap, portion qui sera décalée dans la direction de déplacement du personnage à chaque fois que celui-ci bougera.

Voila par exemple un script qui fait défiler la tilemap n°1 dans le fichier ressources.pyxres, en utilisant les touches de direction :


import pyxel

class Jeu:

    def __init__(self, h, l, titre):

        pyxel.init(h, l, title = titre)

        self.x = 0
        pyxel.load("ressources.pyxres")
        # Lancement du jeu
        pyxel.run(self.update, self.draw)

    def update(self):
        """déplacement avec les touches de direction"""

        if pyxel.btn(pyxel.KEY_RIGHT):
            if (self.x < 1024) :
                self.x = self.x + 5
        if pyxel.btn(pyxel.KEY_LEFT):
            if (self.x > 0) :
                self.x = self.x - 5


    def draw(self):

        pyxel.cls(0)

        pyxel.bltm(0, 0, 1, self.x, 0, 128, 128)

########################
#  PROGRAMME PRINCIPAL #
########################

Jeu(128, 128, "Super Jeu")
			

Interactions avec le décor

Comme pour les interactions entre les sprites, il est possible selon le même principe de gérer les interactions entre les sprites et les tuiles de la tilemap.

Deux problèmes à gérer

Un premier problème à gérer est que les sprites et la tilemap n'utilisent pas le même système de coordonnées :

Pour savoir si un sprite donné se trouve sur une tuile données, il faudra donc passer d'un système de repérage à l'autre, c'est à dire utiliser les relations suivantes :

Le deuxième problème est qu'un sprite peut dans le pire des cas être "à cheval" sur 4 tuiles !

Il faudra donc déterminer les 4 tuiles de la tilemap aux coordonnées (tuile_x, tuile_y), (tuile_x + 1, tuile_y), (tuile_x, tuile_y + 1) et (tuile_x + 1, tuile_y + 1).
Interaction sprite/tuiles

Identification d'une tuile

On identifie alors une tuile grâce à la méthode pget() :


			pyxel.tilemap(numéro de tilemap).pget(tuile_x, tuile_y)		
					

Cette méthode renvoie alors un tuple (x, y) correspondant aux coordonnées (colonne, ligne) dans la banque d'images sélectionnées de la tuile.

Par exemple :


		pyxel.tilemap(0).pget(tuile_x, tuile_y)
					

renverrait le tuple (0, 0) si un sprite se trouvait sur une tuile correspondant à une image située en (0, 0) dans la banque d'image.

Coordonnées tuiles

Un code possible


# Transformation des coordonnées sprite -> tilemap
tuile_x = x // 8
tuile_y = y // 8

# Parcours des 4 tuiles à tester
for i in range(tuile_y, tuile_y + 2):
	for j in range(tuile_x, tuile_x + 2):
		if pyxel.tilemap(0).pget(j, i) == TUILE_A_TESTER:
			...
			...
			pyxel.tilemap(0).pset(j, i) == TUILE_VIDE
			

TUILE_A_TESTER contient le tuple identifiant la tuile "objectif" dans la banque d'images.

La dernière instruction permet, au besoin, "d'effacer" la tuile de la tilemap; là aussi, TUILE_VIDE est le tuple identifiant une tuile vide dans la banque d'images.