协同程序的替代品

abs*_*nce 8 coroutine

此示例已用于另一个问题,以说明协同程序如何用于编写视频游戏中的过场动画:

bob.walkto(jane)
bob.lookat(jane)
bob.say("How are you?")
wait(2)
jane.say("Fine")
...
Run Code Online (Sandbox Code Playgroud)

每个函数产生主动引擎,它在恢复协程之前进行动画,定时等.协程的可能替代方案是事件队列而不是代码,但是必须实现控制逻辑和循环作为事件.是否有其他可用于实现此类功能的协同程序的替代方案?我见过一些文章中提到的回调,但我不确定代码的外观.

Hei*_*erg 3

协程非常适合这种情况,因为您可以轻松地保留所有本地状态变量。即无需手动将其存储在某个上下文中。

但我不认为事件系统可以作为替代方案。除了基于协程的脚本系统之外,更多的是您最可能想要的补充。

示例(在某种程度上连贯的 C++ 中):

您已经使用协程实现了以下行为:

class EnterHouse : public NPCBehavior
{
    EnterHouse(House theHouse) { _theHouse = theHouse; }
    void Begin() { _theHouse.AddNPC(NPC()); }
    void Update()
    {
        NPC().WalkTo(_theHouse.GetDoor().Position());
        NPC().FaceObject(_theHouse.GetDoor());
        NPC().PlayAnimation(NPC().Animations().Get(eAnimKnockOnDoor));
        Sleep(1.0f);       
        NPC().OpenDoor(_theHouse.GetDoor());
    }
    void End() { /* Nothing */ }

    private House _theHouse;
}
Run Code Online (Sandbox Code Playgroud)

想象一下 NPC 上的方法本身会创建 NPCBehavior 对象,将它们推送到某种行为堆栈上,并在这些行为完成时从调用中返回。

Sleep(1.0f)调用将屈服于您的脚本调度程序并允许其他脚本运行。、WalkToFaceObject和也会调用yield PlayAnimation。基于已知的动画持续时间,定期唤醒以查看探路器和运动系统是否完成行走或其他操作。OpenDoorSleep

如果NPC在去门口的路上遇到必须处理的情况怎么办?您不想在基于协程的代码中到处检查所有这些事件。使用事件系统来补充协程将使这变得容易:

垃圾桶翻倒:垃圾桶可以向附近所有 NPC 广播事件。NPC 对象决定将一个新的行为对象推送到他的堆栈上来修复它。该WalkTo行为处于Sleep某处的让步调用中,但现在该FixTrashcan行为由于事件而正在运行。FixTrashcan完成后,行为将从垃圾箱事件WalkTo中醒来,并且永远不会知道。Sleep但它仍将在通往门口的路上,而我们仍在门下奔跑EnterHouse

发生爆炸:爆炸就像垃圾桶一样广播一个事件,但这次 NPC 对象决定重置其运行行为并推送一个FleeInPanic行为。他不会再回来了EnterHouse

我希望您明白我所说的让事件和协程同时存在于人工智能系统中是什么意思。您可以使用协程来保持本地状态,同时仍然屈服于脚本调度程序,并且您可以使用事件来处理中断并保持集中处理它们的逻辑,而不会污染您的行为。

如果您还没有看过Thomas Tong 撰写的有关如何在 C/C++ 中实现单线程协程的文章,我强烈推荐您阅读它。

他只使用最少量的内联汇编(一条指令)来保存堆栈指针,并且代码可以轻松移植到一大堆平台。我已经在 Wintel、Xbox 360、PS3 和 Wii 上运行过它。

调度程序/脚本设置的另一个好处是,如果您需要资源做其他事情,那么饿死屏幕外或远处的人工智能角色/脚本对象就变得微不足道了。只需将其与调度程序中的优先级系统结合起来,就可以开始了。