【问题标题】:Coroutines for game design?用于游戏设计的协程?
【发布时间】:2010-11-17 21:17:47
【问题描述】:

我听说协程是构建游戏的好方法(例如,PEP 342:“协程是表达许多算法的自然方式,例如模拟、游戏......”)但我有一个很难弄清楚这实际上是如何完成的。

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

是否有任何使用协程编写的游戏的简单示例可用?或者有人可以提供一个如何完成的草图吗?

【问题讨论】:

    标签: python coroutine


    【解决方案1】:

    协程允许通过协作多任务创建大量非常轻量级的“微线程”(即微线程故意挂起自己以允许其他微线程运行)。阅读 Dave Beazley 的 article 关于此主题的内容。

    现在,这些微线程对游戏编程的用途显而易见。考虑一个实时战略游戏,你有几十个单位——每个单位都有自己的想法。在模拟的多任务处理环境中,每个单元的 AI 作为微线程运行可能是一种方便的抽象。这只是一个例子,我相信还有更多。

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

    【讨论】:

      【解决方案2】:

      协程最突出的例子可能是老式的图形点击冒险游戏,它们用于编写游戏中的过场动画和其他动画序列。一个简单的代码示例如下所示:

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

      整个序列不能写成普通代码,因为你想看到 bob 在你完成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()
      

      然而,一个小小的警告,虽然协程使这些序列的编码变得非常简单,但并不是你会发现它们的每个实现都会考虑到序列化,所以如果你大量使用游戏保存将成为一个相当麻烦的问题协程。

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

      【讨论】:

        【解决方案3】:

        在游戏中使用协程的一种方式是在 Actor 模型中作为轻量级线程,例如在 Kamaelia 中。

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

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

        因此,它并不是真正特定于游戏,但是当您有大量通信组件同时执行时,任何事情都可以从协程中受益。

        【讨论】:

          【解决方案4】:

          您可能会觉得有趣的两个链接:

          http://aigamedev.com/open/articles/coroutine-state-machine/

          http://www.dabeaz.com/coroutines/(在此页面和 pdf 中搜索“任务”)

          【讨论】:

            【解决方案5】:

            想要使用协程来表示单个演员 AI 脚本是很常见的。不幸的是,人们倾向于忘记协程与线程具有相同的同步和互斥问题,只是在更高的级别。因此,您通常需要首先尽可能多地消除本地状态,然后编写可靠的方法来处理协程中当您所指的内容不复存在时出现的错误。

            因此在实践中它们很难从中受益,这就是为什么像 UnrealScript 这样使用一些协同程序的语言将许多有用的逻辑推向原子事件的原因。有些人从中获得了很好的效用,例如。 EVE Online 的人,但他们几乎必须围绕这个概念构建整个系统。 (以及他们如何处理共享资源的争用并没有很好的记录。)

            【讨论】:

            • 协程存在一些同步问题,但前提是它们的状态在 yield() 调用之间不能保持一致。在 yield() 调用之间对所有代码进行隐式锁定可以大大简化事情。最大的问题是,任何时候创建例程时,都应该在那时决定例程是否总是能够在它必须要 yield() 之前完成。如果例程的调用者不期望例程让步,则在调用例程时它可能不会使事物处于一致的状态。添加一个 yield() 将是一个突破性的变化。
            • 问题在于,您通常希望能够在任何地方让这些例程用于任何用途(如在 UnrealScript 中,“潜在函数”隐式产生),但这会导致组合协程以不可预测的方式交互。例如。如果一个巡逻协程说“向北;看;向南;看;重复;”另一个是反应例程,说“如果(向西听到声音){向西;看;向东;}”然后将两者交错可能会导致演员的移动相当不可预测。这就是为什么 UnrealScript 只允许在一个“协程”中使用此类函数。
            • 即使每个actor只有1个协程,这仍然不足以确保逻辑始终在语言级别上工作:如果你的协程说:“拔剑;等待1秒;用剑攻击对手”,那么如果剑因为你被其他玩家缴械而消失了怎么办?特别是,如果您的语言通过使用剑的局部变量来实现该协程,现在该变量引用了一个空对象怎么办?如果您以正常方式使用通用语言,则考虑所有这些情况非常棘手。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-26
            • 1970-01-01
            • 2012-11-20
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多