Pyxen Script

Pyxen 游戏用 Pyxen Script 编写,这是一种为游戏打造的 Python 方言。它拥有 Python 的语法和手感:缩进、def、列表、字典、f-string、推导式。变化的是底层。你的代码会作为一个整体接受检查,并在游戏开始前完成编译,然后运行在一个只为一件事打造的运行时上:让游戏在每台设备上以同样的方式运行。

如果你已经会 Python,你写的大部分代码都会按预期工作。本页说明 Pyxen Script 为什么存在、它与 Python 有哪些相同之处,以及它有意省略了什么。

为什么是一种方言

游戏对语言的要求,和脚本不一样。

早期版本的 Pyxen 使用 MicroPython 解释器;Pyxen Script 取代了它。

与 Python 相同的地方

Pyxen Script 用数百个单行的 Python 惯用写法进行测试,每一个都与标准 Python 的输出对比。下面这些都与 Python 的行为一致:

不同的地方

没有类

Pyxen Script 没有 class。这是有意的设计。

在 Pyxen 游戏中,状态存放在实体及其组件上。它们归引擎所有,由引擎紧凑地存储,并在帧检查器中展示给你。类会成为存放同一份状态的第二个地方,而引擎和检查器都看不到它。所以在 Python 代码会定义一个类的地方,Pyxen 代码会生成一个实体,给它附加一个组件,再编写作用于它的函数。

一个典型的 Python 类:

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

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

用 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

实体本身已经有位置、精灵和在场景中的位置。enemy 组件存放敌人特有的数据,world.all("enemy") 能找到所有敌人,所以不需要维护一份要保持同步的实例列表。对于不是游戏对象的简单数据,比如高分榜或关卡描述,请使用字典和列表。完整的模型请参见实体与组件。

没有异常

Pyxen Script 没有 try、except、finally 或 raise。这同样是有意的设计。

Pyxen Script 中的错误会让游戏停止,并在对应的行报告出来,同时附上通向它的调用。中途没有任何东西能捕获它,因此错误永远不会被 except: pass 吞掉,变成一个在三关之后悄悄出问题的游戏。

游戏代码里的大多数 try 块,其实是在问一个可以直接回答的问题。那就直接问这个问题:

# 这个实体有这个组件吗?
hp = e.health.value if e.has("health") else 0

# 这个键在字典里吗?
best = scores.get(name, 0)

# 这段文本是数字吗?
if text.isdigit():
    level = int(text)

# 这个索引在列表范围内吗?
if i < len(items):
    item = items[i]

assert 可以使用,条件为假时会在该行停止游戏。

Pyxen Script 中也没有的

以上每一项都会在游戏开始前,按名称在对应的行报告出来。

模块与 import

一个项目会作为一个程序来编译。其中的每个脚本已经可以通过文件名被其他脚本看到:monsters.py 中的函数 spawn(),在任何其他脚本中都可以直接以 monsters.spawn() 调用,不需要 import。引擎 API(world、input、Sprite 等)也始终可用。

你已经写好的 import,比如 from pyxen import * 或 import monsters,仍然可以正常工作。Pyxen Script 会把它们视为不必要的,你可以删掉。