体素世界中的碰撞检测

Collision detection in voxel world

我现在有点受困于我的基本体素物理学。它非常非常起伏不定,我很确定我的数学某处被打破了,但让我们看看你要说什么:

// SOMEWHERE AT CLASS LEVEL (so not being reinstantiated every frame, but persisted instead!)
glm::vec3 oldPos;

// ACTUAL IMPL
glm::vec3 distanceToGravityCenter =
        this->entity->getPosition() -
        ((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time

if (!entity->grounded) {
    glm::vec3 entityPosition = entity->getPosition();

    if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) {
        glm::vec3 dir = entityPosition - oldPos; // Actually no need to normalize as we check for lesser, bigger or equal to 0

        std::cout << "falling dir: " << glm::to_string(dir) << std::endl;

        // Calculate offset (where to put after hit)
        int x = dir.x;
        int y = dir.y;
        int z = dir.z;

        if (dir.x >= 0) {
            x = -1;
        } else if (dir.x < 0) {
            x = 1;
        }

        if (dir.y >= 0) {
            y = -1;
        } else if (dir.y < 0) {
            y = 1;
        }

        if (dir.z >= 0) {
            z = -1;
        } else if (dir.z < 0) {
            z = 1;
        }

        glm::vec3 newPos = oldPos + glm::vec3(x, y, z);
        this->entity->setPosition(newPos);
        entity->grounded = true; // If some update happens, grounded needs to be changed
    } else {
        oldPos = entity->getPosition();
        this->entity->setPosition(distanceToGravityCenter);
    }
}

基本思路是确定实体从哪个方向撞击表面,然后将其 "unit" 放回那个方向。但显然我做错了什么,因为它总是将实体移回它来自的地方,有效地将它保持在生成点。

此外,这可能会容易得多,我想多了。

考虑您的坐标之一的 if 语句:

if (dir.x >= 0) {
    x = -1;
}

if (dir.x < 0) {
    x = 1;
}

假设dir.x < 0。然后你将跳过第一个if,进入第二个,x将被设置为1。 如果dir.x >= 0,你将输入第一个ifx将被设置为-1。现在 x < 0 为真,所以您也将输入第二个 if,并且 x 再次设置为 1。

可能您想要的是将 x 设置为 1 或 -1,具体取决于 dir.x。您应该只在未输入第一个时执行第二个 if,因此您需要一个 else if:

if (dir.x >= 0) {
    x = -1;
} else if (dir.x < 0) {
    x = 1;
}

可以将其压缩为

x = (dir.x >= 0) ? -1 : 1;

正如@CompuChip 已经指出的那样,您的 ifs 可以进一步简化。

但更重要的是一个逻辑问题可以解释 "choppiness" 你描述的(遗憾的是你没有提供任何镜头,所以这是我最好的猜测)

根据您发布的代码:

首先检查实体是否接地。如果是,则继续检查是否存在碰撞,最后,如果没有,则设置位置。

你必须稍微颠倒一下。

  1. 保存旧位置
  2. 检查是否接地
  3. 已将位置设置为新位置!
  4. 进行碰撞检测
  5. 如果您注册了碰撞,请重置为旧位置!

所以基本上:

glm::vec3 distanceToGravityCenter =
        this->entity->getPosition() -
        ((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time

oldPos = entity->getPosition(); // 1.

if (!entity->grounded) { // 2.
    this->fallingStar->setPosition(distanceToGravityPoint); // 3

    glm::vec3 entityPosition = entity->getPosition();

    if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) { // 4, 5
        this->entity->setPosition(oldPos);
        entity->grounded = true; // If some update happens, grounded needs to be changed
    }
}

这应该可以帮助您入门:)

我想详细说明一下:

如果您先检查碰撞,然后设置位置,您会在碰撞时首先 collision/hit 创建一个 "infinite loop",然后如果有碰撞(确实存在),您将设置回旧位置。基本上只是数学上的不准确会让你移动,因为每次检查你都会回到原来的位置。