Answers:
没有正确的方法。您正在描述的内容是可行的,而且速度足够快,这没关系,因为您似乎在问,这是因为您担心设计,而不是因为它引起性能问题。所以这是一个正确的方式,确保万无一失。
当然,您可以采取不同的方式,例如,为每种对象类型保留一个同质列表,或者确保将异类列表中的所有内容组合在一起,从而提高缓存中的代码的一致性,或者允许并行更新。如果不知道游戏的范围和规模,很难说出这样做是否会带来明显的性能提升。
您还可以进一步排除实体的责任-听起来像实体现在同时在更新和呈现自己-更好地遵循“单一责任原则”。通过将各个接口彼此分离,可以提高各个接口的可维护性和灵活性。但是同样,对于我是否很难知道我对你的游戏有什么了解(这基本上是一无是处),使我是否很难从额外的工作中受益?
我建议不要过分强调它,并且要提醒您,如果您发现性能或可维护性问题,则可能是潜在的改进领域。
正如其他人所说,如果速度足够快,那就足够快了。如果您想知道是否有更好的方法,问自己的问题是
我是否发现自己遍历所有对象但仅对它们的一部分进行操作?
如果这样做,则表明您可能希望拥有仅包含相关引用的多个列表。例如,如果您遍历每个游戏对象以渲染它们,但是只需要渲染一部分对象,则可能只需要为那些需要渲染的对象保留一个单独的“可渲染”列表即可。
这样可以减少循环遍历的对象数量,并跳过不必要的检查,因为您已经知道列表中的所有对象都将被处理。
您必须对其进行概要分析,以查看是否获得了很多性能提升。
这几乎完全取决于您的特定游戏需求。它可以完美地用于简单的游戏,但不能在更复杂的系统上运行。如果它适用于您的游戏,请不要担心它,也不要尝试过度设计它,直到出现需求为止。
至于为什么简单方法在某些情况下可能会失败的原因,很难一概而论,因为可能性是无限的,并且完全取决于游戏本身。例如,还提到了其他答案,您可能希望将承担责任的对象归为一个单独的列表。
根据您的游戏设计,这可能是最常见的更改之一。我将趁此机会描述其他一些(更复杂的)示例,但请记住,还有许多其他可能的原因和解决方案。
首先,我将指出,在某些游戏中更新游戏对象并渲染它们可能有不同的要求,因此需要分开处理。更新和渲染游戏对象可能会出现一些问题:
更新游戏对象
在存在对象间相关性的情况下,必须对上述分阶段更新技术进行一些调整。这是因为对象间的依赖关系可能导致控制更新顺序的规则冲突。
换句话说,在某些游戏中,对象可能相互依赖并需要特定的更新顺序。在这种情况下,您可能需要设计一种比列表更复杂的结构来存储对象及其相互依赖关系。
渲染游戏对象
在某些游戏中,场景和对象会创建父子节点的层次结构,并应以正确的顺序进行渲染,并相对于其父级进行转换。在这些情况下,您可能需要一个更复杂的结构,例如场景图(树形结构),而不是一个列表:
例如,其他原因可能是,使用空间分区数据结构以提高执行视图视锥剔除的效率的方式组织对象。
基本上,您要做的是根据需要组成列表。如果您一次性绘制地形对象,那么仅列出它们就很方便了。但是,要使这物有所值,您就需要成千上万的这些,以及许多不是地形的东西。否则,仔细阅读所有内容并使用“ if”拉出地形对象就足够快了。
无论我有多少其他列表,我总是需要一个主列表。通常,我将其制作为Map,键控表或字典,以便可以随机访问各个项目。但是一个简单的列表要简单得多。如果您不随机访问游戏对象,则不需要地图。偶尔地通过几千个条目进行顺序搜索以找到合适的条目不会花费很长时间。所以我想说,无论您做什么,都打算坚持使用主列表。如果地图变大,请考虑使用它。(尽管如果可以使用索引而不是键,则可以坚持使用数组,并拥有比Map更好的东西。我总是以索引号之间的巨大空白而告终,所以我不得不放弃它。)
关于数组的一句话:它们的速度令人难以置信。但是,当它们上升到大约100,000个元素时,它们开始适合垃圾收集器。如果您可以分配空间一次,然后再不碰它,那很好。但是,如果您不断扩展数组,则会不断分配和释放大块内存,并且易于使游戏一次暂停几秒钟,甚至会因内存错误而失败。
如此更快,更高效?当然。随机访问的地图。对于真正的大名单,链表会打一个数组,如果当你并不需要随机访问。多个地图/列表,可根据需要以各种方式组织对象。您可以对更新进行多线程处理。
但这是很多工作。单个阵列既快速又简单。您可以仅在需要时添加其他列表和事物,并且可能不需要它们。只是要知道如果您的游戏变大会出什么问题。您可能需要一个测试版本,其中对象的数量是真实版本中的10倍,以使您了解何时会遇到麻烦。(只有做到这一点,如果你的游戏是越来越大了。)(不要尝试了多线程没有给它花了很多心思的。其他的一切很容易上手,你可以做多可少你喜欢并随时退出。使用多线程,您将付出巨大的代价,停留的代价很高,而且很难退出。)
换句话说,只要继续做自己正在做的事情即可。您最大的限制可能是您自己的时间,并且您正在充分利用这一时间。
对于简单游戏,我使用了某种类似的方法。当满足以下条件时,将所有内容存储在一个阵列中非常有用:
无论如何,我都将此解决方案实现为包含gameObject_ptr
结构的固定大小的数组。该结构包含一个指向游戏对象本身的指针,以及一个表示对象是否为“活动”的布尔值。
布尔值的目的是加快创建和删除操作。我不会将游戏对象从仿真中删除时销毁它们,而只是将“ alive”标志设置为false
。
对对象执行计算时,代码将遍历数组,对标记为“活动”的对象执行计算,并且仅呈现标记为“活动”的对象。
另一个简单的优化方法是按对象类型组织数组。例如,所有项目符号都可以驻留在阵列的最后n个单元中。然后,通过存储带有索引值或指向数组元素的指针的int,可以以降低的成本引用特定类型。
毋庸置疑,此解决方案不适用于具有大量数据的游戏,因为该阵列将无法接受。它也不适用于具有内存限制的游戏,这些内存禁止未使用的对象占用内存。底线是,如果您知道某个时间点的游戏对象数量上限,则将它们存储在静态数组中没有任何问题。
这是我的第一篇文章!希望它能回答您的问题!
可以接受,尽管不寻常。“更快”和“更高效”等术语是相对的;通常,您会将游戏对象分为不同的类别(因此,一个子弹列表,一个敌人列表等等),以使编程工作更有条理(因此效率更高),但这并不是计算机会更快地遍历所有对象。
您说单个数组和对象。
因此,(没有您的代码可供查看)我猜测它们都与“ uberGameObject”相关。
所以也许有两个问题。
1)您可能有一个“神”对象-做很多东西,没有按其作用或作用真正细分。
2)您遍历此列表并强制输入?或拆箱。这有很大的性能损失。
考虑将不同类型(项目符号,坏人等)分解为自己的强类型列表。
List<BadGuyObj> BadGuys = new List<BadGuyObj>();
List<BulletsObj> Bullets = new List<BulletObj>();
//Game
OnUpdate(gameticks gt)
{ foreach (BulletObj thisbullet in Bullets) {thisbullet.Update();} // much quicker.
update()
。
我可以为每种对象类型在一般的单个列表和单独的列表之间提出一些折衷方案。
保持引用多个列表中的一个对象。这些列表由基本行为定义。例如,MarineSoldier
对象将被引用的PhysicalObjList
,GraphicalObjList
,UserControlObjList
,HumanObjList
。因此,当您处理物理学时,您要进行遍历PhysicalObjList
,当过程受毒物损害时- HumanObjList
如果士兵死亡,则将其从中移除,然后HumanObjList
保留PhysicalObjList
。为了使该机制正常工作,您需要在创建/删除对象时组织自动插入/删除对象。
该方法的优点:
AllObjectsList
更新时强制转换)MegaAirHumanUnderwaterObject
将插入到相应列表中,而不是为其类型创建新列表)PS:关于自我更新,当多个对象交互并且它们彼此依赖时,您会遇到问题。为了解决这个问题,我们需要从外部影响对象,而不是在自我更新内部。