[Unity, C#] 몹을 랜덤한 경로로 순찰(patrol)시키기
토이 프로젝트로 좀비 TPS 게임을 만들고 있다.
순찰(patrol)이란 몹이 일정한 지점들을 돌아다니는 것을 이야기한다. 좀비 게임을 생각해보면, 좀비가 가만히 있기만(idle) 하면 난이도가 너무 쉬울 것이다. 좀비가 idle/patrol 상태에 있다가 플레이어를 발견하면 추적(chase) 모드로 전환되어 플레이어를 쫓아간다. 이때 빠르게 도망가지 못하고 좀비와의 거리가 일정 값 이하가 되면 좀비가 플레이어를 공격(attack)한다. 플레이어가 도망가서 인식 범위를 나갔다면 좀비는 처음에 있던 장소로 돌아간다(return). 처음에 있던 장소로 돌아간 이후에는 다시 idle/patrol 상태가 된다.
위 과정을 도식으로 간단히 나타내면 다음과 같다.
💡 return이 필요한 이유는 무엇일까?
return이 없는 경우에 플레이어는 맵을 돌아다니다 한번이라도 플레이어를 인식한 몹은 계속 chase 상태가 되므로, 많은 몹을 끌고 오게 된다. 너무 많은 몹이 한 장소에 쏠리게 되어 시스템에 과부하가 올 수 있고 난이도 조절도 어렵게 된다.
순찰(patrol)하는 지점들을 waypoint라고 한다. waypoint를 직접 지정하는 것 외에, 몹의 초기 위치에서 적당히 떨어진 곳으로 랜덤하게 설정하는 방법을 알아보자.
- patrol할 지점을 적당히 List로 만들어준다.
local 좌표계로 생각해서 넣으면 된다.
List<Monster> Monster_List; // 맵에 있는 모든 몹을 가지고 있는 List
List<Vector3> Monster_Patrol_Positions;
Monster_Patrol_Positions = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(2, 0, 2),
new Vector3(-2, 0, -2),
new Vector3(2, 0, -2),
new Vector3(-2, 0, 2),
new Vector3(3, 0, 0),
new Vector3(-3, 0, 0)
};
- patrol waypoint를 지정한다.
public void InitMonsterPatrol()
{
Monster_Patrol_Positions = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(2, 0, 2),
new Vector3(-2, 0, -2),
new Vector3(2, 0, -2),
new Vector3(-2, 0, 2),
new Vector3(3, 0, 0),
new Vector3(-3, 0, 0)
};
int numberOfPosToSelect = 3; // 순찰할 지점의 개수를 정한다.
foreach (Monster monster in Monster_List)
{
// 몹이 처음에 idle 상태일 경우 patrol waypoint를 지정하지 않았다.
if (monster.m_State == Character.State.Idle)
continue;
List<Vector3> positionsCopy = new List<Vector3>(Monster_Patrol_Positions);
List<Vector3> selectedPositions = new List<Vector3>();
// 위에서 지정한 개수만큼 patrol position을 랜덤하게 뽑는다.
while (selectedPositions.Count < numberOfPosToSelect && positionsCopy.Count > 0)
{
int randomIndex = Random.Range(0, positionsCopy.Count);
selectedPositions.Add(positionsCopy[randomIndex]);
positionsCopy.RemoveAt(randomIndex);
}
// 모두 뽑으면 몹의 property인 patrol waypoint를 지정해준다.
monster.SetPatrolWaypoints(selectedPositions);
monster.StartPatrol();
}
}
Monster 클래스의 메소드인 SetPatrolWaypoints, StartPatrol의 코드는 다음과 같다.
NavMeshAgent agent;
int waypointIndex;
Vector3 waypointTarget;
Vector3 initPosition;
List<Vector3> patrolWaypoints;
public void SetPatrolWaypoints(List<Vector3> posDiffs)
{
foreach (Vector3 posDiff in posDiffs)
{
patrolWaypoints.Add(transform.position + posDiff);
}
}
public void StartPatrol()
{
waypointIndex = 0;
UpdateDestination();
}
void UpdateDestination()
{
if (patrolWaypoints.Count <= waypointIndex)
return;
waypointTarget = patrolWaypoints[waypointIndex];
agent.SetDestination(waypointTarget);
}
경로를 정확히 지정해주고 싶은 경우 (앞뒤, 좌우, 삼각형, 사각형 등) 이 방법을 응용해 2차원 List를 사용하면 될 것 같다.