Pyxen Script
Los juegos de Pyxen se escriben en Pyxen Script, un dialecto de Python hecho para juegos. Tiene la sintaxis y el estilo de Python: la indentación, def, las listas, los diccionarios, las f-strings, las comprensiones. Lo que cambia está por debajo. Tu código se comprueba en su conjunto y se compila antes de que empiece el juego, y se ejecuta en un runtime construido para una sola tarea: ejecutar un juego, de la misma manera, en cada dispositivo.
Si ya conoces Python, casi todo lo que escribas funcionará como esperas. Esta página explica por qué existe Pyxen Script, qué comparte con Python y qué deja fuera a propósito.
Por qué un dialecto
Un juego no le pide a un lenguaje lo mismo que un script.
- Funciona igual en todas partes. El mismo programa compilado se ejecuta en la app en iPad y Mac, en un juego exportado para Windows y en un navegador gracias a la exportación web. Pyxen Script se diseñó para WebAssembly desde el principio, así que el navegador no es un destino de segunda.
- Sin pausas del recolector de basura. Pyxen Script no tiene recolector de basura. Los valores temporales creados durante un fotograma viven en una zona de memoria que se vacía de una sola vez al final de ese fotograma; los valores que conservas, como una lista guardada en una variable global, se liberan en cuanto nada los referencia. Nada detiene tu juego a mitad de fotograma para hacer limpieza.
- Errores antes de que el juego se ejecute. Todo el proyecto se comprueba antes del primer fotograma. Un nombre mal escrito, la llamada a una función de módulo que no existe o una construcción que Pyxen Script no admite se señalan en su línea, antes incluso de que el juego arranque.
- Errores en la línea correcta. Cuando algo sale mal durante el juego, el error se captura donde ocurre, con la cadena de llamadas que llevó hasta él todavía intacta, de modo que Pyxen puede mostrarte la línea exacta.
Las versiones anteriores de Pyxen usaban un intérprete de MicroPython; Pyxen Script lo sustituye.
Qué es igual que en Python
Pyxen Script se prueba con cientos de modismos de Python de una línea, cada uno comparado con la salida de Python estándar. Todo lo siguiente se comporta como en Python:
- Bloques y control de flujo: la indentación,
if/elif/else,forywhileconbreak,continueyelse, las expresiones condicionales (a if ok else b), las comparaciones encadenadas (0 <= x < 320),pass,assert. - Funciones:
defcon argumentos por defecto y con nombre, varios valores de retorno como tupla,lambda, funciones guardadas en listas y diccionarios, funciones anidadas y closures, recursión,global. - Datos: listas, diccionarios (que conservan el orden de inserción), conjuntos, tuplas, slicing, índices negativos, desempaquetado (
x, y = pos,first, *rest = items) y comprensiones de listas, diccionarios y conjuntos. - Cadenas: f-strings con especificaciones de formato (
f"{score:05d}",f"{t:.2f}"),str.format, el formato con%y los métodos habituales (split,join,strip,replace,find,upper,startswith,zfill…). Las longitudes y los índices cuentan caracteres, así que el texto en cualquier idioma funciona. - Funciones integradas:
print,len,range,enumerate,zip,sortedymin/maxconkey=yreverse=,sum,any,all,map,filter,round,abs,int,float,str,isinstancey más. - Números: los enteros son de 64 bits y los flotantes de doble precisión, así que
2 ** 40y0.1 + 0.2dan los mismos resultados que en Python.//y%redondean hacia menos infinito, como en Python. math:import mathfunciona, consqrt,floor,sin,atan2,hypot,piy el resto de las funciones habituales.
Qué es diferente
Sin clases
Pyxen Script no tiene class. Es una decisión de diseño.
En un juego de Pyxen, el estado vive en las entidades y sus componentes, que pertenecen al motor, que los guarda de forma compacta y te los muestra en el inspector de fotogramas. Una clase sería un segundo lugar para guardar el mismo estado, invisible para el motor y para el inspector. Así que donde el código Python definiría una clase, el código de Pyxen crea una entidad, le añade un componente y escribe funciones que actúan sobre ella.
Una clase típica de Python:
class Enemy:
def __init__(self, x, y):
self.x = x
self.y = y
self.hp = 3
def hit(self):
self.hp -= 1
Lo mismo 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
La entidad ya tiene una posición, un sprite y un lugar en la escena. El componente enemy guarda lo que es propio de los enemigos, y world.all("enemy") los encuentra a todos, así que no hay una lista de instancias que mantener sincronizada. Para datos sencillos que no son objetos de juego, como una tabla de récords o la descripción de un nivel, usa diccionarios y listas. Consulta Entidades y componentes para el modelo completo.
Sin excepciones
Pyxen Script no tiene try, except, finally ni raise. También es una decisión de diseño.
Un error en Pyxen Script detiene el juego y se señala en su línea, junto con las llamadas que llevaron hasta él. Nada puede capturarlo por el camino, así que un error nunca se lo traga un except: pass para convertirse en un juego que falla en silencio tres niveles después.
La mayoría de los bloques try del código de juegos hacen una pregunta que tiene respuesta directa. Haz la pregunta:
# ¿tiene esta entidad un componente?
hp = e.health.value if e.has("health") else 0
# ¿está esta clave en el diccionario?
best = scores.get(name, 0)
# ¿es este texto un número?
if text.isdigit():
level = int(text)
# ¿está este índice dentro de la lista?
if i < len(items):
item = items[i]
assert está disponible y detiene el juego en su línea cuando su condición es falsa.
Tampoco está en Pyxen Script
with,yield(funciones generadoras),nonlocal, los decoradores,asyncyawait, y el operador morsa:=. Las expresiones generadoras comosum(x * x for x in xs)sí funcionan.*argsy**kwargsen los parámetros de una función. Desplegar una lista o un diccionario en una llamada, como enprint(*items), sí funciona.- La biblioteca estándar de Python.
mathestá disponible; Pyxen ofrece sus propios módulosrandom,time,inputystorage, documentados en la referencia de scripting. Módulos comoos,sys,jsonorese señalan antes de que el juego se ejecute. - Los enteros de más de 64 bits. Un resultado que no cabe detiene el juego con un
OverflowError; nunca da la vuelta en silencio. Los números guardados en los campos enteros de un componente son de 32 bits. - Las tabulaciones para indentar. Indenta con espacios: el ancho de una tabulación depende del editor, y un bloque que dependa de él podría significar cosas distintas para personas distintas.
Cada uno de estos puntos se señala por su nombre, en su línea, antes de que el juego arranque.
Módulos e imports
Un proyecto se compila como un único programa. Cada script ya es visible para los demás por su nombre de archivo: una función spawn() en monsters.py se llama monsters.spawn() desde cualquier otro script, sin import. La API del motor (world, input, Sprite…) también está siempre disponible.
Los imports que ya tienes, como from pyxen import * o import monsters, siguen funcionando. Pyxen Script los considera innecesarios y puedes quitarlos.