[Unity NavMesh] 좁은 통로에서 좀비가 한 줄로 몰리는 문제 줄이기
여러 좀비가 똑같은 목표 위치로 이동하면 특정 통로에서 줄지어 접근하는 움직임이 나타날 수 있습니다. OUTPOST에서는 모든 좀비에게 목표의 정확한 위치만 반복 전달하기보다, 접근 과정에 약간의 경로 차이를 주도록 구현했습니다.
1. NavMeshAgent를 사용해도 경로가 비슷할 수 있습니다
NavMeshAgent는 이동 가능한 영역 위에서 경로를 계산해 주지만, 같은 위치에서 같은 목표를 향하는 에이전트들이 자연스럽게 서로 다른 전술을 선택해 주는 것은 아닙니다.
특히 좀비가 다수 등장하는 게임에서는 모두 발전기 또는 플레이어의 중심 좌표를 목표로 잡으면 접근 방향이 비슷해질 수 있습니다. 그래서 ZombieAI의 기본 이동에 중간 경유점(waypoint)을 추가했습니다.
2. 목표를 향한 방향에 옆 방향을 더하기
GetRouteDestination()에서는 목표까지의 방향을 구한 후, 그에 수직인 방향 벡터를 계산합니다. 여기에 개체마다 다른 이동 폭과 시간에 따른 흔들림을 적용해 경유점을 만듭니다.
Vector3 towardTarget = _activeTarget.position - transform.position;
towardTarget.y = 0f;
towardTarget.Normalize();
Vector3 lateral = Vector3.Cross(Vector3.up, towardTarget);
float sway = Mathf.Sin(Time.time * 0.82f + _routePhase);
Vector3 candidate = transform.position +
towardTarget * forwardDistance +
lateral * (side * lateralDistance);
Vector3.Cross()는 두 벡터에 수직인 방향을 만드는 함수입니다. 여기서는 목표를 향하는 벡터 옆쪽으로 일정 거리를 벌려 주는 데 사용했습니다.
개체별 _routePhase, _routeMagnitude, _routeSide는 웨이브와 스폰 순서를 바탕으로 계산됩니다. 따라서 좀비마다 같은 위치를 지나가려는 경향을 줄일 수 있습니다.
3. 아무 위치나 경유점으로 쓰지 않기
옆으로 위치를 옮긴다고 해서 항상 이동 가능한 곳은 아닙니다. 벽이나 NavMesh 밖일 수도 있기 때문입니다.
_routeWaypoint = NavMesh.SamplePosition(
candidate, out NavMeshHit hit, 4.5f, NavMesh.AllAreas)
? hit.position
: GetDirectTargetDestination();
먼저 후보 위치 근처의 NavMesh를 찾고, 실패하면 직접 목표를 향하도록 했습니다. 경유점에 가까워지거나 일정 시간이 지나면 새 경유점을 계산합니다. 매 프레임 임의의 새 목표를 만들지 않도록 waypointRefreshInterval도 사용했습니다.
4. 목표 근처에서는 우회하지 않기
GetRouteDestination()에는 directApproachDistance 조건이 있습니다. 목표에 충분히 가까워졌다면 옆으로 돌아가는 대신 실제 공격 대상 쪽으로 바로 이동합니다. 발전기를 대상으로 삼을 때는 중심점보다 콜라이더에 가장 가까운 지점을 활용합니다.
또 NavMeshAgent.avoidancePriority를 개체마다 다르게 설정해 회피 동작에서 동일한 우선순위만 사용하지 않도록 했습니다.
정리
목표 방향 계산 → 옆 방향 경유점 생성 → NavMesh 검증 → 일정 간격 갱신 → 근거리 직접 접근 순서로 이동을 구성했습니다. 이 방식은 좀비 경로의 획일성을 줄이기 위한 것이며, 극도로 좁은 길목의 병목을 완전히 없앤다고 보장하지는 않습니다. 실제 충돌 회피나 장시간 막힘 여부는 별도 플레이 테스트가 필요합니다.