gpt4 book ai didi

c++ - 3D 碰撞分辨率,移动 AABB + 多面体

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:17:47 27 4
gpt4 key购买 nike

我一直在利用空闲时间编写一个游戏引擎,但为了让碰撞发挥作用,我被困了几个星期。

目前我用 AABB 表示实体的碰撞体,关卡的碰撞体由一个相当简单(但不一定是凸面)的多面体表示。所有的绘图都是基于 sprite 的,但底层的碰撞代码是 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());
}

我一直在按照“玩家移动方向的碰撞距离”对碰撞面进行排序,但我还没有找到一种有效的方法来找到所有面的距离值。

有人知道一种算法可以更好地实现我想要完成的目标吗?

最佳答案

我对代码的第一部分非常怀疑。您在每次迭代中修改实体的位置,对吗?这或许可以解释奇怪的边缘情况。

在一个 2D 示例中,如果一个正方形走向一个尖角并与两面墙碰撞,它的位置将首先被一堵墙修改,这使得它更多地穿透到第二堵墙。然后第二面墙使用更大的比例值改变它的位置,这样正方形看起来只被一面墙插入。

如果碰撞发生在表面 S 的法线接近玩家运动的地方,它将比所有其他碰撞晚处理。需要注意的是,在处理其他碰撞时,玩家的位置发生了变化,很可能会更多地穿透S面。所以最后程序处理与S面的碰撞,这会导致玩家大量弹出。

我认为有一个简单的解决方法。只需一次计算穿透量,并使用时间变量对所有位移求和,然后根据总位移改变位置。

关于c++ - 3D 碰撞分辨率,移动 AABB + 多面体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41670569/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com