像Source这样的Engine如何处理实体?


9

在Source引擎上(它的前身是goldsrc,quake),游戏对象分为两种:世界和实体。世界是地图的几何形状,实体是播放器,粒子,声音,乐谱等(对于Source Engine)。

每个实体都有一个思考功能,该功能执行该实体的所有逻辑。

因此,如果需要处理的所有内容都来自具有think函数的基类,则游戏引擎可以将所有内容存储在列表中,并在每一帧上循环遍历并调用该函数。

乍一看,这个想法是合理的,但是如果游戏中有很多实体,它可能会占用太多资源。

那么,像Source这样的引擎如何处理(处理,更新,绘制等)游戏对象?


2
为什么重要<some commercial engine>呢?
共产党鸭子

8
@共产主义鸭子,我想这里的真正问题是,它如何成为一个成功的引擎,以便我可以向他们学习?
2011年

Answers:


5

好吧,几乎没有其他方法可以做到这一点-您将不得不循环遍历并think()至少每隔几帧调用一次每个实体。

您可以将实体放在自己的线程上,但是然后您将面临整个状态同步的噩梦,这绝对不值得。

乍一看,这个想法是合理的,但是如果游戏中有很多实体,它可能会占用太多资源。

因此,源引擎对一次可以存在的实体数量设置了硬限制:4096个实体,其中只有一半(2048)可以联网。超过这些限制之一,游戏将崩溃。

这就是为什么在创建地图时,他们建议您使用的实体不要超过800个。


2 ^ 12函数调用是否仍不是每个帧的BIG编号?
JulioC 2011年

@Júlio:好吧,以60 fps的速度每秒可以进行246k函数调用-可以很多,但是绝对可以在当今的硬件上实现。不过请记住,这是Source引擎崩溃之前允许的绝对最大值 -通常,地图上的实体要少得多。
BlueRaja-Danny Pflughoeft

5
实体有一个下一个思考时间,think函数不会被称为everyframe,而不是针对所有实体。我记得对于地震2来说,最小思考时间是0.1(100毫秒),对于实体处理来说只有10fps。
bcsanches 2011年

“您可以将实体放在自己的线程上,但是然后您将面临整个状态同步的噩梦,这绝对不值得。” 如果您认为这不值得,请查看这些Killzone 4幻灯片:de.slideshare.net/jrouwe/…–
塔拉,

3

您提到的这些步骤很可能是在单独的引擎中完成的。只是简单的游戏引擎通常一口气就能拥有它们。您的顺序

for each object
    do physics
    do game logic
    draw

变成

call physics subsystem
call game logic subsystem
call drawing subsystem

物理引擎负责位置和大小。

Game Logic Engine负责解释物理引擎发生了什么变化(他可能会阻塞某些航路点...),角色具有哪些目标以及他们应采取的行为,他运行预定的脚本(此思考功能)。

Drawing Engine绘制可见的对象,并且他知道哪些对象可见,因为Quake引擎在此处作弊(请参见Draw部分)。

我对您的建议是宁愿研究模拟的完成方式,而不是研究游戏引擎。流行文化与游戏开发相关,游戏引擎采用命令式语言(由于传统和速度)制作;因此,获得一本好的教科书(而不是理论)对我来说更具有启发性,然后他们着眼于引擎(实践),而不是着眼于引擎,并费了好几个小时来思考它们是如何做到的。

物理

迭代所有实体并执行{think,draw}的整个概念可能会导致问题。会有冲突等等。我相信Valve有Havok,我想Havok会照顾到足够正确的物理学。

认为

当游戏中的时间等于下一思维中的时间时,便会运行思考功能。它在Quake引擎中以这种方式工作,而Quake引擎是Half Life引擎的基础。它不是每次都运行。

在内部,这应该是简单的遍历实体列表并检查是否已经过去了调用think函数的时间。时间复杂度将为O(N),其中N是实体数。

如果实体太多,则应测量将提高fps的程度。注意,由于阿姆达尔定律,这可能是看不见的加速。我的意思是,您只需遍历所有项目并减少并检查一个数字即可。

我会通过按nextthink排序实体(创建指向实体的指针列表并每次对其进行排序;而不是实体数组)来加快速度,因为实体可能随时更改其nextthink,因此将它们重新排列在数组中需要O(N)而不是O( 1)在清单中)。

您还应该查看Linux中的O(1)调度程序

引擎从相机所在的区域绘制大致可见的图像。游戏级别是划分为一棵树,而区域是该树的叶子。我不会为您提供详细信息 ……因此,如果一个实体可见,则将其放入一组可见实体中并进行绘制。

它们存储哪些区域是潜在的可见区域。它称为“潜在可见集”,简称PVS。有可视化的PVS,绿色的胶囊是玩家,并且在他周围呈现了PVS包含的内容。


2

因此,如果需要处理的所有内容都来自具有think函数的基类,则游戏引擎可以将所有内容存储在列表中,并在每一帧上循环遍历并调用该函数。

乍一看,这个想法是合理的,但是如果游戏中有很多实体,它可能会占用太多资源。

实际上,将所有内容都放在一个大列表中通常不太理想。如果要根据(例如)实体的类型将实体分组在列表中,则可以更好地将处理分布在多个线程上。例如,如果您知道所有Foo类型的实体在仿真阶段都不会与任何其他实体交互,则可以完全卸载它们。如果将它们散布在某些大单数列表中,这将变得更加困难。

此时,您甚至不必从通用基类派生所有内容。Source在继承滥用方面大为落伍,否则在这方面可以作为数据实现。

当然,即使您开始将工作转移到其他内核,您在每帧可以处理的实体数量上也始终会有上限。没有解决的办法,您只需要了解实现中的限制,并采取措施缓解该限制(正确选择不需要它们的对象的处理阶段,避免对象中的粒度过大,等等)。等等)。


1

您必须考虑的是,遵循先前答案的思路是,您的表现还将取决于何时以及如何调用这些思维功能。

查看您在源引擎上发布的链接,您还可以阅读到可以为每个实体设置思考时间和不同的思考上下文,除了有人已经指出的明显的硬限制之外,这将是获得更高性能的关键拥有更多实体,可以通过创建逐步更新来在多个执行帧中分散对性能要求不高的处理,也可以根据当前上下文剔除不需要的处理(即,距离太远或超出玩家感知范围的实体不需要与“接近玩家”角色具有相同级别的“思考细节”,根本不会看到2英里以外的角色ing鼻子。

根据您特定的游戏逻辑和情况,还有其他更具体的优化级别。


“玩家根本不会看到2英里外的角色正在挖鼻子”哈哈!但是,没有人指出使用虚函数非常慢吗?
塔拉2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.