像大量圆圈的Box2D式碰撞

时间:2017-01-27 20:36:01

标签: c++ collision

我正在尝试编写一个非常精简的简单对撞机,类似于Box2D--没有所有的物理,旋转等。我这样做是为了保持代码足迹微小且易于理解,并且还要简单地学习内部这些事情的个人运作。

我要做的就是碰撞圆圈和线条,防止它们互相嵌入。

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

当我使用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;
}
}

1 个答案:

答案 0 :(得分:2)

解决许多物体之间的碰撞是一个非常困难的问题,因为除了基本的碰撞数学之外,你还必须更加努力地解决来自近似求解器的数学误差的积累(现实世界中的物理学工作)基于整合,它规定了无限小的时间步长;而在我们的模拟中,我们通常每秒仅解决约60次。)

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