许多圆圈的类似 Box2D 的碰撞
Box2D-like collision of lots of circles
我正在尝试编写一个非常精简、类似于 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;
}
}
解决许多对象之间的碰撞是一个相当困难的问题,因为除了碰撞的基本数学之外,您还必须更加努力地修复来自近似求解器(实际中的物理基于积分的世界工作要求无限小time-steps;而在我们的模拟中,我们通常每秒只解决大约 60 次)。
让我们看看Box2D's constraint solver loop, located in b2island.cpp:在每个世界步骤中,碰撞解决器不仅运行一次。它会重复 velocityIterations
次,在官方测试用例中通常设置为 6 或 8 次。这也是你必须做的。
我正在尝试编写一个非常精简、类似于 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;
}
}
解决许多对象之间的碰撞是一个相当困难的问题,因为除了碰撞的基本数学之外,您还必须更加努力地修复来自近似求解器(实际中的物理基于积分的世界工作要求无限小time-steps;而在我们的模拟中,我们通常每秒只解决大约 60 次)。
让我们看看Box2D's constraint solver loop, located in b2island.cpp:在每个世界步骤中,碰撞解决器不仅运行一次。它会重复 velocityIterations
次,在官方测试用例中通常设置为 6 或 8 次。这也是你必须做的。