몬스터가 플레이어를 추적하는 기능을 구현하는 중 Nav Agent가 회전이 너무 굼뜬 문제가 있었다.
몬스터가 이동 중에 회전해야하는 경우라면 그럭저럭 봐줄만한 수준이지만, 플레이어가 바짝 붙어 있는 경우에는 회전이 너무 굼떠서 문제가 된다.
그렇다고 뒤로 칼을 휘두를 수도 없다.
그러니 몬스터 입장에선 플레이어가 엉덩이에 바짝 붙어서 뒤만 잡으면 아무런 피해도 입지 않고 손쉽게 전투가 진행될 수 있다.
따라서 플레이어와 거리에 따라 회전 속도를 제어해야한다.
해결책 1. Nav Agent의 회전 속도를 동적으로 제어해본다.
기존에는 이동 속도만 제어하고 있었는데 여기에 Agent.angularSpeed
를 제어하는 코드를 추가하면 된다.
이상적인 수치는 아직 모르기 때문에 적당히 아무 숫자를 넣어서 처리했다.
float distance = (_detectPlayerTransform.position - _enemyController.transform.position).magnitude;
if (distance > 10f)
{
// 먼 거리: 뛰기
_enemyController.Agent.speed = _enemyController.RunSpeed; // 예: 6f
_enemyController.Agent.angularSpeed = 270f; // ### 추가한 부분
_enemyController.EnemyAnimator.SetFloat("MoveSpeed", 1f); // 애니메이션도 Run으로
}
else
{
// 가까운 거리: 걷기
_enemyController.Agent.speed = _enemyController.WalkSpeed; // 예: 2f
_enemyController.Agent.angularSpeed = 720f; // ### 추가한 부분
_enemyController.EnemyAnimator.SetFloat("MoveSpeed", 0.5f); // Walk 애니메이션
}
가까운 거리만 수치를 조절해보면서 테스트 해봤는데 여전히 바싹 붙은 플레이어를 바라보는데 오랜 시간이 걸렸다. Nav Agent의 회전이 굼뜨게 처리된다는 것을 알게 되었다.
찾아보니 Agent.acceleration
을 이용해서 가속도를 제어할 수 있어 시도해보았다. 단, 이 가속도는 이동속도에도 영향을 끼친다.(엄밀하게 말하면 최대 속도에 도달하는데 걸리는 시간에 관여한다.)
angularSpeed
는 1초에 회전하는 각도를 나타낸다. 아래와 같이 가속도 계수를 높혀주고 속도도 매우 빠르게 설정해주었다.
_enemyController.Agent.angularSpeed = 1080f;
_enemyController.Agent.acceleration = 999f;
이와 같이 비교적 만족스럽게 회전할 수 있게 되었다.
이 정도만 해도 충분해 보이지만 약간 부자연스럽다고 느껴진다. 이를 다른 방법으로 해결할 수 있다.
해결책 2. 코드를 이용해 수동으로 캐릭터를 회전시킨다.
Nav Agent의 회전 기능을 끄고 적절하게 회전시켜 주면 된다.
이렇게 처리하면 위에서 설정한 angularSpeed
방식과 비슷한 동작을 보여주지만, 보다 세밀하게 회전 속도를 조절할 수 있다는 장점이 있다.
_enemyController.Agent.updateRotation = false;
Vector3 direction = _detectPlayerTransform.position - _enemyController.transform.position;
direction.y = 0f; // 수직 회전 방지
if (direction.sqrMagnitude > 0.01f)
{
Quaternion lookRotation = Quaternion.LookRotation(direction);
_enemyController.transform.rotation = Quaternion.Slerp(
_enemyController.transform.rotation,
lookRotation,
Time.deltaTime * 10f // 회전 속도
);
}
Slerp
를 사용하면 자연스럽고 부드러운 회전이 가능하며, Time.deltaTime * 계수
값을 조절하여 회전 속도를 쉽게 튜닝할 수 있다.
두 방법을 결합해서 아래와 같이 코드를 작성했다.
private void PlayerTracking()
{
float distance = (_detectPlayerTransform.position - _enemyController.transform.position).magnitude;
if (distance > 5f)
{
// 먼 거리: 뛰기
_enemyController.Agent.speed = _enemyController.RunSpeed;
_enemyController.Agent.acceleration = 20f;
_enemyController.Agent.angularSpeed = 270f;
_enemyController.Agent.updateRotation = true;
}
else if (distance > 2f)
{
// 가까운 거리: 걷기
_enemyController.Agent.speed = _enemyController.WalkSpeed;
_enemyController.Agent.acceleration = 8f;
_enemyController.Agent.angularSpeed = 720f;
_enemyController.Agent.updateRotation = true;
}
else
{
// 매우 가까움: 직접 타겟 바라보기
_enemyController.Agent.updateRotation = false;
Vector3 direction = _detectPlayerTransform.position - _enemyController.transform.position;
direction.y = 0f; // 수직 회전 방지
if (direction.sqrMagnitude > 0.01f)
{
Quaternion lookRotation = Quaternion.LookRotation(direction);
_enemyController.transform.rotation = Quaternion.Slerp(
_enemyController.transform.rotation,
lookRotation,
Time.deltaTime * 10f // 회전 속도
);
}
}
// 실제 속도 기반으로 애니메이션 제어
float currentSpeed = _enemyController.Agent.velocity.magnitude;
_enemyController.EnemyAnimator.SetFloat("MoveSpeed", currentSpeed);
}
세부 수치는 조절해야겠지만 튀는 동작 없이 잘 동작한다.
마치며
두 가지 방법 모두 각각의 장단점이 있다.
- Nav Agent의 회전 속도 조절 방식은 NavMesh의 경로 탐색과 자연스럽게 연동되기 때문에 관리가 쉽고 직관적이지만, 회전 반응 속도에 한계가 있다.
- 수동 회전 방식은 더 빠르고 부드러운 반응이 가능하지만, Nav Agent의 방향성과 따로 놀게 되면 순간적인 충돌이나 미묘한 움직임 어긋남이 발생할 수 있다.
현재는 두 방법을 병행하여, 멀리 있을 땐 Nav Agent의 회전 기능을 사용하고, 가까이 있을 때는 수동으로 회전하도록 구현하고 있다.
이렇게 하면 먼 거리에서는 자연스럽게 방향을 잡고 이동하다가, 플레이어와 근접한 상황에서는 보다 민첩하게 방향을 트는 행동을 유도할 수 있다.
'Develop > Unity' 카테고리의 다른 글
싱글톤(Singleton) 패턴에 대해서.Araboza (0) | 2025.02.03 |
---|---|
[Unity] 오브젝트 풀링 (Object Pooling) (1) | 2025.01.08 |