在我的游戏中,我通常让每个 NPC / 物品等都派生自一个基类“实体”。然后他们基本上都有一个名为“更新”的虚拟方法,我会在每一帧为我的游戏中的每个实体分类。我假设这是一个有很多缺点的模式。还有哪些其他方法可以在整个游戏中管理不同的“游戏对象”?还有其他众所周知的模式吗?我基本上是在寻找比从基类派生的所有东西更好的模型,这将跨越一个巨大的继承树和无处不在的虚函数
2 回答
There is another pattern that involves inheriting for behaviour instead.
For example: A sword (in a fantasy-styled game) could inherit from Describable
to make it being able to be described to the player; It will also inherit from DoDamage
which handles passing out damage; It further inherits from Grabable
so the player can "grab" it.
A piece of armour in turn could inherit from DamageReduction
to lower damage given, as well as Wearable
so the player could wear it.
Instead of a big and deep inheritance tree, you get many smaller and shallower inheritance trees. The drawback is of course that you have to create lots of classes, and think about the different behaviour and events that can happen.
让您的对象在未来的某个时间点请求更新。
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() 实例,这通常并不理想。