Я пытаюсь написать очень урезанный простой коллайдер, похожий на Box2D--, отсутствующий вся физика, вращение и т. Д. Я делаю это как для того, чтобы держать след в коде крошечным и понятным , а также просто изучить внутреннюю работу этих вещей лично.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
Таким образом, если окружности не получить толкнул на что-нибудь другое, это также работает отлично. Когда вещи накапливаются, тем не менее, это НЕ работает, и я знаю, почему - unembeds просто накапливаются, и все толкаются и толкаются, потому что более поздние круги становятся неотвязными в более ранние круги, а в конце моделирования некоторые просто застревают внутри других , Огромный смысл.
Вот где я смущен: Рядом, как я могу судить, 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;
}
}
Спасибо ... Я действительно пробовал это, и у меня лучшие результаты. Это кажется очень неэффективным. Я не знал, что Box2D сделал это ... Я просмотрел его, но пропустил повторение. Это все, что мне нужно было знать. – Raptisoft
Было бы неплохо посмотреть на среднюю ошибку на итерацию и посмотреть, как больше итераций уменьшает ошибку. Поскольку вы используете полу-неявный Euler, вы должны увидеть линейную корреляцию между количеством итераций и уменьшением ошибок. – Domi
Кроме того, я просто понял, что я цитирую непрерывный решатель, который используется только в специальном случае «пули» (очень быстро движущиеся объекты). Обычно объекты имеют только дискретные шаги решателя, которые вы можете найти в ['b2island :: solve'] (https://github.com/erincatto/Box2D/blob/374664b2a4ce2e7c24fbad6e1ed34bebcc9ab6bc/Box2D/Box2D/Dynamics/b2Island.cpp#L260).Оказывается, они не проверяют наличие ошибок, а скорее повторяют 'speedIterations' раз, обычно 6 или 8. Обновлен мой ответ. - Извини за это! – Domi