协程最显著的案例可能是旧的图形“点击”冒险游戏,在这些游戏中,它们被用来编写剧情场景和其他动画序列。一个简单的代码示例看起来像这样:
# script_file.scr
bob.walkto(jane)
bob.lookat(jane)
bob.say("How are you?")
wait(2)
jane.say("Fine")
...
整个过程无法像普通代码一样编写,因为你想在执行 bob.walkto(jane)
后看到 bob 做他的步行动画,而不是直接跳转到下一行。然而,为了播放步行动画,你需要将控制权交回给游戏引擎,这就是协程发挥作用的地方。
整个过程被执行为协程,意味着你可以随意暂停和恢复它。像 bob.walkto(jane)
这样的命令会告诉引擎端的 bob 对象它的目标,然后暂停协程,等待 bob 到达目标时的唤醒调用。
在引擎端,事情可能看起来像这样(伪代码):
class Script:
def __init__(self, filename):
self.coroutine = Coroutine(filename)
self.is_wokenup = True
def wakeup(self):
self.is_wokenup = False;
def update():
if self.is_wokenup:
coroutine.run()
class Character:
def walkto(self, target, script):
self.script = script
self.target = target
def update(self):
if target:
move_one_step_closer_to(self.target)
if close_enough_to(self.target):
self.script.wakeup()
self.target = None
self.script = None
objects = [Character("bob"), Character("jane")]
scripts = [Script("script_file.scr")]
while True:
for obj in objects:
obj.update()
for scr in scripts:
scr.update()
然而,需要提醒的是,虽然使用协程可以使编写这些序列变得非常简单,但并非您找到的每个实现都考虑了串行化的问题,因此如果您大量使用协程,游戏保存将成为一个麻烦的问题。
这个例子只是在游戏中使用协程的最基本情况,协程本身也可以用于很多其他任务和算法。
协程允许创建大量非常轻量级的“微线程”,实现协作式多任务处理(即,微线程自愿地暂停以允许其他微线程运行)。请参阅Dave Beazley关于这个主题的文章。
现在,这样的微线程如何在游戏编程中有用就显而易见了。考虑一个实时策略游戏,你有数十个单位 - 每个单位都有自己的思想。对于每个单位的人工智能来说,在您的模拟多任务环境中作为这样的微线程运行可能是方便的抽象。这只是一个例子,我相信还有更多。
在谷歌上搜索“协程游戏编程”会带来一些有趣的结果。
协程在游戏中的一种应用是作为一个轻量级线程,类似于Kamaelia中的actor模型。
你的游戏中的每个对象都将成为一个Kamaelia '组件'。组件是一个可以通过yield暂停执行的对象。这些组件还具有消息系统,允许它们异步地安全地相互通信。
所有对象都将同时执行各自的任务,并在交互发生时向彼此发送消息。
因此,这并不仅限于游戏,任何具有大量并发组件相互通信的情况都可以受益于协程。
以下是两个你可能会感兴趣的链接:
http://aigamedev.com/open/articles/coroutine-state-machine/
http://www.dabeaz.com/coroutines/ (在此页面和pdf中搜索“task”)
想要使用协程来表示单个角色AI脚本是非常普遍的。不幸的是,人们往往忘记协程具有与线程相同的所有同步和互斥问题,只是在更高的级别上。因此,您通常需要首先尽可能多地消除本地状态,其次编写强大的方法来处理协程中出现的错误,当您引用的某些内容停止存在时。
因此,在实践中,它们很难从中获得好处,这就是为什么像UnrealScript这样使用某种协程的语言将大部分有用的逻辑推向原子事件的原因。一些人可以从中获得良好的效益,例如EVE Online的人们,但他们必须基本上围绕该概念构建整个系统。(以及他们如何处理共享资源的争用并没有得到很好的记录。)