[Unity] 한 점이 부채꼴 안에 있는지 계산하기

리유리·2022년 11월 7일

상황

플레이어가 현재 있는 위치에 해당하는 스크린이 켜지는 기능을 구현하고 싶다.

  1. 원모양 바닥을 3등분하여 3개의 부채꼴로 나눈 후
  2. 플레이어가 어느 부채꼴 안에 있는지 파악
  3. 해당 부채꼴에 있는 스크린을 켜줌

설명

한 점이 부채꼴 위에 있는지 어떻게 파악해야 할까?

부채꼴의 시작과 끝 선을 a, b라고 할 때 점 A(x,y) 가 주어진다고 가정하자.

x축에서 a 와 b, A 사이의 거리를 각각 θa, θb, θA 라고 할 때

θa < θA < θb

위 조건을 만족하면 점 A 가 부채꼴 안에 있다고 할 수 있다.

한 점과 x축 사이의 각도 구하기

점 A 를 지나는 반지름을 그은 후 직각삼각형을 만들어보면 삼각함수를 사용할 수 있다.

따라서 θA = arctan(y/x) 가 된다.
코드로 작성해보면 아래와 같다 (여기선 x, z 좌표를 가지고 각도를 구함)
각도를 항상 양수로 만들어주는 코드를 추가하였다.

private float CalcAngleWithX(Vector3 pos)
{
    float thetaX = Mathf.Atan2(pos.z, pos.x) * 180 / Mathf.PI;
    if (thetaX < 0)
    {
        return thetaX + 360;
    }

    return thetaX;
}

마무리

스크린 A, B, C 의 교점을 이용해 세 교점이 x축과 이루는 각도를 알아낸 후 플레이어 위치와 비교하면 플레이어가 어느 스크린을 보고 있는지 알 수 있다!

(예시코드라 성능은 생각하지 않고 일단 작동하는지에 중점을 두었다.)

public class PlayerManaer : MonoBehaviour
{
    [SerializeField] Camera mainCamera;
    [Space()]
    [SerializeField] Transform center; // 중심
    [SerializeField] Transform meet1; // 교점 1
    [SerializeField] Transform meet2; // 교점 2
    [SerializeField] Transform meet3; // 교점 3
    [Space()]
    [SerializeField] GameObject canvas1;
    [SerializeField] GameObject canvas2;
    [SerializeField] GameObject canvas3;

    private void Update()
    {
        SetScreenArea();
    }

    private void SetScreenArea()
    {
        float thetaMeet1 = CalcAngleWithX(meet1.position);
        float thetaMeet2 = CalcAngleWithX(meet2.position);
        float thetaMeet3 = CalcAngleWithX(meet3.position);

        float thetaPlayer = CalcAngleWithX(mainCamera.transform.position);

        if (thetaMeet2 < thetaPlayer && thetaPlayer < thetaMeet1)
            SetMainScreen(canvas1);
            
        if (thetaMeet3 < thetaPlayer && thetaPlayer < thetaMeet2)
            SetMainScreen(canvas3);
            
        if (thetaPlayer > thetaMeet1 || thetaPlayer < thetaMeet3)
            SetMainScreen(canvas2);
    }

    private float CalcAngleWithX(Vector3 pos)
    {
        float thetaX = Mathf.Atan2(pos.z, pos.x) * 180 / Mathf.PI;
        if (thetaX < 0)
        {
            return thetaX + 360;
        }

        return thetaX;
    }

    private void SetMainScreen(GameObject canvas)
    {
        canvas1.SetActive(false);
        canvas2.SetActive(false);
        canvas3.SetActive(false);

        canvas.SetActive(true);
    }
}
profile
내 이름은 이유리, 거꾸로 해도 이유리

0개의 댓글