3

我有一个组织游戏循环的想法。我对性能有些怀疑。可能有更好的做事方式。

假设您有一系列游戏组件。他们都被要求在每次游戏循环迭代中做一些事情。例如:

GameData data; // shared
app.registerComponent("AI", ComponentAI(data) );
app.registerComponent("Logic", ComponentGameLogic(data) );
app.registerComponent("2d", Component2d(data) );
app.registerComponent("Menu", ComponentMenu(data) )->setActive(false);
//...
while (ok)
{
//...
app.runAllComponents();
//...
}

好处:

  1. 良好的基于​​组件的应用程序,无依赖关系,良好的模块化
  2. 我们可以动态激活/停用、注册/注销组件
  3. 一些组件可以透明地删除或替换,系统仍然可以正常工作,因为没有发生任何事情(将 2d 更改为 3d)(团队合作:每个程序员都创建他/她自己的组件,并且不需要其他组件来编译代码)

疑点:

  1. 游戏循环中的内部循环,对 Component::run() 进行虚拟调用
  2. 我希望 Component::run() 返回 bool 值并检查该值。如果返回 false,则必须停用组件。所以内循环变得更加昂贵。

那么,这个解决方案有多好?你在实际项目中使用过它吗?

4

4 回答 4

5

一些 C++ 程序员对虚函数的开销有太多的担忧。与函数所做的任何事情相比,虚拟调用的开销通常可以忽略不计。布尔检查也不是很昂贵。

做任何最容易维护的代码。仅当您需要时才进行优化。如果您确实发现需要优化,那么消除虚拟呼叫可能不是您需要的优化。

于 2010-04-20T22:03:55.373 回答
4

在大多数“真实”游戏中,对组件之间的相互依赖关系有非常严格的要求,并且顺序确实很重要。

这可能会或可能不会影响您,但在用户交互处理之前(或之后)让物理生效通常很重要,具体取决于您的场景等。在这种情况下,您可能需要一些额外的处理才能正确排序。

此外,由于您很可能会有某种形式的场景图或空间分区,因此您需要确保您的“组件”也可以利用这一点。这可能意味着,根据您当前的描述,您会在树上行走太多次。不过,这也可以通过设计决策来解决。话虽如此,某些组件可能只对空间分区的某些部分感兴趣,同样,您需要进行适当的设计。

于 2010-04-20T22:05:32.287 回答
1

我在模块化合成音频文件生成器中使用了类似的方法。

我似乎记得注意到在编写了 100 个不同的模块之后,在编写新模块时会对性能产生影响。

总的来说,我觉得这是一个很好的方法。

于 2010-04-20T22:06:18.503 回答
1

也许我是老派,但我真的看不到通用组件的价值,因为我看不到它们在运行时被换掉。

struct GameObject
{
   Ai* ai;
   Transform* transform;
   Renderable* renderable;
   Collision* collision;
   Health* health;
};

这适用于从玩家到敌人再到天空盒和触发器的所有内容;只需将给定对象中不需要的“组件”保留为 NULL。你想把所有的人工智能放到一个列表中吗?然后在施工时这样做。使用多态性,您可以在其中加入各种不同的行为(例如,玩家的“AI”正在翻译控制器输入),除此之外,一切都不需要通用基类。无论如何,它会做什么?

您的“更新所有内容”必须明确调出每个列表,但这不会改变您必须输入的数量,它只是移动它。不是模糊地设置需要全局操作的集合集,而是在操作完成时显式枚举需要操作的集合。

恕我直言,并不是虚拟通话很慢。这是游戏实体的“组件”不是同质的。他们都做着截然不同的事情,因此以不同的方式对待他们是有意义的。事实上,它们之间没有重叠,所以我再次问,如果你不能以任何有意义的方式使用指向该基类的指针而不将其转换为其他东西,那么基类的意义何在?

于 2010-04-23T19:13:01.807 回答