나는 자유 시간에 게임 엔진을 만들었지 만, 충돌을 일으키려고 몇 주 동안 붙어 있었어요.3D 충돌 해상도, AABB + 다면체
현재 엔티티의 콜라이더를 AABB로 표현하고 있으며, 레벨의 콜라이더는 상당히 단순한 (꼭 볼록한 것은 아님) 다면체로 표현됩니다. 모든 도면은 스프라이트 기반이지만 기본 충돌 코드는 3D입니다.
this 알고리즘을 사용하여 AABB/삼각형 충돌 감지가 작동합니다 (레벨 메쉬의 모든면에 순진하게 적용 할 수 있음). 그러나 충돌을 감지 한 후에 충돌을 해결하기 위해 노력하고 있습니다.
제가 생각해 낸 알고리즘은 꽤 잘 작동하지만, 깨지는 부분이 있습니다. 예를 들어 날카로운 구석으로 똑바로 들어가면 항상 플레이어가 한쪽 또는 다른쪽으로 밀려납니다. 또는 작은 충돌면이 다른 모든면보다 플레이어의 이동 방향에 더 가까운 정상을 가지면 다른면의 간격 띄우기를 사용했을 때 플레이어가 그 방향으로 먼저 "팝"합니다. 더 나은 결과.
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());
}
나는 플레이어의 방향으로 충돌 거리 "에 의해 충돌 얼굴을 정렬 덤비는되었습니다 구현의 여기
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
그리고 : 같은 참고로
, 내 현재의 알고리즘은 보인다 운동 "이라고 말하지만, 아직 모든 얼굴의 거리 값을 찾는 효율적인 방법을 찾지 못했습니다.
누구든지 내가 성취하고자하는 목적에 더 잘 작동하는 알고리즘을 알고 있습니까?
현재 코드가 사용자의 질문과 어떤 관련이 있습니까? – xaxxon
주로 참조/문맥을 위해. 하지만 내 현재의 알고리즘은 꽤 가깝다고 생각합니다. 가장자리의 경우를 수정하기 위해 약간의 수정 만 취할 수 있습니다. – 0x5453