덴바의 노트

내일배움캠프 21일차 TIL : 기존 시야각 구하기 문제점 해결 본문

프로그래밍 노트/TIL

내일배움캠프 21일차 TIL : 기존 시야각 구하기 문제점 해결

덴바 2024. 5. 17. 23:05

 

오늘의 키워드

 

  • 기존 시야각 구현 코드의 문제점

 


문제 파악


 

<< 변경 전 >>

   private void CheckAlert()
   {
       colliders = Physics2D.OverlapCircleAll(transform.position, radius, targetMask);

       if (colliders.Length > 0)
       {
           foreach (var enemy in colliders)
           {
               Vector3 enemyDir = (enemy.transform.position - transform.position).normalized;
               float dot = Vector2.Dot(transform.up, enemyDir);
               float theta = Mathf.Acos(dot) * Mathf.Rad2Deg;
               if (theta <= fov * 0.5f)
               {
                   animator.SetBool(blinking, true);
               }
               else
               {
                   animator.SetBool(blinking, false);
               }
           }
       }
       else
       {
           animator.SetBool(blinking, false);
       }
   }

 

 

(문제되는 코드)

 

float theta = Mathf.Acos(dot) * Mathf.Rad2Deg;

 

 

기존 방식은 두 벡터의 내적을 구한 후

 

내적을 구한 부분에서 Degree를 추출하는 방식으로 코드가 이어졌습니다.

 

코드 작동 상 문제는 없습니다!

 

하지만 약간의 비효율적인 부분이 생기게 됩니다.

 

그 이유는 Acos에 있습니다.

 

Sin, Cos, Tan, Asin, Acos, Atan 등의 연산은 생각 외로 무겁다는 것 입니다.

 

왜 비효율적이었는지에 대해서 오늘 강의에 들은 내용을 토대로 설명하고자 합니다.

 

<< 변경 후 코드>>

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

 

    private void CheckAlert()
    {
        colliders = Physics2D.OverlapCircleAll(transform.position, radius, targetMask);

        if (colliders.Length > 0)
        {
            foreach (var enemy in colliders)
            {
            	bool needAlert = false;
                Vector3 enemyDir = (enemy.transform.position - transform.position).normalized;
                float dot = Vector3.Dot(transform.up, enemyDir);
                if (alertThreshold <= dot)
                {
                    needAlert = true;
                    break;
                }
                
                animator.SetBool(blinking, needAlert);
            }
        }
        else
        {
                animator.SetBool(blinking, false);
        }
   
    }

 

Vector3 enemyDir = (enemy.transform.position - transform.position).normalized;

float dot = Vector3.Dot(transform.up, enemyDir);

 

이 부분은 본인 위치에서 적의 방향을 구해내는 코드입니다.

 

그리고 본인을 중점으로 위로의 방향과 적 방향을 내적으로 구합니다.

 

이를 그림으로 표현하면 아래와 같습니다.

 

 

enmiyDir이 아니라 enemyDir!

 

Transform.UP은 Vector3(0, 1, 0)을 뜻하고

 

enemyDir 또한 normalized를 했기 때문에 그 값은 1이 될 것입니다.

 

여기서 핵심은

 

Vector2.Dot()

 

즉, 내적을 구하는 공식은

 

A * B * Cos θ라는 것 입니다.

 

그리고 우리는 A와 B값이 각자 1인 것을 알고 있습니다.

 

이 뜻은 Vector2.Dot() => Cos θ 라는 것입니다.

 

그렇기 때문에 굳이 Acos을 이용하여 각을 구할 필요가 없던 것입니다.

 

또한 위 Start문에서

 

alertThreshold = Mathf.Cos((fov * 0.5f) * Mathf.Deg2Rad)

 

이를 한 번 계산하여 제한되는 임계점 radian 값이 몇인지를 알 수 있으며,

 

if (alertThreshold <= dot)

 

내적의 결과 값이 alertThreshold. 즉 임계점 보다 클 경우

 

대상은 시야각에 들어온 것으로 처리할 수 있습니다.

 


 

오늘의 회고

 

이렇게 해서 최종적으로 시야각에 관한 포스팅을 마무리했습니다.

 

처음에는 매우 복잡하고 어려워서 구현하지 못했지만

 

이번 강의로 사이다 마냥 완벽하게 이해됐습니다.

 

내적의 결과가 세타각의 radian 값을 출력할 줄은 꿈에도 몰랐네요...