2017-01-16 3 views
1

나는 자유 시간에 게임 엔진을 만들었지 만, 충돌을 일으키려고 몇 주 동안 붙어 있었어요.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 

그리고 : 같은 참고로

, 내 현재의 알고리즘은 보인다 운동 "이라고 말하지만, 아직 모든 얼굴의 거리 값을 찾는 효율적인 방법을 찾지 못했습니다.

누구든지 내가 성취하고자하는 목적에 더 잘 작동하는 알고리즘을 알고 있습니까?

+0

현재 코드가 사용자의 질문과 어떤 관련이 있습니까? – xaxxon

+0

주로 참조/문맥을 위해. 하지만 내 현재의 알고리즘은 꽤 가깝다고 생각합니다. 가장자리의 경우를 수정하기 위해 약간의 수정 만 취할 수 있습니다. – 0x5453

답변

0

코드의 첫 번째 부분에 대해서는 상당히 의심 스럽습니다. 각 반복에서 엔티티의 위치를 ​​수정합니다. 맞습니까? 이상한 엣지 경우를 설명 할 수 있습니다.

2 차원 예를 들어, 사각형이 날카로운 모서리를 향해 걷고 두 벽과 충돌하면 첫 번째 벽에서 해당 위치가 수정되어 두 번째 벽에 더 많이 침투합니다. 그리고 나서 두 번째 벽은 더 큰 스케일 값을 사용하여 위치가 바뀌므로 사각형이 한 벽에만 밀린 것처럼 보입니다.

표면 S의 법선이 플레이어의 움직임에 가까울 때 충돌이 발생하면 다른 모든 충돌보다 늦게 처리됩니다. 다른 충돌을 처리 할 때 플레이어의 위치가 수정되어 표면 S에 더 많이 침투 할 가능성이 있습니다. 따라서 마침내 프로그램은 플레이어를 많이 터뜨리는 표면 S와의 충돌을 처리합니다.

간단한 수정이 있다고 생각합니다. 한 번에 관통을 계산하고 시간 변수를 사용하여 모든 변위를 합한 다음 전체 변위로 위치를 변경하십시오.