使用 Python 进行游戏脚本编程 [翻译]

2023-11-01

翻译自 GDC 2002 上 Bruce Dawson 的 Game Scripting in Python

简单介绍 Python 作为游戏脚本语言的优势

原文:Game Scripting in Python
作者:Bruce Dawson <bruced@humongous.com>
译文:使用 Python 进行游戏脚本编程
译者:Breaker <breaker.zy_AT_gmail>


使用 Python 进行游戏脚本编程

作者

Bruce Dawson
Humongous 娱乐公司
http://www.cygnus-software.com/papers/

介绍

使用脚本语言可以更加快速地开发游戏逻辑,而不必担心由于 C++ 程序员的粗心大意所造成的后果。使用已有的脚本语言可以节省开发新型自定义语言的时间和开销,并且这些语言通常要比自己创造的语言更加的强大。

Python 对于游戏脚本语言来说是一种不错的选择,它很强大,容易嵌入使用,能够无缝地使用 C/C++ 进行扩展,包含很多脚本语言所具有的高级特性,并且它可以用来实现自动化过程[TR1: automating production]。另外,关于 Python 的书籍、开发工具 和 库 很丰富,使得我们很容易从其他开发者那里受益。

下来就谈一谈我们在 Humongous 娱乐公司将 Python 集成进新游戏引擎的一些经验。说明我们选择 Python 的原因、获得的收益、遇到的问题,以及我们是怎样解决它们的。

为什么要使用脚本语言

C++ 是一种强大的语言,并且是 C 语言的巨大改进,但它并不是完成所有任务的最佳选择。C++ 非常强调运行时性能 [Stroustrup94],譬如,假如一个语言特性使得程序跑起来变慢,那么这个特性便不会加入 C++ 语言中。C++ 程序员也因此背负了很多的限制和烦恼。

这里列出一些限制,C++ 程序员经常遭遇这些事情但很少注意它们的存在:

  • 手工管理内存:C++ 程序员的大量时间都花在考虑调用 delete 的适当时机。
  • 链接过程:C++ 模块(在编译时或加载时)链接在一起,因此在运行时,无需进行的函数地址的解析。这提高了运行时的性能,但是却使 编辑/测试 周期变长了。
  • 缺乏自省能力 [TR2: introspection]:C++ 有自己的方式知道一个类中包含哪些成员,但是这种方式需要编写过多的加载和存储对象的代码,而在一些脚本语言中这只需调用一个内建函数就可以完成。

C++ 是静态的,而脚本语言是动态的。简单地说,C++ 的程序运行地很快,但是脚本语言能让你编码更快。

所以,C++ 应该只用在你希望优化运行时性能的地方。现在计算机的运行速度都足够快,对于大多数代码来说性能都不是问题。如果你用 C++ 开发那些用脚本语言也能实现的程序,那么你是在错误的事情上进行优化。

SCUMM 的问题

Humongous 公司已经使用 SCUMM (Script Creation Utility for Maniac Mansion) 创造了 50 多个游戏。SCUMM 是一个强大的 冒险游戏 开发语言,但是它有一些局限性。SCUMM 是十多年前写的,它缺少一些现代语言的特性。

尽管 SCUMM 有持续的补丁和维护,它也没有办法像其它语言一样健壮和有完备的功能了。

为什么选择 Python

我们有过创造一种新型的、现代的 私有语言的想法,但最终明智地放弃了这种想法。我们的职责是在做游戏,而不语言。

我们在每年花费大量开销维护一套私有工具的情况下,确实希望使用一种已有的脚本语言而不是重新创造一种。使用已有语言更快地投入工作,花费更少的开销,并且通常情况下要比我们创造的好,并且以后会发展地更好,即使我们不用它工作。

一旦我们决定要使用已有的脚本语言,就需要从中选择一种。我们需要一种支持 面向对象编程,并且能嵌入到我们游戏中的语言,而且它不存在任何技术和许可授权上的问题。

我们考虑了 Lua [Lua01] 和 Python [Python02],这两种语言已经被应用在某些游戏中了。

Lua 较小,更加容易嵌入到应用程序中,并且有一些很棒的语言结构。但是,那时我们发觉 Lua 的文档有些粗略,这大概是因为 Lua 是比 Python 更新的语言。

Python 比 Lua 有更多的扩展模块,更多的参考书籍,并且 stackless Python [Tismer01] 很适合为对象 AI 创建微线程[TR3: micro-threads]。最后我们没有选择 Python 的 stackless 版本,但开始用 Python 写自动生成脚本,这给了我们继续使用 Python 的动力。当了解了 Python 后,我们喜欢上了它的语法,最后选择了它。

在我们决定之后,这两种语言都发生了改进:Lua 已经变成 stackless,而 Python 有了生成器,这个能提供一些相似的功能。现在任何一种都是安全的选择。

谁在游戏中使用了 Python

Python 已经被使用在很多游戏中,包括:

还有很多其它的游戏,只是我们很难确认,例如至少有一个 PS2 游戏使用了 Python。

同时 Python 也至少用在两个游戏引擎中:

一个生成脚本示例

下面是一段 Python 代码示例,它是一个递归生成所有 VC++ 工作区的简单生成脚本。它只有以下几行:

import os def BuildAllWalker(unused, dirname, nameList): for entry in nameList: if entry.endswith(".dsw"): workspaceName = os.path.join(dirname, entry) resultFile = os.popen("msdev %s /make all" % workspaceName) resultLines = resultFile.readlines() for line in resultLines: print line if __name__ == '__main__': os.path.walk(os.curdir, BuildAllWalker, None)

加上更多的代码,可以让这个脚本 [Dawson02] 分析输出结果,然后给团队中的每个人发送一份结果报告邮件。不像某些其它脚本语言,上面代码有很好的可读性。使用 Python 来写生成脚本和游戏脚本将会省却很多学习的时间。

这个生成脚本示例也显示了一些对 Python 新手很头疼的问题。Python 的流程控制由缩进指明,而不使用 begin/end 声明或大括号。

我用了很短的时间来适应这种规则,最后我发现这种规则很有效。我曾经不止一次讨论过 C/C++ 中的大括号应该写在哪里,我想 Python 程序员有更高的工作效率,因为他们不用花费时间争论 K&R 及其它缩进风格[TR4: indenting style] 的事情。因为代码块由缩进定义,编写时便不会出现任何不符合 Python 编译器规则的缩进(因为那样的话,程序就会出错)。

要注意的是,当你混用 TAB 和空格进行缩进时,可能出现问题。大多数程序员使用宽度为 3 个或 4 个空格的 TAB 缩进,但是在 Python 编译器内部却使用 8 个空格的缩进,混合使用 TAB 和空格可能导致语法错误。如果你完全地使用空格或 TAB 进行缩进,并且使用一个能够提示混用空格、TAB 缩进警告的 IDE,那么便没有什么问题。

游戏脚本示例

下面的示例是我们的第一个 Python/C++ 游戏中的一些 Python 代码。这些代码是 Python 正在执行的一个主循环,它调用了其它的模块,这些模块甚至可以用其它语言编写:

try: # Update the input g_InputManager.DispatchInput() # Tick the menu code g_MenuManager.Tick() # Tick the scene code g_SceneManager.Tick() g_SceneManager.SetScene() except: import traceback traceback.print_exc(None,sys.stderr) g_Clock.Stop() #-endtry

因此,我们的游戏由 Python 启动,并在需要时调用 C++ 程序。

它是如何工作的

Python 程序由模块组成,当在一个源文件中使用另一个源文件中定义的函数时,需要导入那个文件。例如,gameai.py 有一个 UpdateAI 函数,那么在其它 Python 源文件中可以这样调用它:

import gameai gameai.UpdateAI()

游戏程序员能够想到的一个很棒的事情是,如果 UpdateAI() 跑起来很慢,那么可以用 C++ 来重写它。为了做到这点,在 gameai.py 中的函数和类型需要用 C++ 实现,并且在 Python 中注册为原先的模块名。之后,使用者能够继续导入并使用 gameai 模块,而不需要任何更改。

因此,Python 模块能够帮你简单地用 Python 搭建你的整个游戏框架,而在适当的地方用 C++ 代码实现。

粘合代码 (Glue Code)

如果你自己手工编写让 C++ 代码和 Python 协同工作的粘合代码,那将是一件枯燥繁琐的事情 [TR5: glue code]。一个能够产生粘合代码的系统框架是很重要的。

Swig, Boost, CXX 等 [Abrahams01] 能帮你产生代码,更方便地将 Python 和 C++ 粘合起来。还有 Fubi[Bilas01],它是一个通用的框架,可以将 C++ 的函数和类映射到一种脚本语言中。

早期,大多数这些粘合代码框架都依靠分析 C++ 头文件工作。因此,它们受到暴露的 C++ 头文件的限制,并且一些框架不支持从 C++ 类派生出 Python 类。后来,这些框架都有所改进,所以现在还是值得考虑的。

而我们决定做一个自己的方案,它可以根据类的 IDL 描述或导出函数来生成粘合代码。它的代码叫做 Yaga,是一个递归命名法,表示 Yaga is A Game Architecture。

一个典型的 Yaga IDL 代码如下:

module yagagraphics { struct Rect { int32_t x; int32_t y; int32_t width; int32_t height; }; }

它可以生成以下粘合代码,还有其它一些代码:

static int YAGAPYCALL Rect_init( YagaPyStruct<Rect>* self, PyObject* args, PyObject* kwds) { if (!PyArg_ParseTuple(args, "|llll", &self->structData.x, &self->structData.y, &self->structData.width, &self->structData.height)) { return -1; } return 0; }

使用这个框架可以很简单地导出类和函数,从 C++ 类派生 Python 类,将 C++ 的数组和 vector 映射为 Python 的序列类型,以及更多的事。

内存分配

Python 之中任何东西都是对象,对象被分配内存。因为所有的对象都有引用计数,所有你不用担心释放内存。但是,如果你是在编写游戏,尤其是控制台游戏(译注:指次时代及专用游戏机平台游戏),你必需要明白这些内存从何处分配而来,以及分配过程会产生内存碎片的严重性。

为了控制这个性能问题,你需要隔离 Python,使其有自己的内存分配场。你需要重定向所有的内存分配操作到一个自定义的分配器上,它从一个固定大小的分配场中分配内存。只要你预留足够大小的缓冲区,大于最大的 Python 历史分配额度(原文:leave enough of a buffer above the maximum Python memory footprint),应该就能避免内存碎片问题。

另一个内存问题是没有释放的块。这通常在 Python 中不是问题,因为每个对象都有引用计数,当变量离开作用域或者被显式删除,其引用计数就会减一,当计数为 0 时,对象就被释放,对象生命结束。

试想这样情况,一个被忘记的变量,它关联了一串其它的对象,这时就会阻碍这些对象的释放,所以你应该对清理对象保持警惕。然而,更糟糕的事情是循环引用问题,例如:对象 A 包含对象 B,但是对象 B 有一个回调指针指向对象 A,那么这两个对象永远都不会被删除。Python 的开发者们意识到这个问题,在最近的 Python 版本中加入了一个垃圾收集器,它搜寻无法访问到达的对象,并将其全部清除。

垃圾收集器对于游戏是很糟的,因为无法预知它们的运行时间,并且可能运行很长时间,使得画面的帧率降低。因此,游戏程序中需要禁用垃圾收集器,这个做起来很简单,随后在每个游戏关卡后显式地调用它。垃圾收集器同时也能告诉你 有多少无法访问到达的对象仍然在分配中,这个可以帮助你跟踪循环引用的情况,之后你可以手工地解决它们,这相当于 Python 的内存泄露检查。

性能

如果你用 Python 做一些繁重的浮点计算工作,和 C++ 的性能相比会很让人失望。Python 是一个慢语言,每个对象引用都意味着进行哈希表查询,每个函数调用也一样。这根本不能和 C++ 的性能相提并论,后者的变量位置和函数调用地址在编译时就决定了。

但这并不意味着 Python 不适合做游戏编程,而是你需要在适当的地点用它。如果拿字符串操作或 C++ STL 的 set 和 map 类型操作做对比,那么 Python 代码也许会做地更快。Python 的字符串操作函数是用 C 写的,并且 Python 的引用计数对象模型能够避免一些 C++ string 类的字符串复制过程。set 和 map 的大多数操作的复杂度是 O(log n),而对于 Python 的哈希表复杂度则是 O(1)。

你一定想,最好不要用 Python 写 场景图形遍历 或 BSP 冲突检测代码。但是如果你用 C++ 写它们,而后又导出到 Python 中使用,那么你就可以更快地编写 AI 代码。

请牢记 90/10 原则,这意味着对于 90% 的代码,你不必过多操心它们的运行时性能,而代码的明确表达力和编码的效率才是关键。

控制台游戏

内存和性能问题在控制台游戏平台上尤其重要。当不存在虚拟内存可以让你漫不经心做内存分配的时候,保证在独立的内存分配场中分配 Python 内存就显得格外重要。同时,也要更明智地使用垃圾收集器 (as is using the garbage collector wisely)。

控制台平台没有键盘、鼠标和多显示器,所以在控制台平台上运行 Python 调试器用起来很不方便。远程调试是关键,它能让你知道 Python 代码的运行过程。很幸运,使用免费的 HapDebugger[Josephson02] 可以很容易建立远程调试环境。

Python 使用 C 编写,并且已经被移植到多种编译环境和平台下,包括 PDA。因此,在某个控制台游戏平台下 Python 可能已经有了很充分的发展。

Python 会花费掉一小部分和控制台游戏无关的内存,但是在新一代游戏平台上可以不用担心这个,它们最小都有 24M 内存。

法律问题

推向一种新的语言对于我们公司来说是个重大的决定,我觉得在进行之前,它定是受到了公司律师们的祝福。

律师懂得法律,但他们通常不太懂编程。大多数程序员在引入开源代码前都不会咨询公司的律师,当你确实问他们时,他们会认为你正在问一些奇怪且偏僻的事情。他们的立即反应是,认为那是有风险、没有保证的计划。

如果你和一个擅长知识产权的律师长谈,他会一直向你灌输“使用开源软件会让你焦头烂额”的思想。有一些案例指明,在“免费发布”的源码中包含专利或有版权的内容时,有严重的法律问题隐患。当你从商业软件供应商那里得到授权代码时,他们会保护你免受法律责任,但对于开源软件没有人能给予授权许可 (with open source software there is no one to license it from)。

然而,开源社区对知识产权法律总是很警惕。例如 JPEG 已经从它们的开发库中移除了 LZW 算法代码以避免专利问题 [IJG]。负责的程序员会关心授权许可问题,并且通常对 GPL 和 LPGL[FSF01] 以及他们的区别很熟悉。

将开源代码引入商业产品存在很多风险。这些风险应严肃对待,但不应该阻止对开源代码的使用。有很多开源的开发库使用在游戏开发中,Python 实在没什么理由不被使用。

缺点

多语言开发增加了额外的复杂层次。同时调试两种语言的代码很困难,而且必须花费时间维护绑定两种语言的粘合代码。

类似 Python 的动态语言没有编译时类型检查。这种情况初看让人惊恐,但它的实际意味着,相比 C++ 你会遇到各式各样不同的运行时错误,通常它们都很容易解决。

不同类型的换行符

UNIX (LF)、Mac OS (CR) 和 Windows (CR LF) 对待文本文件中一行的结束有不同的约定,这实在很糟。

Windows 上的 C/C++ 库(译注:指 Windows API 和 VC 运行时库)会做换行符转换,所以 UNIX 文件能够在 Windows 上读取,可以将 Windows 文件像 UNIX 文件一样的操作。UNIX 和 Macintosh 文本文件之间的共同点更少,只能依靠假定某个平台上的文件都只是这个平台上曾经创建的,这个假设进行转换。这个假设在当今的网络环境下站不住脚,Python 也深受其害。直到现在,在 Windows 下写的 Python 代码可能无法在 Macintosh 下编译,反之亦然。

这个问题的解决方法是,在运行 Python 代码前,将 Python 源文件通过一个文件过滤器(可以用 Python 开发?)执行,另一种方法是以编译后的字节码形式发布 Python 代码。但是,这两种办法都有缺点。最理想的是在计算机工业中标准化文本文件格式,或者让所有的文件 IO 库实现读取任意类型文本文件的能力。

这个问题在苹果的 OS X 上更加有趣,换行符由运行程序的模式而定,你可以运行 UNIX 或 Macintosh 两种模式程序。这会在一个系统下出现两种不同的换行符,甚至不用重启。

Python 的 Macintosh 版本最近修正了这个问题,在打开文件时检查换行符并对每个文件进行调整。将所有的换行符都规定为 UNIX 类型是一种可行的方法,它在所有平台下都能工作,但是还是要留心这个问题。

调试器问题

很多 Python 程序员认为自动化测试和打印语句是他们唯一需要的调试工具,而使用调试器会影响编码的产能。或许这对他们来说的确如此,但我已经习惯于进行源码级调试,并且不会轻易放弃它。

PythonWin 是一个在 Windows 下的 Python 调试器兼 IDE(奇特吧?)。它是免费的,有一些不错的功能,但也有一些缺点,如:只能在 Windows 下运行,无法调试有自身消息循环的 Python 程序。

在 Humongous 娱乐公司,我们为 Macintosh 和 Windows 开发游戏,同时也涉及控制台游戏的开发。我们需要一种能工作在所有三个平台上的调试器,而最好的方案就是使用远程调试器。Python 的架构使得编写它的调试器很容易,再加上其它一些免费组件,我们开发出了自己的 Python 调试器,我觉得它的效果比 PythonWin 好,并且具有远程调试功能。被调试的客户端需要运行一些额外代码。调试接口是 socket 上的 ASCII 文本,另外,我们还没考虑将调试器客户端移植到更多其它平台的问题。

因为我们希望集中精力开发游戏本身,而不是语言工具,所以决定再次借用开源的力量。我们在 Python 社区发布了 HAP 调试器 (Humongous Addition to Python),将其作为一个开源项目[Josephson02]。这是一个回馈社区的好机会,并且我们也从维护这个调试工具的事务中解放出来。

我们还没有解决的问题是调试器的性能问题。大多数编译式语言实现调试断点的方法是,将常规指令替换为导致 CPU 异常的指令,如 x86 处理器的 int 3 中断。这让程序可以全速执行,直到触发中断点。Python 不支持从异常处恢复执行,所以不能使用断点异常的方法。Python 调试器处理断点的方法是 单步检查代码,即不停地在问自己“这一行有没有断点?”

这个性能影响的后果可能很严重。我们现在减小此影响的方法是,保证开发机器要比目标机器快得多。还有,将所有重量级计算用 C++ 扩展实现,这样即使 Python 代码拖慢了调试器,也不至于让整个游戏速度太慢。这是一个可以解决的问题,只是 Python 的主要开发者还没考虑过。

代码安全和游戏

C++ 程序员有时开玩笑说,删除注释和缩短变量名可以优化代码。然而,在 Python 中确实如此。

Python 代码在运行时被编译成字节码,并缓存起来以备后续运行,所以删除注释的方法不会起到优化程序的效果,但是缩短变量名则是另外一回事。大多数脚本语言都是在运行时通过名字定位变量的,这也是脚本语言强大的原因之一,因为它可以突破很多由 C++ 编译时绑定造成的限制。然而,这也意味着变量名会一直伴随着代码而存在(译注:C/C++ 等传统编译式语言则不同,经优化编译后的 C/C++ 程序中没有变量名而只有地址的概念)。

游戏程序中包含语义清晰 (scatological) 的变量名,会被人当做笑谈。更严重的问题是,如果在多人游戏中使用 Python 脚本,者反编译 Python 程序后会得到完整的变量和函数名,这比起通过反编译 C++ 程序来破解游戏要更简单。

Python 的优点

Python 编程很有趣。Python 易于学习,有更高的生产效率,并且促使你使用另一种思维编程。学习 Python 编程让我成为更好的 C++ 程序员。

快乐的程序员有更高的学习效率和生产效率,他们倾向创造更好的游戏。Humongous 公司中使用 Python 开发游戏的团队,在整个公司中拥有最高的工作士气。

Python 游戏编程系统(译注:应指开发工具、框架、类库等)具有很高的生产效率,而且它们仍然在发展之中。因为采用了它们,我们节省了很多资金。(原文:Productivity is higher with the Python game programming system, even though development is still being done on it. It is already clear that we will save a lot of money from this switch.)

用户界面的开发,在 C++ 中可能花费较长的时间,而在 Python 中可以使用一些新意的方式进行实现。通常使用文本文件定义 GUI 元素的位置和关联图形资源,进而定义菜单。在 C++ 中会使用硬编码的函数和控件对象,挂钩 GUI 元素;而在 Python 中,可将函数及对象名放入文本文件中,并在运行时扫描它们。Python 的动态和内省特性 (introspective) 使得做起这些事来很自然。(译注:C++ 也可使用读取文本配置方式,自动生成菜单,只是用 Python 的反射特性做起来更自然)

很多起先我们担忧的 Python 语言限制问题都已成为过去。Python 的开发者们对该语言进行持续地改进,有时他们就像一直在满足我们对 Python 特性需求的渴望一样。

游戏存档和读档

C++ 程序员要花费很多时间解决脚本语言中不会出现的困难问题。例如,用 C++ 进行游戏状态的存储和读取就是一个麻烦问题,经常要编写大量的代码。而且这种方法通常会导致,存档只能和特定版本的游戏程序配合工作。而在 Python 中,使用 cPickle 模块可以很方便的解决此问题,它可以存储和读取任何复杂的数据结构。

下面例子中声明了一个对象 mainObject,通常它是一个用户自定义类对象,包含各种需要存储的状态的句柄,但为简单起见,这里只把它做成一个列表。最初该列表包含数字 0 和一个字符串,然后将列表的第一个元素赋值为另外一个列表。这个过程可以继续下去,让 mainObject 包含任意复杂嵌套层次的对象,包括循环引用。

mainObject = [0, "A string"] mainObject[0] = ["a string", 0.234, 10, 12341234123412341234]

接下来保存着这个 mainObject,这需要两行代码。一行导入 cPickle 模块,另一行打开一个文件,将对象保存为二进制格式。在开发时,保存为文本格式很有用,只需省略掉 dump() 的最后一个参数即可。

import cPickle cPickle.dump(mainObject, open("game.sav", "wb"), 1)

然后是装载文件数据,这同样需要两行代码。一行导入 cPickle 模块,另一行重建 mainObject 对象,以及包含的子对象、列表、成员变量等。第三行打印出 mainObject 对象,可以看出已经正确地恢复了嵌套的列表。

import cPickle mainObject = cPickle.load(open("game.sav")) print mainObject [['a string', 0.23400000000000001, 10, 12341234123412341234L], 'A string']

这个 Python 特性在 C++ 基本功能中不存在。

生成器:游戏 AI 的微线程

微线程将对象状态信息放到局部变量中(这是恰当的位置),从而极大简化 AI 和对象更新代码 [Carter01]。可以使用汇编语言的技巧将微线程放进 C++ 中,但是那样很凌乱。在最近版本的 Python 中,微线程内建于语言之中。现在使用微线程会工作地很好。

在 Python 中它们叫做生成器 (generator),使用它们编写函数,函数产生某个结果后,控制返回到主程序。主程序稍后可以重新唤醒它们,并从中断处继续运行,并保持原来的局部变量值。下面的示例代码展示创建一个对象,并移动它们穿过屏幕。这个简单例子并不能从微线程/生成器中得到实际的好处,它只是基本展示它们怎样用来简化 AI 和对象更新代码。

# 告诉编译器 generator 和关键字 yield 可用 from __future__ import generators # 一个类,定义角色的行为 class MovingObject: # 构造函数中创建一个 generator 函数的句柄,并把它保存在私有成员变量中 def __init__(self): self.__genHandle = self.__UpdateFunction() # 每个栈帧调用一次 Update(),它恢复运行更新函数 __UpdateFunction() # 原文:Update() is called once per frame and tells the update function to resume. def Update(self): return self.__genHandle.next() # 每个栈帧通过 Update() 恢复运行私有的对象更新函数 __UpdateFunction() # 该函数保存所有的状态到局部变量中,如果需要的话,其中一些可以存储到成员变量中 # 原文:The private object update function is resumed once per frame by Update(). # It stores all of its state in local variables. Some of it could be stored in # member variables if desired. def __UpdateFunction(self): x, y = 0, 0 dx = 0.25 dy = 0.75 while 1: # 死循环 # 打印当前的位置 print x, y # 返回当前的位置,主程序中需要它 yield (x, y) # 函数从这里恢复运行,更新位置 x += dx y += dy # 更新速度,会加速运动 dx += 0.1 dy += 0.5 # 创建一个带 generator 的 AI 对象 character = MovingObject() # 首次运行 generator,局部变量 x, y 初始化、打印,然后控制返回到这里 character.Update() # 接着再次运行 generator 若干次 # 角色移动、打印它的位置,然后控制返回,重复若干次 for x in range(10): character.Update()

即使你不使用生成器,在 Python 中实现 AI 更新方法也比用 C++ 更干净。因为如果你的某部分 AI 代码需要一些额外的临时状态时,Python 可以将它加入到对象中,然后在不需要时删除它。而 C++ 因其静态特点,不能在运行时加入新的成员变量,这使你的对象在任何时候都必须包含所需的所有状态。

开始使用 Python

如果你开始使用 Python,第一件事是访问 Python 的官方网站 http://www.python.org/ 下载你的平台上的 Python 版本。

Python 文档在 http://www.python.org/doc/current/download.html,也有编译的 HTML 版本 (CHM) 更便于检索。

Windows 开发者可以使用 PythonWin 和各种 Win32 扩展 http://users.bigpond.net.au/mhammond/win32all-142.exe

调试器 HapDebugger https://sourceforge.net/projects/hapdebugger/

你可以下载并编译 Python 源码,构建自己的 Debug 和 Release 版 Python。Python 2.2 源码下载 ftp://ftp.python.org/pub/python/2.2/Python-2.2.tgz

最后,你可以阅读关于手工创建 Python 扩展的细节 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66509,然后选择一种粘合代码包来帮你做这些事情,参考http://www.boost.org/libs/python/doc/comparisons.html

参考

关于作者

Bruce 是 Humongous 娱乐公司的技术主管,他从事其他人无闲去做的挑战性工作,并充满乐趣。同时他兼职在 DigiPen 进行教学。在进入 Humongous 娱乐公司之前,他在 Cavedog 娱乐公司的多个产品组工作。

Bruce 在 Avid 技术公司的 Elastic Reality 分公司工作多年,编写特效处理软件和视频编辑插件,在这之前他在 EA 加拿大公司工作,并在公司更名为 Distinctive 软件的时候回国。他在那里编写了他的第一个电脑游戏,基于 Commodore Amiga 平台,并在作品的 2/3 已上色后回国。(原文:back when thirty-two colours was state of the art)

Bruce 在 GDC 2001 上发表了一篇名为 "What Happened to my Colours?!?"(《我的颜色究竟怎么了》),讨论 NTSC 制式上的奇怪问题。

他现在从事 Python 脚本编程,用其自动化完成工作任务,因此他现在有足够的时间摆弄他的游戏机了,那是在一次扑克中赢得的。

Bruce 和他的妻子及孩子住在西雅图近郊。在华盛顿,他告诉同事,所有的程序员都应该学会变魔术、单轮车和走钢丝。

[END]


翻译注释

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Python 进行游戏脚本编程 [翻译] 的相关文章

随机推荐

  • driverclasss oracle,Kettle 错误:Error connecting to database: (using class oracle.jdbc.driver.OracleDr...

    一 问题描述 使用kettle8 0连接oracle12c 发现将oracle安装目录下与ojdbc相关的jar包都拷贝到kettle的相关目录Program Files kettle data integration lib下 配置连接
  • 清华源镜像pip

    清华源镜像使用帮助 彻底解决timeout的问题 临时使用 pip install i https pypi tuna tsinghua edu cn simple some package opencv python 注意 末尾openc
  • Windows上提示 api-ms-win-core-path-l1-1-0.dll 丢失怎么办?

    Windows上提示 api ms win core path l1 1 0 dll 丢失怎么办 最近有用户在开启电脑的photoshop软件使用的时候 出现另外无法启动软件的情况 因为系统中缺失了对应的dll文件 那么这个情况怎么去进行问
  • 电源学习总结(三)——线性稳压的参数

    前文提到了一些线性稳压的主要特点 本文作者将结合NXP智能车大赛实际案例 说一下电源设计容易踩的坑和线性稳压选型过程 文章目录 主要参数 输出电压 输入电压 热阻 基准电压 压降 最大电流 输入电容 纹波抑制 选型实例 主要参数 在为实际应
  • 二十八.刷题.18

    输入三个字符 可以重复 后 按各字符的ASCII码从小到大的顺序输出这三个字符 include
  • 解密 QQ 号-队列-c语言

    问题描述 分析 每次从最前面拿两个 第 1 个扔掉 第 2 个 放到尾部 需要一个数组来存储这一串数即 int q 101 并初始化这个数组即 int q 101 0 6 3 1 7 5 8 9 2 4 head 用来记录队列的队首 即第一
  • 动态规划-货币问题

    动态规划 货币问题 题目一 arr是货币数组 其中的值都是正数 再给定一个正数aim 每个值都认为是一张货币 即便是值相同的货币也认为每一张都是不同的 返回组成aim的方法数 例如 arr 1 1 1 aim 2 第0个和第1个能组成2 第
  • 机器学习 之线性回归(包含推导过程)

    参考B站视频新手狂喜 目前B站最全最清晰的 机器学习算法 教程 从零开始详细解读 原理 代码实现 通通都在这里了 收藏慢慢学 决策树 随机森林 聚类分析 人工智能 哔哩哔哩 bilibili 线性回归 eg 银行贷款 数据 工资和年龄 特征
  • C Primer Plus(第六版)

    1 开发一个包含你需要的预处理器定义的头文件 写一个 h的头文件 包含你此次练习的题目就可以了 注意防止头文件被重复引用导致的错误 需要用 ifndef或者 pragma once 下面题目需要的声明和结构都在对应题目中 方法1 使用 if
  • mysql使用查询结果作为临时表

    一 select查询作为临时表 select cou name from select count AS cou enabled AS name from user group by enabled as a where cou gt 0
  • 基于微信小程序的新冠疫苗预约系统

    末尾获取源码 开发语言 Java Java开发工具 JDK1 8 后端框架 SSM 前端 微信小程序 Vue 数据库 MySQL5 7和Navicat管理工具结合 服务器 Tomcat8 5 开发软件 IDEA Eclipse 是否Mave
  • ArrayList集合及常用方法的使用

    ArrayLise
  • Spring IOC容器初始化过程 源码分析

    本文主要记录Spring容器创建 源码分析过程 首先贴上一张时序图 好久没画 忘的差不多了 画的不好 可以凑合看一下 接下来 贴上一份测试代码 这里使用AnnotationConfigApplicationContext来初始化Spring
  • upload-labs(11~12)通关笔记

    环境准备 1 php版本 lt 5 3 4 2 magic quotes gpc Off php我用的是upload labs官方推荐的5 2 17 搭建平台用的是phpStudy2018 修改magic quotes gpc magic
  • Java实现邮件发送功能

    确定发件人邮箱和密码 某些邮箱服务器为了增加邮箱本身密码的安全性 给 SMTP 客户端设置了独立密码 有的邮箱称为 授权码 对于开启了独立密码的邮箱 这里的邮箱密码必需使用这个独立密码 授权码 确认发件人邮箱的 SMTP 服务器地址 发件人
  • python人工智能:模型关系(泉舟时代)

    1 授课 林德尧 泉舟时代 未来城市技术总监 2 主要文章内容 通常下 Flask SQLAlchemy 的行为就像一个来自 declarative 扩展配置正确的 declarative 基类 因此 我们强烈建议您阅读 SQLAlchem
  • 《机器学习》Chapter 5 神经网络笔记

    机器学习 Chapter 5 神经网络 1 神经元模型 神经元接收到来自n个其它神经元传递过来的输入信号 这些输入信号通过带权重的连接进行传递 神经元接收到的总输入值将与神经元的阈值进行比较 然后通过激活函数处理以产生神经元的输出 2 感知
  • 使用css去除input边框

  • 不同CUDA版本对应的最小GPU运算能力和最低兼容驱动

    The minimum compute capability for various CUDA versions CUDA Version Minimum Compute Capability Default Compute Capabil
  • 使用 Python 进行游戏脚本编程 [翻译]

    翻译自 GDC 2002 上 Bruce Dawson 的 Game Scripting in Python 简单介绍 Python 作为游戏脚本语言的优势 原文 Game Scripting in Python 作者 Bruce Daws