[Unity]/[Unity]

[Unity] AI의 시야각(FieldOfView) 구현하는 법

극꼼 2021. 8. 4. 14:47
반응형


안녕하세요 극꼼입니다.

오늘은 젤다 모작을 만들며 사용한 몬스터의 시야각을 구현하는 방법을 알아보겠습니다.

참고로 제가 만든 모작은 3D게임이지만 지형이 입체적이거나 하지는 않기 때문에, 평면적인(y축 값 변동이 없는) 시야각을 사용했습니다.

 

구현한 기능을 미리보기로 보면 다음과 같습니다.

 

 

이와 같은 기능을 위해 3가지 단계를 거쳤습니다.

1. 필요한 변수 적절하게 선언하기
2. 시야각의 범위를 OnDrawGizmos를 통해 표시하기
3. 시야각의 범위  내에 들어온 적 또는 장애물 인지하기

1. 필요한 변수 선언하기

[SerializeField] bool DebugMode = false;
[Range(0f, 360f)] [SerializeField] float ViewAngle = 0f;
[SerializeField] float ViewRadius = 1f;
[SerializeField] LayerMask TargetMask;
[SerializeField] LayerMask ObstacleMask;

먼저 다음과 같이 변수들을 선언해줍니다. 그러면 이와 같이 인스펙터 창에서 각도 등을 조절할 수 있게 됩니다. 


2. 시야각 범위 OnDrawGizmos를 통해 표시하기

 

먼저 ViewAngle에 따른 시야각을 보여주는 선을 OnDrawGizmos를 통해 그어줄건데요, ViewRadius 범위를 그려주는 원을 그어줍니다.

if (!DebugMode) return;
Vector3 myPos = transform.position + Vector3.up * 0.5f;
Gizmos.DrawWireSphere(myPos, ViewRadius);

 

 

그 다음 시야각을 그어주는 선을 그어주기 이전에, 각도를 벡터값으로 바꿔주는 함수를 만들어줍니다.

Vector3 AngleToDir(float angle)
{
    float radian = angle * Mathf.Deg2Rad;
    return new Vector3(Mathf.Sin(radian), 0f, Mathf.Cos(radian));
}

 

 

이 함수를 이용해 시야각을 그어줍니다.

float lookingAngle = transform.eulerAngles.y;  //캐릭터가 바라보는 방향의 각도
Vector3 rightDir = AngleToDir(transform.eulerAngles.y + ViewAngle * 0.5f);
Vector3 leftDir = AngleToDir(transform.eulerAngles.y - ViewAngle * 0.5f);
Vector3 lookDir = AngleToDir(lookingAngle);

Debug.DrawRay(myPos, rightDir * ViewRadius, Color.blue);
Debug.DrawRay(myPos, leftDir * ViewRadius, Color.blue);
Debug.DrawRay(myPos, lookDir * ViewRadius, Color.cyan);

이제 인스펙터 창에서 이와 같이 조정할 수 있습니다.

 


3. 범위 내에 들어온 적 또는 장애물 인지하기

먼저 범위 내로 들어오는 오브젝트들의 Collider를 담을 list 변수를 선언합니다. 

List<Collider> hitTargetList = new List<Collider>();

 

계산 순서는 다음과 같습니다.

1. 범위 내로 들어온 특정 layer의 오브젝트 인식

2. 해당 오브젝트의 위치와 시야각 각도 비교

3. 해당 오브젝트와 나 사이에 장애물이 없는지 판단

 hitTargetList.Clear();
 Collider[] Targets = Physics.OverlapSphere(myPos, ViewRadius, TargetMask);

 if (Targets.Length == 0) return;
 foreach(Collider EnemyColli in Targets)
 {
     Vector3 targetPos = EnemyColli.transform.position;
     Vector3 targetDir = (targetPos - myPos).normalized;
     float targetAngle = Mathf.Acos(Vector3.Dot(lookDir, targetDir)) * Mathf.Rad2Deg;
     if(targetAngle <= ViewAngle * 0.5f && !Physics.Raycast(myPos, targetDir, ViewRadius, ObstacleMask))
     {
         hitTargetList.Add(EnemyColli);
         if (DebugMode) Debug.DrawLine(myPos, targetPos, Color.red);
     }
 }

 

이제 움직이는 적을 위와 같이 인식합니다.

코드를 응용해서 적을 공격하도록 할 수도 있습니다!

반응형