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

当然

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

下来就谈一谈我们在 Humongous 娱乐公司将 Python 集成进新游戏引擎的一些履历。
解释我们选择 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 已经被利用在很多游戏中,包括:

ToonTown - http://www.toontown.com/EveOnline - http://www.eve-online.com/Blade of Darkness - http://www.codemastersusa.com/blade/

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

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

Game Blender - http://www.blender.nl/gameBlenderDoc/python.htmlPyGame - http://www.pygame.org/

一个天生脚本示例

下面是一段 Python 代码示例,它是一个递归天生所有 VC++ 事情区的大略天生脚本。
它只有以下几行:

源码打印?

加上更多的代码,可以让这个脚本 [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 正在实行的一个主循环,它调用了其它的模块,这些模块乃至可以用其它措辞编写:

源码打印?

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

它是如何事情的

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

源码打印?

游戏程序员能够想到的一个很棒的事情是,如果 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 代码如下:

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

源码打印?

利用这个框架可以很大略地导出类和函数,从 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,这须要两行代码。
一行导入 cPickle 模块,另一行打开一个文件,将工具保存为二进制格式。
在开拓时,保存为文本格式很有用,只需省略掉 dump() 的末了一个参数即可。

源码打印?

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

源码打印?

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

天生器:游戏 AI 的微线程

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

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

源码打印?

纵然你不该用天生器,在 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

参考

[Stroustrup94] Stroustrup, Bjarne \"大众The Design and Evolution of C++\公众, Addison Wesley, 115[Lua01] \公众The Programming Language Lua\"大众http://www.lua.org/[Python02] Python Language Websitehttp://www.python.org/[Tismer01] Tismer, Christian \"大众Stackless Python\"大众http://www.stackless.com/[Dawson02] Dawson, Bruce \"大众Python Scripts\"大众ftp://ftp.cygnus-software.com/pub/pythonscripts.zip[Abrahams01] Abrahams, David, \"大众Comparisons with Other Systems\公众http://www.boost.org/libs/python/doc/comparisons.html[Bilas01] Bilas, Scott, \公众FuBi: Automatic Function Exporting for Scripting and Networking\"大众http://www.gdconf.com/archives/proceedings/2001/bilas.doc[IJG] http://www.ijg.org/ - docs\README in the source distribution[FSF01] \公众What is Copyleft?\公众http://www.gnu.org/copyleft/[Josephson02] Josephson, Neal \"大众HAP Python Remote Debugger\公众http://sourceforge.net/projects/hapdebugger/[Carter01] Carter, Simon \公众Managing AI with Micro-Threads\公众, Game Programming Gems II, Charles River Media, 265-272