TITLE:我给Python游戏引擎加上了Squirrel脚本,第一个函数已经跑起来了
ABS Engine 是一个基于 pygame 的 2D 引擎,用 Python 编写,编辑器用的是 Tkinter。我一直想在引擎里嵌入一门真正的脚本语言,这周终于动手做了:引擎现在内置了一个 Squirrel 虚拟机,第一个函数也已经迁移进去了。
引擎里其实早就有第二门语言。编辑器的主题配置放在 engine/tcl/theme.tcl 里,由一个很小的 tcl_loader.py 加载。这套机制运行得不错,所以这次嵌入 Squirrel 的路径对我来说并不陌生。
为什么选 Squirrel
Squirrel 是一门为游戏设计的小型可嵌入语言,支持花括号、类、闭包和生成器。《求生之路2》和《传送门2》的脚本都用它,这算是一个不错的背书。PyPI 上有一个叫 squirrel-lang 的 Python 绑定,封装的是 Squirrel 3.2 稳定版,所以我不需要写任何 C 代码。
我也是这个绑定项目的贡献者,这也是我选择它的原因之一。因为之前已经深入过这个项目,嵌入工作就没那么让人发怵了。
加载器的实现
我新建了 engine/nut_loader.py,用来对应已有的 Tcl 加载器。整个文件大约 85 行,核心接口包括:
get_vm():返回一个 Squirrel 静态虚拟机实例
nut_source(script_name):运行一个 .nut 文件
nut_eval(nut):运行一段字符串脚本
nut_call_function(name, *args):调用虚拟机里的函数
虚拟机是一个惰性单例。functools.cache 加在零参数函数上,就能免费得到一个线程安全、首次使用时才创建的单例。这意味着导入引擎本身不会启动虚拟机,除非真的有脚本要运行。
调用体验比我预想的要好。vm.get_roottable()["add"](3, 4) 会直接返回 7。真实的 Python 值进去,真实的 Python 值出来,不需要字符串插值,也不需要转义。整数、浮点数、字符串、列表和字典都能干净地跨语言传递。绑定函数内部抛出的 Python 异常,也会带着原始类型传播回来。
第一个迁移的函数
我刻意从小处开始。engine/nut/math.nut 里现在只有一个 clamp 函数:
function clamp(value, low, high) {
if (value < low) { return low }
if (value > high) { return high }
return value
}
这个函数现在已经在引擎核心的两个地方负责钳制颜色通道:Entity.__init__ 和 Game.set_bg_color。它还修复了一个真实的崩溃:如果某个颜色通道超出 0-255,pygame 会抛出 ValueError: invalid color,而游戏循环每一帧都要填充屏幕,所以一个计算过界的颜色会在帧中途把整个游戏拖垮。现在它只会被钳制住。
热门跟贴