Initiation à la création de jeu rétro avec Pyxel

Pyxel

Pyxel permet de créer en Python des jeux rendant hommage aux vieilles consoles, comme la Game Boy Color : de gros pixels, une palette de couleur réduite, un son minimaliste (mais sur 4 canaux).

Ce minimalisme permet de créer rapidement des jeux qui fonctionneront de manière fluide, grâce à sa bibliothèque en Rust, sur n’importe quelle brouette, et même sur une page web (avec Wasm).

Préparation

Pyxel fonctionne sur Linux, Mac et Windows.

Le développement se passe depuis le terminal, en tapant des commandes.

Éditeur de texte

Il vous faut un éditeur de texte : VSCode, Sublime Text, ou un autre qui vous plait. Ils sont disponibles en téléchargement depuis leur page web, ou via les appstores des différents OS.

Sur Mac ce sera Homebrew avec brew install vscode.

Sur Ubuntu, ce sera avec Snap disponible depuis la barre d’outils.

Python

Vérifiez que vous avez Python dans une version décente (>= 3.10) : python3 -V

Sinon, il faut l’installer. Pour Mac, ce sera brew install python, pour Linux, ce sera sudo apt install python3-dev python3-venv python3-pip, pour Windows ce sera via python.org.

Installation

Pyxel n’est pas une application, mais un framework Python, avec un éditeur de médias.

Donc, pour l’utiliser, il suffit d’avoir Python sur sa machine, et de créer des projets, avec leur environnement, (un venv), puis d’ajouter la bibliothèque pyxel avec l’outil pip.

Pour ne pas mettre le bazar, on va ranger le code dans un dossier

mkdir pyxel-demo
cd pyxel-demo

Puis créer un environnement pour Python, pour pouvoir installer des bibliothèques uniquement pour ce projet et ne pas tout cochonner.

python3 -m venv .pyxel-env
source .pyxel-env/bin/activate

Sur Windows, l’activation utilise une commande différente.

Installation des bibliothèques. On commence par mettre à jour pip pour ne pas qu’il râle, puis on installe pyxel.

pip install -U pip
pip install pyxel

Exemples de projets Pyxel

pyxel copy_examples

Pyxel va télécharger son dossier d’exemple et créer le dossier pyxel_examples.

La commande pyxel run permet de lancer les démos.

pyxel run pyxel_examples/02_jump_game.py

Jump Game utilise les flèches droite/gauche pour accélérer/ralentir le personnage.

L’éditeur de médias pour Pyxel

Pyxel fournit un éditeur de médias (sprites, tilemap, sound, music).

Pyxel utilise des formats traditionnels (et même antiques).

Les images sont en PNG (la transparence est gérée). Les jeux vidéos ont l’habitude d’avoir le zéro dans le coin haut gauche. Il est possible de glisser/déposer des images PNG/GIF/JPG dans la fenêtre de l’éditeur.

Les polices de caractères (les fonts, quoi) sont en pixel et au format BDF, éditables avec fontforge.

Les maps sont au format TMX, éditables avec Tiled (plus de formats sont disponibles sur Github)

L’éditeur Pyxel crée des archives pyxres qui sont fichiers ZIP avec un gros TOML à l’intérieur avec les binaires gérés comme des listes d’entiers.

Le son est dans un format spécifique.

pyxel edit

Démo

Demo party !

Une fois ce premier test réussi, et quelques bidouillage pour se l’approprier, il faudra lire la doc, une longue page.

Le code est typé, mais non commenté, ce qui permet d’avoir quelques indices depuis son éditeur.

Éditeur de médias

pyxel edit initiation.pyxres &

Dessinez deux vaisseaux spatiaux, de 16 pixels de côté.

C’est votre vaisseau, vous dessinez bien ce que vous voulez, tant que vous respectez les consignes.

Le premier regarde à droite, le second, à gauche.

Par convention, la transparence est la première couleur (noir), et correspond à la valeur 0 dans le code.

pyxel

Si vous galérez, vous pouvez simplement télécharger le fichier initiation.pyxres.

Code

Éditez un fichier initiation.py, puis adaptez le code d’exemple (plus bas) à vos besoins.

# Uniquement sur UNIX
touch initiation.py

Un vaisseau immobile

L’applicaition est rangé dans une class.

Lors de la création de la class, on choisit la taille de l’écran et son titre. Les assets sont chargés.

La méthode update gère les entrées (le clavier dans ce cas).

La méthode draw va entièrement redessiner l’écran.

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO static")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.ship = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

Demandez à Pyxel de surveiller votre jeu.

pyxel watch . initiation.py

Le vaisseau s’affiche au milieu du vide de l’espace, et on peut juste quitter l’application avec la touche Q.

Le vaisseau est secoué

Un peu de math, un simple cosinus, pour que le vaisseau rebondisse mollement.

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO shaken")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.ship = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

        # 🛸 bumps slowly 1/16 per tick starting with y = 72
        self.player_y = pyxel.cos(pyxel.frame_count * 360 / 16) + 72

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

Le vaisseau peut avancer dans un sens ou dans l’autre

Le vaisseau peut pointer d’un côté ou de l’autre, grâce à deux sprites.

Avec les flèches, on peut avancer ou reculer de 2 pixels (sans sortir du cadre).

import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120, title="UFO exploration")  # width, height, title
        pyxel.load("initiation.pyxres")
        self.player_x = 72  # 🛸 X position
        self.player_y = 72  # 🛸 Y position
        self.spaceship_r = (0, 0, 16, 16, 0)  # x, y, width, height, transparent color
        self.spaceship_l = (16, 0, 16, 16, 0)
        self.ship = self.spaceship_r  # 🛸 looks ->

        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

        if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_LEFT):
            self.player_x = max(self.player_x - 2, 0)  # go ->
            self.ship = self.spaceship_l  # 🛸 looks <-
        if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_RIGHT):
            self.player_x = min(self.player_x + 2, pyxel.width - 16)  # go ->
            self.ship = self.spaceship_r  # 🛸 looks ->

        # 🛸 bumps slowly 1/16 per tick starting with y = 72
        self.player_y = pyxel.cos(pyxel.frame_count * 360 / 16) + 72

    def draw(self):
        pyxel.cls(0)  # clear screen with color 0

        # Draw 🛸
        pyxel.blt(self.player_x, self.player_y, 0, *self.ship)


App()

Voilà, vous avez un sprite qui gigote et réagit aux frappes sur un clavier.

La suite

Quelle sera la suite ? Du bruit, un décor avec des étoiles, des tirs de laser, des antagonistes ?

blogroll

social