我一直在空閒時間寫一個遊戲引擎,但是我一直堅持幾個星期試圖讓碰撞工作。3D碰撞分辨率,移動AABB +多面體
目前,我用AABB表示實體的碰撞體,而一個關卡的碰撞體由一個相當簡單的(但不一定是凸的)多面體表示。所有圖形都是基於精靈的,但底層的碰撞代碼是3D。我已經使用this算法(我可以天真地應用於關卡網格中的每個面孔)運行AABB /三角形碰撞檢測,但是我在檢測到它存在後試圖解決碰撞問題時卡住了。
我提出的算法工作得很好,但有一些邊緣情況下會中斷。例如,直接走向尖銳的角落總是會將玩家推向一側或另一側。或者,如果一個小碰撞面碰巧具有比所有其他面部更接近玩家運動方向的法線,則會首先在該方向上「彈出」該玩家,即使使用來自不同面部的偏移將會具有效果更好。
僅供參考,我目前的算法是這樣的:
Create list of all colliding faces
Sort list in increasing order of the angle between face normal and negative direction of entity movement (i.e. process faces with the most "stopping power" first)
For each colliding face in collision list:
scale = distance of collision along face normal
Entity position += face normal * scale
If no more collision:
break
而這裏的實現:
void Mesh::handleCollisions(Player& player) const
{
using Face = Face<int32_t>;
BoundingBox<float> playerBounds = player.getGlobalBounds();
Vector3f negPlayerDelta = -player.getDeltaPos(); // Negative because face norm should be opposite direction of player dir
auto comparator = [&negPlayerDelta](const Face& face1, const Face& face2) {
const Vector3f norm1 = face1.normal();
const Vector3f norm2 = face2.normal();
float closeness1 = negPlayerDelta.dot(norm1)/(negPlayerDelta.magnitude() * norm1.magnitude());
float closeness2 = negPlayerDelta.dot(norm2)/(negPlayerDelta.magnitude() * norm2.magnitude());
return closeness1 > closeness2;
};
std::vector<Face> collidingFaces;
for (const Face& face : _faces)
{
::Face<float> floatFace(face);
if (CollisionHelper::collisionBetween(playerBounds, floatFace))
{
collidingFaces.push_back(face);
}
}
if (collidingFaces.empty()) {
return;
}
// Process in order of "closeness" between player delta and face normal
std::sort(collidingFaces.begin(), collidingFaces.end(), comparator);
Vector3f totalOffset;
for (const Face& face : collidingFaces)
{
const Vector3f& norm = face.normal().normalized();
Point3<float> closestVert(playerBounds.xMin, playerBounds.yMin, playerBounds.zMin); // Point on AABB that is most negative in direction of norm
if (norm.x < 0)
{
closestVert.x = playerBounds.xMax;
}
if (norm.y < 0)
{
closestVert.y = playerBounds.yMax;
}
if (norm.z < 0)
{
closestVert.z = playerBounds.zMax;
}
float collisionDist = closestVert.vectorTo(face[0]).dot(norm); // Distance from closest vert to face
Vector3f offset = norm * collisionDist;
BoundingBox<float> newBounds(playerBounds + offset);
totalOffset += offset;
if (std::none_of(collidingFaces.begin(), collidingFaces.end(),
[&newBounds](const Face& face) {
::Face<float> floatFace(face);
return CollisionHelper::collisionBetween(newBounds, floatFace);
}))
{
// No more collision; we are done
break;
}
}
player.move(totalOffset);
Vector3f playerDelta = player.getDeltaPos();
player.setVelocity(player.getDeltaPos());
}
我一直在搞亂播放器的方向由「碰撞距離排序的碰撞面運動「,但我還沒有找到找到所有人臉距離值的有效方法。
有沒有人知道一個算法,會更好地爲我所要完成的工作?
你目前的代碼與你的問題有什麼關係? – xaxxon
主要供參考/上下文。但我認爲我目前的算法非常接近正確;它可能只需要一些小修改來修復邊緣情況。 – 0x5453