游戏循环性能和组件方法
我有一个组织游戏循环的想法。我对性能有些怀疑。可能有更好的做事方法。
假设您有一系列游戏组件。他们都被要求在每个游戏循环迭代中做一些事情。例如:
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();
//...
}
优点:
- 良好的基于组件的应用程序,无依赖性,良好的模块化,
- 我们可以动态激活/停用、注册/取消注册组件,
- 某些组件可以透明地删除或替换,并且系统仍将正常工作,就像什么都没有发生一样(将 2d 更改为3d)(团队合作:每个程序员创建他/她自己的组件,不需要其他组件来编译代码)
疑问:
- 游戏循环中的内部循环,虚拟调用 Component::run()
- 我想要 Component:: run() 返回 bool 值并检查该值。如果返回 false,则必须停用组件。因此内循环变得更加昂贵。
那么,这个解决方案有多好呢?你在实际项目中使用过吗?
I have an idea of organising a game loop. I have some doubts about performance. May be there are better ways of doing things.
Consider you have an array of game components. They all are called to do some stuff at every game loop iteration. For example:
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();
//...
}
Benefits:
- good component-based application, no dependencies, good modularity
- we can activate/deactivate, register/unregister components dynamically
- some components can be transparently removed or replaced and the system still will be working as nothing have happened (change 2d to 3d)(team-work: every programmer creates his/her own components and does not require other components to compile the code)
Doubts:
- inner loop in the game loop with virtual calls to Component::run()
- I would like Component::run() to return bool value and check this value. If returned false, component must be deactivated. So inner loop becomes more expensive.
Well, how good is this solution? Have you used it in real projects?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
一些 C++ 程序员对虚函数的开销有太多的担心。与函数所做的任何事情相比,虚拟调用的开销通常可以忽略不计。布尔检查也不是很昂贵。
尽一切努力使代码最容易维护。仅当您需要时才进行优化。如果您确实发现需要优化,消除虚拟调用可能不是您需要的优化。
Some C++ programmers have way too many fears about the overhead of virtual functions. The overhead of the virtual call is usually negligible compared to whatever the function does. A boolean check is not very expensive either.
Do whatever results in the easiest-to-maintain code. Optimize later only if you need to do so. If you do find you need to optimize, eliminating virtual calls will probably not be the optimization you need.
在大多数“真实”游戏中,对组件之间的相互依赖关系有相当严格的要求,并且顺序确实很重要。
这可能会或可能不会影响您,但根据您的场景等,让物理在用户交互处理之前(或之后)生效通常很重要。在这种情况下,您可能需要一些额外的处理才能正确排序。
另外,由于您很可能会拥有某种形式的场景图或空间分区,因此您需要确保您的“组件”也可以利用它。这可能意味着,根据您当前的描述,您会在树上行走太多次。不过,这可以通过设计决策来解决。话虽如此,某些组件可能只对空间分区的某些部分感兴趣,同样,您需要进行适当的设计。
In most "real" games, there are pretty strict requirements for interdependencies between components, and ordering does matter.
This may or may not effect you, but it's often important to have physics take effect before (or after) user interaction proecssing, depending on your scenario, etc. In this situation, you may need some extra processing involved for ordering correctly.
Also, since you're most likely going to have some form of scene graph or spatial partitioning, you'll want to make sure your "components" can take advantage of that, as well. This probably means that, given your current description, you'd be walking your tree too many times. Again, though, this could be worked around via design decisions. That being said, some components may only be interested in certain portions of the spatial partition, and again, you'd want to design appropriately.
我在模块化合成音频文件生成器中使用了类似的方法。
我似乎记得注意到,在编写了 100 个不同的模块后,在编写新模块时会对性能产生影响。
但总的来说,我觉得这是一个很好的方法。
I used a similar approach in a modular synthesized audio file generator.
I seem to recall noticing that after programming 100 different modules, there was an impact upon performance when coding new modules in.
On the whole though,I felt it was a good approach.
也许我是老派,但我真的没有看到通用组件的价值,因为我没有看到它们在运行时被交换。
这适用于从玩家到敌人再到天空盒和触发器的所有内容;只需将给定对象中不需要的“组件”保留为 NULL 即可。您想将所有 AI 放入一个列表中吗?然后在施工时就这样做。通过多态性,您可以将各种不同的行为固定在其中(例如,玩家的“AI”正在翻译控制器输入),除此之外,不需要为所有内容提供通用基类。无论如何,它会做什么?
您的“更新所有内容”必须显式调用每个列表,但这不会改变您必须执行的输入量,它只是移动它。您不是混淆地设置需要全局操作的集合,而是在操作完成时显式枚举需要操作的集合。
恕我直言,并不是虚拟呼叫很慢。而是游戏实体的“组件”不是同质的。他们所做的事情截然不同,因此以不同的方式对待他们是有道理的。事实上,它们之间没有重叠,所以我再次问,如果您不能以任何有意义的方式使用指向该基类的指针而不将其转换为其他内容,那么基类的意义何在?
Maybe I'm oldschool, but I really don't see the value in generic components because I don't see them being swapped out at runtime.
This works for everything from the player to enemies to skyboxes and triggers; just leave the "components" that you don't need in your given object NULL. You want to put all of the AIs into a list? Then just do that at construction time. With polymorphism you can bolt all sorts of different behaviors in there (e.g. the player's "AI" is translating the controller input), and beyond this there's no need for a generic base class for everything. What would it do, anyway?
Your "update everything" would have to explicitly call out each of the lists, but that doesn't change the amount of typing you have to do, it just moves it. Instead of obfuscatorily setting up the set of sets that need global operations, you're explicitly enumerating the sets that need the operations at the time the operations are done.
IMHO, it's not that virtual calls are slow. It's that a game entity's "components" are not homogenous. They all do vastly different things, so it makes sense to treat them differently. Indeed, there is no overlap between them, so again I ask, what's the point of a base class if you can't use a pointer to that base class in any meaningful way without casting it to something else?