我正在尝试编写一个类似于 Box2D 的非常精简、简单的碰撞器 - 缺少所有物理、旋转等。我这样做既是为了保持代码占用空间小且易于理解,也是为了简单地了解内部这些事情的运作都是个人的。
我想做的就是使圆和线碰撞,并防止它们相互嵌入。
Box2D 几乎完美地做到了这一点——非常少量的重叠!然而,当我编写自己的简单模拟器时,我得到了很多重叠: .
当我使用 Box2D 运行相同的模拟时(这只是所有圆圈追逐屏幕中心的一个点),我根本没有看到任何可见的重叠。
在伪代码中,这就是我所做的:
For each Circle In List:
Determine who will collide with the circle in next step
Sort collisions by closest first
For each possible collision:
Add the unembed vector to the Circle's movement vector
...and then:
For each Circle In List:
At the movement to the circle
所以,如果圆圈没有被插入其他任何东西,这也可以完美地工作。然而,当事情堆积起来时,它就不起作用了,我知道为什么——未嵌入的东西只会堆积起来,每个人都会推挤和推挤,因为后面的圆圈没有嵌入到前面的圆圈中,而在模拟结束时,一些圆圈只是卡在了其他圆圈内。非常有道理。
这是我感到困惑的地方: 据我所知,Box2D 的运行方式完全相同——实现可能的碰撞,彼此解除嵌入,完成。但 Box2D 永远不会像我的那样重叠(或者它使它们变得如此小以至于无关紧要)。
有人可以告诉我我在这里错过了哪一步吗?我可以进行调整来改进事情(比如迭代一次又一次碰撞的任何人......但 Box2D 似乎没有这样做,我想在保持代码轻便和快速的同时理解)。
谢谢!
相关真实代码如下:
aO->mPos = x,y of object
aO->mMove = x,y of movement this step
aO->mRadius = radius of object
aO->MovingBound() = bound of object including the move
void Step()
{
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.GetAllNearbyObjects(aO->MovingBound().Expand(aO->mRadius/4),&aHitList);
aHitList-=aO; // Remove self from list
if (aHitList.GetCount()>0)
{
// Sort the collisions by closest first
if (mSortCollisions)
{
// Snip, took this out for clarity...
// It just sorts aHitList by who is closest
// to the current object
}
// Apply the unembedding
EnumList(MCObject,aO2,aHitList) CollideObjectObject(aO,aO2);
}
}
// Do the actual moves
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.Move(aO->mProxy,aO->Bound(),aO->mMove);
aO->mPos+=aO->mMove;
aO->mMove=0;
}
}
void CollideObjectObject(MCObject* theO1, MCObject* theO2)
{
float aOverlap=gMath.DistanceSquared(theO1->mPos+theO1->mMove,theO2->mPos+theO2->mMove);
float aMixRadius=theO1->mRadius+theO2->mRadius;
if (aOverlap<=aMixRadius*aMixRadius)
{
Point aUnembed=(theO1->mPos-theO2->mPos);
float aUnembedLength=aMixRadius-sqrt(aOverlap);
aUnembed.SetLength(aUnembedLength);
float aMod=.5f;
if (theO2->mCollideFlags&COLLIDEFLAG_STATIONARY) aMod=1.0f;
theO1->mMove+=aUnembed*aMod;
}
}
最佳答案
解决许多对象之间的碰撞是一个相当困难的问题,因为除了碰撞的基本数学之外,您还必须更加努力地修复来自近似求解器的数学误差的累积(现实世界中的物理工作)基于积分,它规定了无限小的时间步长;而在我们的模拟中,我们通常每秒只求解大约 60 次)。
让我们看看Box2D's constraint solver loop, located in b2island.cpp :在每个世界步骤中,碰撞解析器不仅仅运行一次。它将重复 velocityIterations
次,在官方测试用例中通常设置为 6 或 8。这也是您必须做的。
关于c++ - 许多圆的类似于 Box2D 的碰撞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41902594/