float 오차로 충돌처리가 제대로 안 되서 골치 아픈 경험을 하고 있는 분이라면 해결하시는 데, 약간의 아이디어가 될 수도 있겠습니다.
알고리즘에 별 문제는 없는 데도 불구하고, 게임상에서 충돌하면서 이동하다 보면 오브젝트를 뚫고 가거나 묻히는 경우가 종종 생기는 데, 디버깅을 해보면 충돌한 지점이 미세하게 뚫고 들어간 위치로 판정이 되는 경우가 많습니다.
(평면에 정확히 닿는 부분까지 이동한 후에, 위치와 그 평면과 거리를 계산해보면 당연히 0이 나와야 할 텐데, 0에 가까운 음수나 양수가 나오는 경우죠. 크기는 클 경우 0.001 까지도 나오는 데, 이 값이 얼마 이하다라고 딱 말하기가 힘들기 때문에, 오차한계를 조절해서 막기엔 불안한 …)
특히나 슬라이딩 같이 충돌 후의 위치가 민감한 경우에는 애매한 경우가 종종 생깁니다.
이런 문제를 회피하기 위한 아이디어는 '닿지도 말자'입니다. ^^;
(기본적으로 출발 위치는 완벽하게 어느 영역과도 충돌되지 않는 영역이라고 가정하면, 이동 후의 위치가 완벽하게 충돌되지 않는 영역이라면 항상 충돌되지 않은 영역에 있다고 할 수 있겠죠.)
먼저 아래와 같은 일반적인 슬라이딩 루틴을 보겠습니다.
Vector MoveTest(const Vector &pos, const Vector &dv) { float t; Plane p; Vector dv_;
t = COLTEST(pos, dv, &p);
if (t < 1.0f) { dv_ = dv * (1.0f - t); return MoveTest(pos + (dv - dv_), dv_ - Normal(p) * DotProduct(dv_, Normal(p))); }
return pos + dv; }
|
문제가 되는 경우는 충돌 되는 경우입니다.
만약 진행방향으로 0.5(t) 지점에서 충돌된다고 했을 때, 0.5(t)만큼 이동하면 문제가 될 수도 있다는 얘기겠죠.
이 경우 완전히 닿는 지점이 아닌 거의(!) 닿을 위치로 t 값을 보정하면 되겠습니다.
이때 t를 일정하게 줄여주는 식으로도 가능은 하겠습니다만 비스듬히 입사할 경우엔 문제가 될 수 있으니 t값은 충돌할 평면에 일정거리(SAFE_GAP)가 되는 위치로 보정을 합니다.
그림을 보시면, t의 위치에서 충돌되었다고 했을 때 gap (평면과 출발위치의 거리) 을 구해서 t * (gap - SAFE_GAP) / gap 로 보정된 위치 t' 를 구합니다. 적어도 이 t'의 위치는 어떤 경우에도 평면에 묻히거나, 다른 영역을 침범할 일이 없을 것입니다. (구해진 t 값은 0에서 t사이에는 충돌하는 것이 전혀(!) 없다는 의미이기 때문에...)
단 슬라이딩을 계속할 경우에는 gap 이 SAFE_GAP보다 작은 경우가 생기는 데, 이때는 과감하게 제자리에 있도록(t=0) 합니다. (항상 출발 위치는 안전한 위치이기 때문에 평면에 너무 접근했다고 하더라도 문제되는 위치는 아니기 때문입니다.)
평면과의 거리가 SAFE_GAP 이 되도록 이동하게 되면 (이때는 뒤로 이동하게 되겠죠 ?) 매우 골치 아픈 일이 발생합니다.
항상 이동은 0에서 t 사이로 이동해야 문제가 생기지 않습니다.
아래처럼 수정할 수 있을 겁니다
#define SAFE_GAP 0.001f
Vector MoveTest(const Vector &pos, const Vector &dv) { float t, gap; Plane p; Vector dv_;
t = COLTEST(pos, dv, &p);
if (t < 1.0f) { gap = pos * p; t = gap <= SAFE_GAP ? 0.0f : t * (gap - SAFE_GAP) / gap;
dv_ = dv * (1.0f - t); return MoveTest(pos + (dv - dv_), dv_ - Normal(p) * DotProduct(dv_, Normal(p))); }
return pos + dv; }
|
그리고 t 가 1일 때 평면에 완벽하게 붙어서 오차가 발생할 수 있다고 의심되는 경우에는
Vector MoveTest(const Vector &pos, const Vector &dv) { float t, gap; Plane p; Vector dv_;
t = COLTEST(pos, dv * 1.1f, &p) * 1.1f;
if (t <= 1.0f) { gap = pos * p; t = gap <= SAFE_GAP ? 0.0f : t * (gap - SAFE_GAP) / gap;
dv_ = dv * (1.0f - t); return MoveTest(pos + (dv - dv_), dv_ - Normal(p) * DotProduct(dv_, Normal(p))); }
return pos + dv; }
|
처럼 약간 이동 거리를 늘려서 충돌체크해도 되겠습니다.
ps. 아래에 구 sweep 샘플 보시면 Ball::MoveTest 에 간단하게 적용되어 있으니 참고하세요. (슬라이딩이 아니고, 반사라 오차로 인한 문제가 많이 덜합니다만…)
ps. 특히나 BSP를 이용한 충돌 체크를 할 경우에는, 이런 케이스에 대한 처리 안 해주면 치명적인 문제가 많이 발생합니다. (BSP는 출발 위치가 solid가 되면 충돌체크가 많이 힘들기 때문에…)
댓글을 달아 주세요
우수한 일! 감사!
위치에 중대한 일은 그것을 좋아했다!
중대한 위치 축하!경이롭 위치!
우수한 일! 감사!
너는 우수한 위치가 있는다!
이 위치는 유익한뿐 아니라 재미있는다!
여보세요, 아주 좋은 위치!