2009-08-08 121 views
16

我听说过,协程是构建游戏的好方法(例如,PEP 342:“协程是一种表达许多算法的自然方式,比如模拟,游戏......”),但我很难包装我的脑袋围绕着这将如何实际完成。游戏设计的协程?

我从这个article可以看出,协程可以代表一个状态机中的状态,这个状态机使用调度器相互转换,但我不清楚这是如何适用于游戏状态根据多个移动而变化的游戏玩家。

有使用协程编写的游戏的简单例子吗?或者有人可以提供一个如何完成的草图?你可能会发现有趣的

回答

7

游戏中可以使用单向协同程序,就像模型中的演员中的轻量级线程一样,如Kamaelia

游戏中的每个对象都是Kamaelia的“组件”。组件是一个可以暂停执行的对象,当它允许暂停时,可以通过让步来暂停执行。这些组件还有一个消息传递系统,可以让他们安全地异步通信。

所有的对象将同时做自己的事情,发生互动时互相发送消息。

所以,它并不是特定于游戏,但是当您有多个通信组件同时运行时,任何事情都可以从协同程序中受益。

9

协同程序允许创建大量的非常轻量级的“微线程“与合作多任务(即微螺纹悬挂自己故意让其他微螺纹运行)。在这个问题上阅读Dave Beazley的article

现在,很明显这种微螺纹如何能够用于游戏编程。考虑一个实时策略游戏,你有几十个单位 - 每个单位都有自己的想法。对于每个单位的AI来说,在模拟多任务环境中运行这样一个微线程可能是一个方便的抽象。这只是一个例子,我相信还有更多。

在Google上的“协同游戏编程”搜索似乎带来了有趣的结果。

8

协程最突出的例子是有点老的图形点&点击冒险游戏,在那里他们用来编写游戏中的过场动画和其他动画序列。一个简单的代码示例是这样的:

# script_file.scr 
bob.walkto(jane) 
bob.lookat(jane) 
bob.say("How are you?") 
wait(2) 
jane.say("Fine") 
... 

这整个序列不能被写为正常代码,只要你想看到鲍勃做他的步行动画你做的不是右跳到下一行bob.walkto(jane)后。对于步行动画来说,你需要将控制权交还给游戏引擎,这就是协程的作用。

这整个序列作为协同程序执行,这意味着您可以随意暂停和恢复它。像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() 

警告豆蔻词然而,尽管协程使编码这些序列很简单,不是每一个实现你会发现他们的意志在考虑序列化的情况下构建,所以如果大量使用协程,节约游戏将成为一个相当麻烦的问题。

这个例子也是一个协程在游戏中最基本的情况,协程本身也可以用于很多其他的任务和算法。

1

想要使用协程来表示个人角色AI脚本是很常见的。不幸的是,人们往往会忘记,只有在更高层次上,协程才具有线程所具有的所有同步和互斥问题。因此,您经常需要首先尽可能多地去消除本地状态,然后编写可靠的方法来处理协同程序中出现的错误,这些错误在您所指的内容不再存在时就会出现。

所以在实践中他们很难从中受益,这就是为什么像UnrealScript这样的语言使用一些协程的外表将大部分有用的逻辑推出到原子事件。有些人从他们身上获得很好的效用,例如。 EVE在线人员,但他们不得不围绕这个概念设计他们的整个系统。 (以及它们如何处理共享资源上的争用没有很好的文档记录。)

+0

协同程序有一些同步问题,但只有当它们的状态在yield()调用之间不能一致时。对yield()调用之间的所有代码进行隐式锁定可以大大简化事情。 最大的问题是,任何时候创建一个例程时,都应该决定这个和那个例程在它必须yield()之前是否总能完成。如果一个例程的调用者不希望某个例程产生,那么在调用该例程时它可能没有一致的状态。添加一个yield()将是一个重大改变。 – supercat 2010-07-14 15:10:25

+0

问题在于,你通常希望能够让所有这些例程都可以被使用(就像在UnrealScript中'隐式函数'隐含地产生的那样),但是这导致了以不可预知的方式进行交互的协同程序的组合。例如。如果一个巡逻队说“往北走,看看,往南走,看,重复”。另一个是反应例程,说“如果(听到西方的声音){去西部,看看,去东部}}”然后交错两者可能导致演员移动相当不可预测。这就是为什么UnrealScript只能在一个“协同程序”中允许这些功能。 – Kylotan 2010-07-15 09:35:14

+0

即使每个演员只有一个协程,但仍然不足以确保逻辑始终在语言层面上工作:如果您的协同程序说:“画剑;等待1秒;用剑攻击对手”,那么是什么如果因为你被另一名玩家解除了剑而消失了,会发生什么?特别是,如果您的语言通过使用局部变量来实现该协程,那么现在引用空对象的剑是什么?考虑所有这些情况是非常棘手的,如果你正常使用通用语言。 – Kylotan 2010-07-15 09:40:24