Deff_Dev

[Unity/C#] 2D 부채꼴 범위 탐색 본문

Unity(유니티)/유니티 공부

[Unity/C#] 2D 부채꼴 범위 탐색

Deff_a 2024. 5. 14. 22:22

이 글에서는 게임 수학에 관한 자세한 내용은 다루지 않고 사용법과 왜 쓰는지에 대해 설명하겠다.

게임 수학에 관한 자세한 설명은 다른 블로그를 찾아보면 자세하게 설명되어있으니 다른 블로그 참고하면 된다.

만들어 볼 기능

 

위처럼 로켓(삼각형)이 바라보고 있는 방향의 부채꼴 범위 안에 들어온 오브젝트를 탐지하는 기능을 만들어보겠다.

 

이 글에서 설명하는 프로젝트는 2D이기 때문에 Vector2를 기준으로 설명하겠다.


내적

 

내적은 Vector2.Dot(vector , vector)로 사용 가능하다.

 

그럼 내적을 왜 쓸까 ?

 

내적은 플레이어가 바라보는 방향과 물체의 사이 각도의 코사인 값를 구하기 위해 사용한다. 

Vector2.dot(오브젝트가 바라보는 방향, 타겟의 방향);

이런 식으로 내적을 사용할 수 있고 내적에 들어가는 매개변수는 정규화된 벡터를 사용한다.

 

정규화된 벡터를 사용하는 이유는 정규화된 벡터의 내적을 구한다면 각도의 코사인 값이 나오기 때문이다.

 

Cos 값은 -1부터 1까지 나오고 플레이어가 위를 바라보고 있다면 1에 가까워진다.

 

그렇다면 내적한 값이 1하고 가까워진다면 플레이어가 바라보는 방향과 가까워진다는 것을 알 수 있다.

 

내적 값을 어떻게 이용할까 ?

 

내적으로 구한 값을 로켓의 시야각 / 2 코사인 값과 비교한다면 부채꼴 안에 들어왔는지를 확인할 수 있다.

시야각 / 2를 하는 이유는 코사인은 Y축 대칭이기 때문에 시야가 90도라고 한다면 반 절인 45도의 코사인 값을 구해서 내적 값과 비교하는 것이다.

 

그럼 코드로 어떻게 적용할까 ?

using System;
using UnityEditor;
using UnityEngine;

public class AlertSystem : MonoBehaviour
{
    // fov가 45라면 45도 각도안에 있는 aesteriod를 인식할 수 있음.
    [SerializeField] private float fov = 45f;
    // radius가 10이라면 반지름 10 범위에서 aesteriod들을 인식할 수 있음.
    [SerializeField] private float radius = 10f;
    private float alertThreshold;

    private Animator animator;
    private static readonly int blinking = Animator.StringToHash("isBlinking");

    public Transform aestreiod;

    private Color color = new Color(0, 1, 0, 0.3f);
    private void Start()
    {
        animator = GetComponent<Animator>();
        // FOV를 라디안으로 변환하고 코사인 값을 계산
        alertThreshold = Mathf.Cos(fov / 2 * Mathf.Deg2Rad);
    }

    private void Update()
    {
        CheckAlert();
    }

    private void CheckAlert()
    {
        // 물체의 방향을 구한다.
        Vector2 targetDir = aestreiod.position - transform.position;

        if (targetDir.magnitude <= radius) // 오브젝트가 플레이어의 탐지 범위에 들어올 때 
        {
            // 내적
            float dot = Vector2.Dot(transform.up, targetDir.normalized);
            
            // 내적한 값이 각도 / 2 코사인 값보다 크다면 => 플레이어가 바라보는 방향에 더 가깝다면
            if (dot >= alertThreshold) 
            {
                animator.SetBool(blinking, true); // 탐지
            }
            else
            {
                animator.SetBool(blinking, false);
            }
        }
        else
        {
            animator.SetBool(blinking, false);
        }
    }

    private void OnDrawGizmos() // 씬 창에서 부채꼴 범위 그리기
    {
        Handles.color = color;

        // 시야의 시작 방향 벡터 계산
        Vector2 startDirection = Quaternion.Euler(0, 0, fov / 2) * transform.up;

        // DrawSolidArc 함수를 이용하여 시야 범위를 나타내는 부채꼴 그리기
        Handles.DrawSolidArc(transform.position, Vector3.back, startDirection, fov, radius);
    }

}