【发布时间】:2017-01-16 06:25:07
【问题描述】:
我在空闲时间一直在编写游戏引擎,但我被困了几个星期试图让碰撞工作。
目前我用 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());
}
我一直在用“玩家移动方向上的碰撞距离”对碰撞面进行排序,但我还没有找到一种有效的方法来找到所有面的距离值。
有没有人知道一种算法可以更好地完成我想要完成的任务?
【问题讨论】:
-
您当前的代码与您的问题有什么关系?
-
主要用于参考/上下文。但我确实认为我目前的算法非常接近正确;修复边缘情况可能只需要一些小的修改。
标签: c++ algorithm 3d collision-detection