避免虚拟调用循环的模式

Pattern to avoid looping on virtual calls

本文关键字:模式 循环 调用 虚拟      更新时间:2023-10-16

在我的游戏中,我通常让每个NPC/物品等都从基类"实体"派生。然后他们基本上都有一个名为"更新"的虚拟方法,我会在每一帧为游戏中的每个实体分类。我认为这种模式有很多缺点。还有哪些其他方法可以在整个游戏中管理不同的"游戏对象"?这方面还有其他众所周知的模式吗?我基本上是想找到比从基类派生所有东西更好的模型,基类将跨越一个巨大的继承树和遍布的虚拟函数

还有另一种模式涉及行为继承。

例如:一把剑(在幻想风格的游戏中)可以继承Describable,使其能够被玩家描述;它还将继承DoDamage,后者处理传球伤害;它进一步继承了Grabable,因此玩家可以"抓取"它

一件盔甲反过来可以继承DamageReduction,以降低所造成的伤害,以及Wearable,这样玩家就可以穿上它。

你会得到许多更小、更浅的继承树,而不是一棵又大又深的继承树。当然,缺点是你必须创建很多类,并考虑可能发生的不同行为和事件。

让您的对象在未来的某个时间点请求更新。

struct UpdateToken {
UpdateToken() {}
/* ... */
};
class Updater {
// ...
struct UpdateEntry
{
UpdateToken token;
Time when;
bool repeats;
Time frequency
std::function<void()> callback;
};
// Various indexes into a collection of UpdateEntries
// sorted by when and by token so you can look up both ways quickly
public:
UpdateToken RegisterUpdate( Time t, std::function<void()> callback ); // once after time t
UpdateToken RegularUpdate( Time t, std::function<void()> callback ); // every time t
void UnregisterUpdate( UpdateToken );
};
Updater* GetUpdater();
// use:
class Foo
{
UpdateToken token;
void DoUpdate()
{
std::cout << "I did it!n";
}
void PlanRepeatingUpdate( Time t )
{
if (!GetUpdater())
return;
if (token.valid())
GetUpdater()->UnregisterUpdate(token);
token = GetUpdater()->RegularUpdate( t, [&]()->void
{
this->DoUpdate();
});
}
~Foo() { if (token.valid() && GetUpdater()) GetUpdater()->UnregisterUpdate(token); }
};

这里我们有一个未来事件的源(Updater())和j个随机类Foo,它可以在其中注册一个回调,用于一个重复或一系列重复。

您可以在0.1秒内、1秒内或1小时内要求更新。如果你碰巧被销毁,或者不想更新,你可以注销它,但它永远不会到达。

必须在Updater中投入一些精力,这样它既可以快速找到下一个要调用的事件,也可以通过令牌找到更新。我见过一个Boost双索引容器,或者你可以手动处理它,方法是有一个主std::set(或无序集),以及一个具有唯一顺序的第一个集合的辅助迭代器集合,你可以非常小心地管理它(你必须注意是什么使迭代器无效,并确保迭代器集不包含任何无效内容)。

上面还有效地使用了全局Updater()实例,这通常并不理想。