Pyxen Script

Les jeux Pyxen s’écrivent en Pyxen Script, un dialecte de Python conçu pour les jeux. Il a la syntaxe et l’allure de Python : l’indentation, def, les listes, les dictionnaires, les f-strings, les compréhensions. Ce qui change se trouve en dessous. Votre code est vérifié dans son ensemble et compilé avant que le jeu ne démarre, puis il s’exécute sur un runtime conçu pour une seule tâche : faire tourner un jeu, de la même façon, sur chaque appareil.

Si vous connaissez déjà Python, l’essentiel de ce que vous écrivez fonctionne comme vous l’attendez. Cette page explique pourquoi Pyxen Script existe, ce qu’il partage avec Python et ce qu’il laisse de côté volontairement.

Pourquoi un dialecte

Un jeu n’attend pas la même chose d’un langage qu’un script.

Les versions précédentes de Pyxen utilisaient un interpréteur MicroPython ; Pyxen Script le remplace.

Ce qui est identique à Python

Pyxen Script est testé sur des centaines d’idiomes Python d’une ligne, chacun comparé à la sortie de Python standard. Tout ce qui suit se comporte comme en Python :

Ce qui est différent

Pas de classes

Pyxen Script n’a pas de class. C’est un choix de conception.

Dans un jeu Pyxen, l’état vit dans les entités et leurs components, que le moteur possède, stocke de façon compacte et vous montre dans l’inspecteur de frames. Une classe serait un second endroit où ranger le même état, invisible pour le moteur comme pour l’inspecteur. Là où du code Python définirait une classe, du code Pyxen crée une entité, lui attache un component et écrit des fonctions qui agissent dessus.

Une classe Python typique :

class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 3

    def hit(self):
        self.hp -= 1

La même chose en Pyxen Script :

def spawn_enemy(x, y):
    e = world.spawn(name="enemy", x=x, y=y, sprite=Sprite("slime"))
    e.enemy = {"hp": 3, "speed": 1.5}
    return e

def hit(e):
    e.enemy.hp -= 1
    if e.enemy.hp <= 0:
        world.destroy(e)

def update():
    for e in world.all("enemy"):
        e.x -= e.enemy.speed

L’entité a déjà une position, un sprite et une place dans la scène. Le component enemy contient ce qui est propre aux ennemis, et world.all("enemy") les retrouve tous : il n’y a pas de liste d’instances à tenir à jour. Pour des données simples qui ne sont pas des objets de jeu, comme un tableau des meilleurs scores ou la description d’un niveau, utilisez des dictionnaires et des listes. Voir Entités et components pour le modèle complet.

Pas d’exceptions

Pyxen Script n’a ni try, ni except, ni finally, ni raise. C’est aussi un choix de conception.

Une erreur en Pyxen Script arrête le jeu et est signalée à sa ligne, avec les appels qui y ont mené. Rien ne peut l’intercepter en chemin : une erreur n’est jamais avalée par un except: pass pour devenir un jeu qui se comporte mal en silence trois niveaux plus loin.

La plupart des blocs try du code de jeu posent une question qui a une réponse directe. Posez la question :

# cette entité a-t-elle un component ?
hp = e.health.value if e.has("health") else 0

# cette clé est-elle dans le dictionnaire ?
best = scores.get(name, 0)

# ce texte est-il un nombre ?
if text.isdigit():
    level = int(text)

# cet indice est-il dans la liste ?
if i < len(items):
    item = items[i]

assert est disponible, et arrête le jeu à sa ligne quand sa condition est fausse.

Également absent de Pyxen Script

Chacun de ces points est signalé par son nom, à sa ligne, avant que le jeu ne démarre.

Modules et imports

Un projet est compilé comme un seul programme. Chaque script y est déjà visible des autres par son nom de fichier : une fonction spawn() dans monsters.py s’appelle monsters.spawn() depuis n’importe quel autre script, sans import. L’API du moteur (world, input, Sprite…) est elle aussi toujours disponible.

Les imports que vous avez déjà, comme from pyxen import * ou import monsters, fonctionnent toujours. Pyxen Script les considère comme inutiles, et vous pouvez les retirer.