경로에 따라 움직일때 특정 구간에서만 이벤트를 발생시키고 싶은 상황이 생겨서 점과 직선사이의 거리를 구해야하는 상황이 발생했다.
처음에는 Raycast가 생각이 났다.
근데 매번 물리충돌을 검사하는거라 간단히 지나가기만하면 되는건데 굳이 설정을 많은 기능말고 다른 기능을 사용하고 싶었다.
우리가 계산하고 싶은건 : 점 P가 선분AB에 얼마나 가까운가?이다.
이를 위해 점P에서 선분AB에 수직으로 선을 내렸을때 닿는 점을 P'라고 하면 P와 P'사이의 거리를 구하면 된다. 이에 벡터 투영을 이용하면 된다.
벡터 투영 공식
1) 점 P에서 벡터 방향으로 수직으로 내렸을 때, 그 투영 벡터의 길이만을 나타냄
2) 1에서 구한 투영 길이에 AB방향의 단위 벡터를 곱해 투영벡터를 완성
3) 내적공식을 통하여 cosθ를 AP,AB로 나타냄
4) 기준점 A에서 투영 벡터 AP를 더해 최종적으로 투영된 위치 P'의 좌표를 구함
01 방향 벡터 구하기
Vector3 ap = p - a; // a에서 p까지의 방향벡터(아직 크기 포함, 단위 방향 벡터X)
Vector3 ab = b - a; // a에서 b까지의 방향벡터(아직 크기 포함, 단위 방향 벡터X)
02 거리계산 - sqrMagnitude vs Vector3.Distance
Vector3.Distance
보통 거리를 계산할때 Vector3.Distance(a,b)로 계산한다.
float dist = Vector3.Distance(a, b);
실제 내부 구현을 보면 루트 연산은 무거운 작업이기 때문에 루트 연산이 필요없을 경우 sqrMagnitude을 사용하는것이 훨씬 좋다.
Mathf.Sqrt((a - b).sqrMagnitude);
sqrMagnitude
float dist = (a - b).sqrMagnitude;
// 내부 구현
= (a.x - b.x)^2 + (a.y - b.y)^2 + (a.z - b.z)^2
성능 비교
실제로 연산을 100만번 한다고 가정했을때
sqrMagnitude 1~2ms
Distance 10~20ms
매 프레임마다 플레이어랑 가까운지 비교할때는 sqrMagnitude을 사용하는게 좋다!
03 벡터 내적(Dot)
float dot = Vector3.Dot(ap, ab);
내적 공식
내적의 결과는 방향에 따라 결과가 달라진다.
양수: 점 P는 AB 선분을 기준으로 B 방향 쪽에 있음
0: P는 A에서 AB에 수직인 위치에 있음
음수: P는 AB 선분을 기준으로 A의 반대쪽에 있음
내적을 통해 " 점 P는 선분 AB의 어디쯤에 위치해 있는가 "를 파악할 수 있다.
04 투영 비율 계산과 함께 Clamp01
선분을 벗어나지 않도록 처리를 위해 Clamp01사용
float t = Mathf.Clamp01(dot / abLenSq);
dot/abLensq : AP가 AB방향으로 얼마나 투영이 진행됐는지 비율을 나타낸다.
t가 0이면 P는 A 지점
t가 1이면 P는 B 지점
t가 0.5면 AB 선분의 중간에 있음
그러므로 P'이 항상 선분 AB위에 위치하게 하고 싶기 때문에 Clamp01을 사용하는 것이다.
05 투영점 계산
Vector3 projection = a + t * ab; // a를 더하는 이유는 기준점 a를 시작으로 한 상대 벡터
06 최종거리 계산
Vector3.Distance(point, projection); // P'에서 P 거리 계산
코드 전문
float DistancePointToSegment(Vector3 p, Vector3 a, Vector3 b)
{
Vector3 ap = p - a;
Vector3 ab = b - a;
float abLenSq = ab.sqrMagnitude;
float dot = Vector3.Dot(ap, ab);
float t = Mathf.Clamp01(dot / abLenSq);
Vector3 projection = a + t * ab;
return Vector3.Distance(point, projection);
}
'유니티 공부 > Unity' 카테고리의 다른 글
C# - JSON 파일 직렬화/역직렬화시 주의 사항 정리(Tuple, 프로퍼티, 한글이 깨지는 경우) (0) | 2025.04.15 |
---|---|
Unity - Unity 개발자가 왜 C++을 알아야할까? (0) | 2025.04.08 |
Unity - Batching과 함께 알아보는 Frame, LifeCycle, DrawCall (0) | 2025.04.07 |
Unity - 쉐이더 분홍색(핑크색) 오류 해결 방법 (1) | 2025.03.13 |
Unity - 에셋 추천 Trails FX(잔상 효과, 검 효과, 발자국 등) (0) | 2025.03.12 |
댓글