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

오늘의 키워드
- 기존 시야각 구현 코드의 문제점
문제 파악
<< 변경 전 >>
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);
이 부분은 본인 위치에서 적의 방향을 구해내는 코드입니다.
그리고 본인을 중점으로 위로의 방향과 적 방향을 내적으로 구합니다.
이를 그림으로 표현하면 아래와 같습니다.

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 값을 출력할 줄은 꿈에도 몰랐네요...