游戏循环
Pyxen 以固定的每秒 30 帧运行你的游戏。你不需要编写主循环——定义两个函数,Pyxen 会为你调用它们。
start() 和 update()
def start():
# called once, at the beginning
pass
def update():
# called every frame, 30 times per second
pass
start() 是你设置世界的地方:生成实体、放置它们、配置场景。
update() 是你的游戏逻辑运行的地方:读取输入、移动实体、检查条件、更新状态。每次调用 update() 之后,引擎会渲染该帧。
固定帧率
每一帧占用完全相同的时间:1/30 秒(约 33ms)。这意味着:
- 游戏逻辑是确定性的——相同的输入产生相同的结果
- 你可以直接计算帧数(
time.frame),而不必处理可变的 delta 时间 - 时间拖动条和帧检查器可靠运行,因为每一帧都是一个干净的快照
如果你偏好基于时间的运动,仍然可以使用 time.dt(增量时间),但它始终是 1/30。
帧顺序
每一帧,Pyxen 按以下顺序执行这些步骤:
- 输入 — 读取键盘、鼠标、触控和手柄状态
- update() — 你的代码运行
- 碰撞 — 引擎处理网格碰撞体并调用碰撞回调
- 渲染 — 引擎绘制所有可见实体
这意味着当你在 update() 中读取输入时,值是最新的。当你移动一个实体时,碰撞处理紧随其后。
碰撞回调
如果你在顶层定义了 on_hit_body 或 on_hit_tile,引擎会在碰撞阶段调用它们:
def on_hit_body(entity, other):
# two grid bodies overlapped
pass
def on_hit_tile(entity, tile):
# a grid body hit a tilemap tile
pass
这些在 update() 之后、渲染之前运行。
时间控制
pyxen.time 模块提供:
| 属性 | 值 | 用途 |
|---|---|---|
time.frame | 0, 1, 2, ... | 自启动以来的帧计数 |
time.dt | 0.0333... | 增量时间(始终为 1/30) |
time.t | 0.0, 0.033, 0.066, ... | 经过的秒数 |
time.fps | 30 | 每秒帧数 |
对于定时事件,你可以使用两种方式:
# Frame-based: fire every 60 frames (2 seconds)
if time.frame % 60 == 0:
fire()
# Time-based: same thing
if int(time.t * 30) % 60 == 0:
fire()
帧计数通常更简单、更可预测。