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.
- Il tourne de la même façon partout. Le même programme compilé s’exécute dans l’app sur iPad et Mac, dans un jeu exporté pour Windows et dans un navigateur grâce à l’export web. Pyxen Script a été conçu pour WebAssembly dès le départ : le navigateur n’est pas une cible de second rang.
- Pas de pauses du ramasse-miettes. Pyxen Script n’a pas de ramasse-miettes. Les valeurs temporaires créées pendant une frame vivent dans une zone de mémoire vidée d’un seul coup à la fin de cette frame ; les valeurs que vous conservez, comme une liste rangée dans une variable globale, sont libérées dès que plus rien n’y fait référence. Rien n’interrompt votre jeu en pleine frame pour faire le ménage.
- Les erreurs avant que le jeu ne tourne. Tout le projet est vérifié avant la première frame. Un nom mal orthographié, l’appel d’une fonction de module qui n’existe pas, ou une construction que Pyxen Script ne prend pas en charge est signalé à sa ligne, avant même le démarrage du jeu.
- Les erreurs à la bonne ligne. Quand quelque chose se passe mal pendant le jeu, l’erreur est capturée là où elle se produit, avec la chaîne d’appels qui y a mené encore intacte : Pyxen peut vous montrer la ligne exacte.
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 :
- Blocs et contrôle du flux : l’indentation,
if/elif/else,foretwhileavecbreak,continueetelse, les expressions conditionnelles (a if ok else b), les comparaisons enchaînées (0 <= x < 320),pass,assert. - Fonctions :
defavec arguments par défaut et nommés, plusieurs valeurs de retour sous forme de tuple,lambda, des fonctions rangées dans des listes et des dictionnaires, les fonctions imbriquées et les closures, la récursivité,global. - Données : listes, dictionnaires (qui conservent l’ordre d’insertion), ensembles, tuples, slicing, indices négatifs, déballage (
x, y = pos,first, *rest = items), et compréhensions de listes, de dictionnaires et d’ensembles. - Chaînes : f-strings avec spécifications de format (
f"{score:05d}",f"{t:.2f}"),str.format, le formatage avec%, et les méthodes habituelles (split,join,strip,replace,find,upper,startswith,zfill…). Longueurs et indices comptent des caractères : un texte dans n’importe quelle langue fonctionne. - Fonctions intégrées :
print,len,range,enumerate,zip,sortedetmin/maxaveckey=etreverse=,sum,any,all,map,filter,round,abs,int,float,str,isinstance, et d’autres. - Nombres : les entiers font 64 bits et les flottants sont en double précision :
2 ** 40et0.1 + 0.2donnent les mêmes résultats qu’en Python.//et%arrondissent vers moins l’infini, comme en Python. math:import mathfonctionne, avecsqrt,floor,sin,atan2,hypot,piet le reste des fonctions habituelles.
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
with,yield(fonctions génératrices),nonlocal, les décorateurs,asyncetawait, et l’opérateur morse:=. Les expressions génératrices commesum(x * x for x in xs)fonctionnent.*argset**kwargsdans les paramètres d’une fonction. Déplier une liste ou un dictionnaire dans un appel, commeprint(*items), fonctionne.- La bibliothèque standard de Python.
mathest disponible ; Pyxen fournit ses propres modulesrandom,time,inputetstorage, documentés dans la référence de script. Des modules commeos,sys,jsonouresont signalés avant que le jeu ne tourne. - Les entiers au-delà de 64 bits. Un résultat qui ne tient pas arrête le jeu avec une
OverflowError; il ne déborde jamais en silence. Les nombres rangés dans les champs entiers d’un component font 32 bits. - Les tabulations pour l’indentation. Indentez avec des espaces : la largeur d’une tabulation dépend de l’éditeur, et un bloc qui en dépend pourrait signifier des choses différentes selon la personne.
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.