숏컷 코드
direction = target - origin
angle = atan2(direction.y, direction.x)문법과 예시
atan(y/x) 대신 atan2(y, x)를 써야 하는 이유
단순한 atan(y / x) 계산은 사분면을 구분하지 못합니다. 예를 들어 (1, 1)과 (-1, -1) 방향은 기울기가 같기 때문에 atan 결과도 같습니다. atan2는 y와 x를 따로 받아 부호를 보존하므로 4사분면 전체에서 -π ~ π 범위의 고유한 각도를 돌려줍니다. 2D 조준, 미니맵 방향 아이콘, AI 감지 각도처럼 방향이 화면 어느 쪽이든 올바르게 작동해야 하는 경우 이 차이가 결정적입니다.
(1, 0) -> atan2 = 0 (오른쪽)
(0, 1) -> atan2 = π/2 (위)
(-1, 0) -> atan2 = π (왼쪽)
(0, -1) -> atan2 = -π/2 (아래)degree/radian 혼동과 좌표계 기준축이 버그의 주된 원인이다
atan2가 반환하는 값은 라디안입니다. Unity나 엔진 API가 degree를 기대할 때 라디안 값을 그대로 넘기면 캐릭터가 전혀 다른 방향을 보게 됩니다. 또 엔진마다 0도 = 오른쪽(+X)인지 0도 = 위쪽(+Y)인지가 다르고, 화면 좌표는 Y 축이 아래를 향하기 때문에 월드 좌표와 y 부호가 반대로 뒤집힐 수 있습니다. 코드에서 각도를 쓸 때마다 "이 값의 단위와 기준축이 지금 사용 중인 API와 일치하는가"를 확인하는 습관이 필요합니다.
// 마우스 조준 예시
direction = mouseWorldPos - playerPos
rawAngle = atan2(direction.y, direction.x) // 라디안, +X 기준
degrees = rawAngle * (180 / PI) // degree 변환
offset = 90 // 스프라이트가 +Y 방향을 앞으로 그렸을 때
spriteRotation = degrees - offsetatan2는 각도를 구하는 도구이지 회전을 부드럽게 만드는 도구가 아니다
atan2로 목표 각도를 얻은 다음, 현재 각도와의 차이를 그대로 회전량으로 쓰면 359도에서 1도로 넘어가는 순간 -358도 회전 같은 급격한 결과가 나옵니다. 이 문제를 "angle wrapping" 또는 "shortest-path rotation"이라 부르며, 두 각도 차이를 -180 ~ 180 범위로 정규화하는 처리가 별도로 필요합니다. 부드러운 회전이 필요하다면 atan2 + angle wrap + lerp 또는 쿼터니언 slerp를 조합해야 합니다.
diff = targetAngle - currentAngle
// wrap-around 처리
diff = ((diff + 180) % 360) - 180
currentAngle += diff * turnSpeed * deltaTime각도가 꼭 필요한가, 방향 벡터만으로 충분한가를 먼저 판단한다
각도 계산은 atan2 → 비교 → cos/sin으로 다시 벡터 변환하는 왕복 경로를 만들기 쉽습니다. 이동 방향 판단이나 AI 감지처럼 각도 숫자 자체보다 방향 관계가 중요한 경우에는 정규화된 방향 벡터 + dot product로 훨씬 간단하게 해결됩니다. atan2는 HUD 방향 표시, 스프라이트 회전량, yaw 값 저장처럼 실제로 각도 수치가 필요할 때 쓰는 것이 성능과 코드 명확성 양쪽에 유리합니다.
방향 각도를 계산할 때 핵심
| 상황 | 적합한 선택 |
|---|---|
| 2D 스프라이트를 마우스 방향으로 회전시키기 | atan2 + degree 변환 + 스프라이트 축 오프셋 |
| 적이 플레이어 정면을 보는지 판정 | dot product로 충분, atan2 불필요 |
| 미니맵 방향 화살표 각도 | atan2 후 UI 기준축 보정 |
| 부드러운 캐릭터 회전 | atan2 + angle wrap + lerp 또는 쿼터니언 |
| 넓은 각도 전환 시 급격한 회전 방지 | shortest-path wrap 처리 필수 |
| 각도 숫자 없이 좌우/정면 판정만 필요할 때 | dot 또는 cross 부호만으로 해결 |
특이 케이스와 주의할 점
흔한 실패는 각도 숫자가 필요하지도 않은데 atan2 -> degree -> cos/sin 왕복을 돌며 코드를 복잡하게 만드는 것입니다.
정면 판정이나 좌우 판정이면 벡터 연산만으로 충분한 경우가 많습니다. 정말 각도가 필요한 경우에만 atan2를 쓰고,
radian/degree, 기준축, wrap-around를 같이 점검하세요.
참고 링크
2 sources