体素世界中的碰撞检测
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
,你将输入第一个if
,x
将被设置为-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" 你描述的(遗憾的是你没有提供任何镜头,所以这是我最好的猜测)
根据您发布的代码:
首先检查实体是否接地。如果是,则继续检查是否存在碰撞,最后,如果没有,则设置位置。
你必须稍微颠倒一下。
- 保存旧位置
- 检查是否接地
- 已将位置设置为新位置!
- 进行碰撞检测
- 如果您注册了碰撞,请重置为旧位置!
所以基本上:
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",然后如果有碰撞(确实存在),您将设置回旧位置。基本上只是数学上的不准确会让你移动,因为每次检查你都会回到原来的位置。
我现在有点受困于我的基本体素物理学。它非常非常起伏不定,我很确定我的数学某处被打破了,但让我们看看你要说什么:
// 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
,你将输入第一个if
,x
将被设置为-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" 你描述的(遗憾的是你没有提供任何镜头,所以这是我最好的猜测)
根据您发布的代码:
首先检查实体是否接地。如果是,则继续检查是否存在碰撞,最后,如果没有,则设置位置。
你必须稍微颠倒一下。
- 保存旧位置
- 检查是否接地
- 已将位置设置为新位置!
- 进行碰撞检测
- 如果您注册了碰撞,请重置为旧位置!
所以基本上:
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",然后如果有碰撞(确实存在),您将设置回旧位置。基本上只是数学上的不准确会让你移动,因为每次检查你都会回到原来的位置。