C++ 许多圆的Box2D类碰撞

C++ 许多圆的Box2D类碰撞,c++,collision,C++,Collision,我正试图编写一个非常精简、简单的碰撞器,类似于Box2D——没有所有的物理、旋转等。我这样做是为了使代码占用空间小且易于理解,同时也是为了亲自学习这些东西的内部工作原理 我所要做的就是碰撞圆和线,防止它们相互嵌入 Box2D几乎完美地做到了这一点——只有极少量的重叠!然而,当我编写自己的简单模拟器时,我得到了很多重叠: 当我使用Box2D运行相同的模拟时(这只是所有的圆都在屏幕中心追逐一个点),我没有看到任何可见的重叠 在伪代码中,我是这样做的: For each Circle In List:

我正试图编写一个非常精简、简单的碰撞器,类似于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;
}
}
aO->mPos=x,y对象
aO->mMove=x,y此步骤的移动
aO->mRadius=物体半径
aO->MovingBound()=包含移动的对象的绑定
无效步骤()
{
枚举列表(MCObject、aO、mMovingObjectList)
{
mTree.GetAllNearbyObjects(aO->movingbund().Expand(aO->mRadius/4),&aHitList);
aHitList-=aO;//从列表中删除self
如果(aHitList.GetCount()>0)
{
//按最接近的优先顺序对碰撞进行排序
if(mSortCollisions)
{
//剪,为了清楚起见把这个拿出来。。。
//它只是根据最接近的人对列表进行排序
//指向当前对象
}
//应用未嵌入的
EnumList(MCObject,aO2,aHitList)CollizeObjectObject(aO,aO2);
}
}
//做实际的动作
枚举列表(MCObject、aO、mMovingObjectList)
{
移动(aO->mProxy,aO->Bound(),aO->mMove);
aO->mPos+=aO->mMove;
aO->mMove=0;
}
}
无效碰撞对象对象(MCObject*theO1,MCObject*theO2)
{
浮点AOOVERLAP=G最大距离平方(theO1->mPos+theO1->mMove,theO2->mPos+theO2->mMove);
浮动半径=theO1->mRadius+theO2->mRadius;
if(aOverlapmPos-theO2->mPos);
浮动长度=半径sqrt(aOverlap);
aunEmbedd.SetLength(aunEmbeddLength);
浮动aMod=0.5f;
如果(theO2->McCollideFlags&COLLIDEFLAG_静止)aMod=1.0f;
theO1->mMove+=aUnembed*aMod;
}
}

解决多个对象之间的碰撞是一个相当困难的问题,因为除了碰撞的基本数学之外,还必须更加努力地解决近似解算器累积的数学错误(现实世界中的物理学是基于积分工作的,积分规定了无穷小的时间步;而在我们的模拟中,我们通常每秒只解60次)


让我们看一看:在每个世界步骤中,冲突解决程序都不会只运行一次。它会重复
velocityIterations
次,在官方测试用例中通常设置为6或8次。这也是您必须要做的。

谢谢——我实际上已经尝试过了,而且我确实得到了更好的结果。它看起来非常低效客户端。我不知道Box2D会这样做——我看了一遍,但错过了重复。这就是我所需要知道的。最好看看每次迭代的平均误差,看看更多的迭代如何减少误差。因为你使用的是半隐式Euler,你应该看到迭代量和误差减少之间的线性关系。另外,我刚刚意识到我引用的是连续解算器,它仅用于“子弹”(非常快速移动的对象)的特殊情况.通常,对象只有离散的解算步骤,您可以在中找到。事实证明,它们不检查错误,而只是重复
velocityIterations
次,通常是6或8次。更新了我的答案。-抱歉!这很好地解释了为什么如果我只是循环迭代,它会像Box2d一样工作。因为这就是Box2d工作。这很有趣,因为我来自一个周期很重要的时代……对我来说,在一次更新中运行看似昂贵的代码(看在上帝的份上,它有sqrt)六次是很难适应的。所以我一直在寻找“优雅的、卡马克式的解决方案”。结果是没有一个,如图所示:@Raptisoft您正在谈论的是一个地震式的“Carmackian”解决方案,它几乎不需要任何物理知识(您可能想看看,因为我们都知道,它非常平滑)。我敢肯定,《愤怒》中的物理引擎相当复杂。但另一种实现完全Carmack的方法是接受这样一个事实,即您无法改变现代物理引擎的算法要求,而是将重点放在通过缓存和TLP优化恢复循环上。