如果这是您第一次遇到此问题,建议您先阅读下面的更新前部分,然后阅读本部分。 不过,这是问题的综合内容:
基本上,我有一个带有网格空间分区系统的碰撞检测和解决引擎,碰撞顺序和碰撞组很重要。一次必须移动一个身体,然后检测碰撞,然后解决碰撞。如果我一次移动所有物体,然后生成可能的碰撞对,则显然速度更快,但是由于不遵守碰撞顺序,因此分辨率会下降。如果我一次移动一个身体,我将不得不让身体检查碰撞,这将成为一个^ 2问题。将组混合在一起,您可以想象为什么在很多身体上它变得非常慢。
更新:我已经为此付出了很多努力,但是无法优化任何东西。
我成功实现了Will所描述的“绘画”,并将组更改为位集,但这是非常非常小的加速。
我还发现了一个大问题:我的引擎取决于冲突顺序。
我尝试了一种独特的碰撞对生成的实现,该实现肯定可以大大加快一切,但是却破坏了碰撞的顺序。
让我解释:
在我的原始设计中(不生成对),发生这种情况:
- 一个身体移动
- 移动后,它会刷新其单元格并使其碰撞到的身体
- 如果它与需要解决的物体重叠,则解决碰撞
这意味着,如果一个物体移动并撞到墙壁(或任何其他物体),则只有已移动的物体才能解决其碰撞,而另一个物体将不受影响。
这是我想要的行为。
我了解到物理引擎并不常见,但对于复古风格的游戏却有很多优势。
在通常的网格设计(生成唯一对)中,会发生以下情况:
- 所有身体移动
- 在所有身体移动之后,刷新所有单元格
- 生成唯一的碰撞对
- 对于每对,处理碰撞检测和解决
在这种情况下,同时移动可能会使两个物体重叠,并且它们将同时分解-这有效地使物体“相互推挤”,并破坏了与多个物体的碰撞稳定性
这种行为对于物理引擎是很常见的,但在我的情况下是不可接受的。
我还发现了另一个主要问题(即使在现实情况中不太可能发生):
- 考虑A,B和W组的身体
- A与W和A相撞并解决
- B与W和B相撞并下定决心
- A对B无能为力
- B对A无所作为
可能存在许多A主体和B主体占据同一个单元的情况-在这种情况下,主体之间存在很多不必要的迭代,这些迭代不能相互反应(或仅检测碰撞但不能解决它们) 。
对于占据同一单元的100个物体,这是100 ^ 100次迭代!发生这种情况是因为没有生成唯一对 -但是我无法生成唯一对,否则我将得到我不希望的行为。
有没有一种方法可以优化这种碰撞引擎?
这些是必须遵守的准则:
碰撞顺序非常重要!
- 身体必须一次移动一个,然后一次检查一个碰撞,然后一次移动一个就解决。
机构必须具有3个群组位组
- 组:身体所属的组
- GroupsToCheck:人体必须检测到碰撞的组
- GroupsNoResolve:团体不能解决的碰撞
- 在某些情况下,我只希望检测到碰撞但不能解决
更新前:
前言:我知道优化此瓶颈不是必需的-引擎已经非常快。但是,出于娱乐和教育目的,我很想找到一种使引擎更快的方法。
我正在创建一个通用的C ++ 2D碰撞检测/响应引擎,重点是灵活性和速度。
这是其架构的非常基本的图:
基本上,主类是World
,它拥有(管理内存)a ResolverBase*
,a SpatialBase*
和a vector<Body*>
。
SpatialBase
是处理宽相碰撞检测的纯虚拟类。
ResolverBase
是处理冲突解决的纯虚拟类。
实体World::SpatialBase*
与SpatialInfo
实体本身拥有的对象通信。
当前,有一个空间类别:Grid : SpatialBase
,这是一个基本的固定2D网格。它具有自己的信息类GridInfo : SpatialInfo
。
以下是其架构的外观:
所述Grid
类拥有的2D阵列Cell*
。本Cell
类包含的集合(不是所有)Body*
:一个vector<Body*>
其中包含了所有单元格中的尸体。
GridInfo
对象还包含指向主体所在单元格的非所有指针。
如前所述,引擎基于组。
Body::getGroups()
返回std::bitset
身体所属的所有组中的一个。Body::getGroupsToCheck()
返回std::bitset
身体必须检查碰撞的所有组中的一个。
身体可以占据一个以上的细胞。GridInfo始终将非所有者指针存储到占用的单元格。
单个身体移动后,就会发生碰撞检测。我假设所有物体都是与轴对齐的边界框。
广相碰撞检测如何工作:
第1部分:空间信息更新
对于每个Body
body
:
- 计算最左上角的占用单元格和最右下角的占用单元格。
- 如果它们与先前的单元格不同,则将
body.gridInfo.cells
其清除,并填充身体所占据的所有单元格(从最左上角的单元格到最右下角的单元格为2D循环)。
body
现在保证知道它占用了什么细胞。
第2部分:实际碰撞检查
对于每个Body
body
:
body.gridInfo.handleCollisions
叫做:
void GridInfo::handleCollisions(float mFrameTime)
{
static int paint{-1};
++paint;
for(const auto& c : cells)
for(const auto& b : c->getBodies())
{
if(b->paint == paint) continue;
base.handleCollision(mFrameTime, b);
b->paint = paint;
}
}
void Body::handleCollision(float mFrameTime, Body* mBody)
{
if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;
auto intersection(getMinIntersection(shape, mBody->getShape()));
onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
mBody->onDetection({*this, mFrameTime, userData, -intersection});
if(!resolve || mustIgnoreResolution(*mBody)) return;
bodiesToResolve.push_back(mBody);
}
然后,解决每个人体的碰撞
bodiesToResolve
。而已。
因此,我已经尝试优化这种广相碰撞检测了一段时间了。每次我尝试使用当前体系结构/设置以外的其他方法时,都没有按计划进行,或者我对模拟进行了假设,后来被证明是错误的。
我的问题是:如何优化碰撞引擎的广泛阶段?
是否可以在此处应用某种神奇的C ++优化?
是否可以重新设计架构以实现更高的性能?
- 实际实现:SSVSCollsion
- Body.h, Body.cpp
- World.h, World.cpp
- Grid.h, Grid.cpp
- Cell.h, Cell.cpp
- GridInfo.h, GridInfo.cpp
最新版本的Callgrind输出:http : //txtup.co/rLJgz
getBodiesToCheck()
被调用5462334次,并占用了整个配置时间的35.1%(指令读取访问时间)