플레이어를 추격하는 적 Ai를 만들어 보겠습니다.
그러기 전 먼저 Ai가 이동할 경로를 만들어야 합니다.
Surface를 추가해야만 Ai가 이동할 수 있습니다.
해당 Ai가 돌아다닐 바닥에 NavMesh SUrface 컴포넌트를 추가해줍니다.


추가하게 된다면 Ai가 돌아다닐 수 있는 구역이 파란색으로 칠해지게 됩니다.
Ai로 지정할 오브젝트에 Nav Mesh Agent를 추가해 줍니다.

이상태가 된다면 움직일 Ai와 돌아다닐 필드를 지정해 주었기에 간단한 코드만 구현해주면 추격해 오는 적 Ai를 만들 수 있습니다.
유니티에서 제공하는 기능을 사용하여 간단한 추격 Ai를 만들어 보겠습니다.
using UnityEngine.AI;
void update()
{
agent.SetDestination(player.position)
}
agent.SetDestination 으로 해당 오브젝트가 가고자 하는 지점을 찍어주는 기능입니다.
단순히 플레이어를 따라오는 Ai가 아닌 정해진 범위와 시각을 이용해 플레이어를 인식하고 추격해 오는 기능을 추가하겠습니다.
public Transform[] patrolPoint; //순찰 지점
void patrol() //순찰모드
{
if (patrolPoint.Length == 0) return;
if (agent.pathPending) return; //경로 계산중 이거나 목적지까지 도달하지 않았음 이동
//도착 거리 확인
if (agent.remainingDistance < 0.5f)
{
waitTimer += Time.deltaTime; //! 지점 도착후 타이머 시작
//지점 도착후 3초 이상일 경우 다른 지점으로 이동
if (waitTimer >= waitDuration)
{
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoint.Length;
agent.SetDestination(patrolPoint[currentPatrolIndex].position);
waitTimer = 0f;
}
else
{
agent.isStopped = true; //대기중엔 멈춰있기
animator.SetBool("isWalk", false);
}
}
else
{
agent.isStopped = false; //이동중엔 경로 계속 갱신
animator.SetBool("isWalk", true);
}
}
먼저 플레이어를 인식하기 전인 순찰 모드를 구현해 주었습니다. 순찰 모드는 말 그대로 플레이어를 추격하기 위해 해당 구역을 순찰하는 모드이며 순찰하는 과정에서 플레이어를 마주쳤을 경우 순찰 모드가 아닌 추격 모드로 바꿔 플레이어를 따라가게 구현할 것입니다.
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoint.Length;
현재 순찰 인덱스를 다음 인덱스로 바꿔줍니다. % patrolPoint.Length 한 이유는 배열의 길이가 넘지 않게 순환 하기 위함입니다. 마지막 인덱스 뒤에는 다시 0으로 돌아갑니다.
waitTimer를 이용해 순찰지점에 도착하면 3초 동안 서있다 다른 순찰지점으로 이동하게 됩니다.
agent.SetDestination(patrolPoint[currentpatrolIndex].position);
Ai Agent에게 다음 경로로 이동하게 만든 코드입니다.

해당 빈오브젝트 포인트를 잡아 요소들을 넣어주면 Element0 부터 3까지 계속 순환하며 순찰하게 되는 구조입니다.
public float aiAngle = 90.0f; //Ai 시야각 90.0f는 정면 180도
public float detectionRange = 20.0f; //시야를 통해 플레이어를 감지할 수 있는 최대 거리
bool PlayerInSight() //플레이어가 Ai의 시야각 및 시야 감지 거리 내에 있는지 확인
{
Vector3 dirToPlayer = player.position - transform.position; //Ai에서 플레이어로 향하는 방향 벡터 계산
float angle = Vector3.Angle(dirToPlayer, transform.forward); //Ai의 정면과 플레이어 방향 사이의 각도 계산
//계산된 각도가 설정한 시야각 보다 작을때
if (angle < aiAngle * 0.5f) //aiAngle / 2 보다 작을때
{
RaycastHit hit;
if (Physics.Raycast(transform.position, dirToPlayer.normalized, out hit, detectionRange))
{
if (hit.transform == player)
{
return true;
}
}
}
return false;
}
해당 코드는 Ai에게 시각 기능을 줌으로써 추격하는 범위가 아닌 Ai 오브젝트 기준 180도 전방으로 레이케스트를 이용해 플레이어가 해당 범위에 있는지 판단하는 코드입니다.
단순한 코드일 수 있지만 시각 기능이 있고 없고는 게임플레이에 재미가 있지 않을까 싶은 생각에 추가해 봤습니다.
void Update()
{
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
if (!isChasing && (PlayerInSight() || distanceToPlayer <= chaseDis))
//플레이어가 시야 안에 있거나 일정거리 내에 들어왔을 때 추격 시작
{
agent.isStopped = false; //이동 재게
isChasing = true;
//Debug.Log("Chasing now!");
}
if (isChasing) //추적 중이라면 시야 및 거리 기반으로 유지/해제
{
agent.isStopped = false; //추격중에는 항상 이동 가능하게
agent.SetDestination(player.position);
animator.SetBool("isChase", true);
animator.SetBool("isWalk", false);
if (PlayerInSight())
{
loseTimer = 0f; //시야 안이면 타이머 초기화
}
else
{
loseTimer += Time.deltaTime; //시야를 잃은 상태면 타이머 증가
}
//시야에 플레이어가 없고, 추적 시간과 추적 범위를 벗어났을때 추적기능 false
if (!PlayerInSight() && (loseTimer >= ChaseTime) && distanceToPlayer >= stopChaseDis)
{
isChasing = false;
animator.SetBool("isChase", false);
animator.SetBool("isWalk", true);
}
}
else
{
patrol();
//Debug.Log("Patrol Start!");
}
}
가장 중요한 Ai의 추적 기능입니다.
1. Ai는 플레이어가 시야안에 들어오거나, 추적범위 내에 있으면 추적을 시작합니다.
2. 추적중 이라면 플레이어의 위치로 이동합니다.
3. Ai의 시각 거리가 추적범위보다 멀기 때문에 플레이어는 장애물을 이용해 Ai의 시각으로 부터 도망가는게 우선시 됩니다.
4. 플레이어가 Ai의 시각과 추적 범위로 부터 멀어졌다면 Ai는 플레이어를 놓친 지점에서 3초 동안 그 자리에 서있다 순찰 모드로 들어가게됩니다.
가장 큰 문제가 추격하는 부분이였습니다.
지금은 수정이 되었지만 Ai의 추적거리가 시각거리보다 짧아서 발생한 문제입니다.
시각으로 플레이어를 인식해서 추격을 해야 되는 상황인데 플레이어가 추격에 실패하는 범위 밖에 있기 때문에 적 Ai의 애니메이션이 걸었다 뛰었다를 반복했습니다.
그당시 코드의 조건문이 불린 형식으로 껐다 켰다 하는 방식이였는데 시각으로 인식하면 Chase = true, 추적 범위에 벗어나면 chase = false, 인 상황에서 시각으론 인식하는데 추적범위에 멀어지게 되면 chase의 변수가 true가 됐다 false가 됐다 반복되는 상황에서 애니메이션이 꺼졌다켜졌다 하는 문제였습니다.
추적에 실패하는 조건을 좀 더 강화해서 플레이어가 시야에 없고, 추격시간도 초과되고, 추적 범위에 놓쳤을 때만 추적에 실패하게끔 수정하였습니다.
구현해 놓은 기능을 한번에 블로그에 작성하는데 많은 번거로운 느낌이 들어 다음부터는 작은거라도 구현했다면 바로바로 블로그에 작성할 생각입니다.
깃허브: https://github.com/Junamgyu/PAT_Project/commit/1d9b09043b57b7094c67407af3d682472c2870c0