2017-01-27 29 views
2

我想写一个非常简洁的,类似于Box2D的简单对撞机 - 没有所有的物理,旋转等。我正在做这两个,以保持代码足迹微小和易于理解,也只是简单地了解这些事情的内部工作。Box2D像很多圈的碰撞

我想要做的就是碰撞圆圈和线条,并防止它们嵌入到彼此中。

Box2D几乎完美地做到了这一点 - 非常少量的重叠!但是,当我编写我自己的简单模拟器时,我得到了很多重叠:picture of overlapped circles

当我使用Box2D运行相同的模拟(这只是在屏幕中心追逐一个点的所有圆圈)时,我在ALL处没有可见的重叠。

伪代码,这是我做的:

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; 
} 
} 

回答

2

解决冲突的是一个相当困难的问题,因为,除了碰撞的数学基础,你必须在固定的数学积累更勤奋地工作来自近似求解器的错误(现实世界中的物理工作基于集成,这意味着无限小的时间步长;而在我们的模拟中,我们通常每秒只解决大约60次)。

让我们来看看Box2D's constraint solver loop, located in b2island.cpp:在世界的每一步,碰撞解析器不仅运行一次。它会重复velocityIterations次,在官方测试用例中通常设置为6或8.这也是你必须要做的。

+1

谢谢 - 我已经尝试过,我确实得到了更好的结果。它看起来效率很低。我不知道Box2D是否做到了这一点 - 我看了一遍,但错过了重申。这就是我需要知道的。 – Raptisoft

+0

在每次迭代中查看中值错误并查看更多迭代如何减少错误是一个好主意。由于您使用的是半隐式Euler,因此您应该会看到迭代次数与误差减少之间的线性关系。 – Domi

+0

另外,我刚才意识到我引用了连续求解器,它只用于“子弹”(非常快速移动的对象)的特殊情况。通常情况下,对象只有离散的求解步骤,您可以在['b2island :: solve']中找到(https://github.com/erincatto/Box2D/blob/374664b2a4ce2e7c24fbad6e1ed34bebcc9ab6bc/Box2D/Box2D/Dynamics/b2Island.cpp#L260 )。原来,他们不检查错误,而只是重复'velocityIterations'次,通常是6或8.更新我的答案。 - 对于那个很抱歉! – Domi