멈추지 않고 끈질기게

[Unity][2D] 타겟을 향한 회전 본문

Unity

[Unity][2D] 타겟을 향한 회전

sam0308 2023. 2. 11. 11:27

※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.

※ 해당 포스팅은 Unity 2021.3.15f1 버전을 기준으로 작성되었습니다.

 

 

이번 포스팅에서는 유니티 2D 환경에서 타겟을 향해 회전시키는 방법에 대해 알아보겠습니다.

 

서문

 유니티에서 특정 대상을 바라보도록 회전시키려 한다면 Transform.LookAt() 함수를 사용하여 간단하게 해결할 수 있습니다. 다만 2D 게임일 경우 얘기가 조금 다릅니다. 해당 함수는 x, y, z축 모두 사용하여 회전하기 때문에, 2D 게임에서 사용 시 오브젝트가 사라지는 현상을 볼 수 있습니다. 따라서 2D 게임에서 오브젝트를 회전시키려면 직접 대상과의 각도를 계산하여 회전시켜야 합니다.

 

Arctan를 활용한 각도 계산

 플레이어가 적을 향해 투사체를 발사하는 게임을 생각해 봅시다. 투사체의 원본 스프라이트는 북쪽(y축 방향)을 향할 때 기준으로 설명하겠습니다.

 

그림 1. 타겟과의 각도 구하기

 플레이어를 원점으로 두고 타겟의 좌표를 (a, b)라고 했을 때, 상기 그림에서 tan θ = a / b 가 되고, 여기서 역삼각함수인 arctan를 이용하면 arctan (a / b) = θ 를 통해 해당 각도를 구할 수 있습니다. a와 b는 각각 타겟과 플레이어의 x좌표, y좌표 차이를 통해 알 수 있고, arctan 계산은 유니티의 Mathf.Atan() 함수를 통해 실행할 수 있습니다. 단, 해당 함수의 리턴값은 라디안 스케일이므로, 디그리 스케일은 여기에 Mathf.Rad2Deg 값을 곱해주면 됩니다. 각도를 구했으니 오브젝트를 z축을 기준으로 θ만큼 회전시키면 되고, 이는 Transform.Rotate() 함수를 통해 구현할 수 있습니다.

 

 

예외 처리

 다만 이렇게 계산할 경우, 타겟의 y좌표가 원점(플레이어)보다 아래쪽이라면 발생하는 문제가 있습니다. 

 

그림 2. 타겟이 x축보다 아래일 경우

 상기 그림에서 알 수 있듯이 타겟이 x축보다 아래에 있는 경우, 상기 방법으로 계산하면 원점을 기준으로 대칭시켰을 때의 좌표와 동일한 θ값이 나오게 됩니다. 따라서 타겟의 y좌표를 따로 체크한 뒤, 원점(플레이어)보다 아래라면 계산한 θ값에 180도를 더해주는 작업이 필요합니다. 

 

 또한 y좌표가 완벽하게 일치할 경우 젯수가 0이 되는 문제가 발생합니다. 유니티에서 좌표값은 소수점까지 다루기 때문에 실제로 발생할 확률은 매우 낮겠지만, DevideByZeroException을 막기 위한 예외처리 또한 필요합니다.

 

using UnityEngine;

//오브젝트 회전용 함수 선언 네임스페이스
//회전 로직이 필요한 클래스에서 참조하여 사용
namespace MyMath
{
    public class MyRotation
    {
        //방향벡터를 통해 회전시키는 경우
        public static Vector3 Rotate(Vector3 dir)
        {
            if (dir.y == 0) dir.y = 0.01f; // DevideByZero 방지

            float angle = Mathf.Atan(dir.x / dir.y) * Mathf.Rad2Deg * -1;
            //아래 방향일 경우
            if (dir.y < 0) angle += -180;
            Vector3 rotationVec = Vector3.forward * angle;
            return rotationVec;
        }

        //발사자와 타겟의 위치값을 통해 회전시키는 함수
        public static Vector3 Rotate(Vector3 pos, Vector3 target)
        {
            float diff_x = target.x - pos.x;
            float diff_y = target.y - pos.y;
            if (diff_y == 0) diff_y = 0.01f; // DevideByZero 방지

            float angle = Mathf.Atan(diff_x / diff_y) * Mathf.Rad2Deg * -1;
            //타겟의 y좌표가 플레이어보다 아래쪽일 경우
            if (diff_y < 0) angle += -180;
            Vector3 rotationVec = Vector3.forward * angle;
            return rotationVec;
        }
    }
}

 상기 코드는 제 포트폴리오 프로젝트 코드의 일부입니다. 방향 벡터를 통해 각도를 계산하는 함수와 플레이어와 타겟의 좌표를 통해 각도를 구하는 함수를 별도의 네임스페이스에 작성한 뒤, 필요한 클래스에서 참조하여 사용할 수 있도록 했습니다. 아래쪽 함수는 시작점과 타겟의 좌표를 매개변수로 전달하면 위에서 설명한 대로 각도 θ를 구한 뒤,  해당 값을 Vector3.forward, 즉 (0, 0, 1)에 곱하여 벡터 형태로 반환하는 함수입니다.

 기본 회전방향이 반시계로 되어있어서 계산한 각도에 -1을 곱했고, 타겟의 y좌표가 플레이어의 y좌표보다 낮을 경우에도 -180도를 더하였습니다. 또한 y좌표값의 차이가 0일 경우 0.01로 만들어 DevideByZeroException을 방지하였습니다. 투사체 발사 시 플레이어 좌표와 타겟의 좌표를 해당 함수에 전달한 뒤, 반환된 벡터값을 Transform.Rotate() 함수의 매개변수로 사용하면 투사체가 타겟을 바라보도록 회전시킬 수 있습니다. 

'Unity' 카테고리의 다른 글

[Unity][UI] Content Size Fitter  (0) 2023.04.13
[Unity][UI] 레이아웃 그룹(Layout Group)  (0) 2023.03.31
[Unity][번외] Transform.SetPositionAndRotation  (0) 2023.02.15
[Unity] 코루틴(CoRoutine)  (0) 2022.12.17
[Unity] Script Lifecycle  (0) 2022.12.16