Unity에서 캐릭터 이동을 구현하던 도중 하나의 문제점을 마주했다. Unity뿐만 아니라 다른 엔진을 사용하면서도 발생할 수 있는 문제이다. 큰 문제는 아닐 수 있지만 공유해볼법한 문제라고 생각하여 글을 적는다.
미리 적지만 난 아직 학생이다. 혹시 이 문제에 대해 보다 현명한 방법이 있다면 부디 적어주길 바란다. 당신의 지식은 나의 꽃틔울 토양이다.
float verticalInput = Input.GetAxis("Vertical");
float horizontaInput = Input.GetAxis("Horizontal");
// unnormalized
Vector3 moveDirection = orientation.forward * verticalInput + orientation.right * horizontaInput;
m_characterController.Move(moveDirection * speed * Time.deltaTime);
보통 위와 같은 코드를 사용하여 플레이어의 이동을 처리한다. 아래의 코드를 작성하여 플레이어의 속도를 측정한다. speed는 10으로 설정하였다.
Debug.Log(m_characterController.velocity.magnitude);
W키를 눌러 앞으로 이동할 경우 우리가 설정한 speed대로 움직이는 것을 볼 수 있다. 하지만 대각선 방향으로 이동하면 문제가 발생한다.
Vector3 moveDirection = orientation.forward * verticalInput + orientation.right * horizontaInput;
m_characterController.Move(moveDirection * speed * Time.deltaTime);
이 코드를 다시 보면 알겠지만, 가로로 1.0, 세로로 1.0을 동시에 이동하면 대각선 방향으로 만큼 이동하는 것은 당연지사. 거기에 speed 10을 곱한 속도로 이동하는 것을 볼 수 있다.
대부분의 경우 큰 문제는 없겠지만, 내가 설정한 속도 이상으로 움직일 수 있다니 상당히 건방지다. 무엇보다 게임에서 플레이어의 속도에 의존하여 레벨 디자인을 했을 경우(플레이어가 아슬아슬하게 통과하지 못할 타이밍에 문이 닫힌다거나...) 플레이어가 대각선으로 이동하면 디자이너가 예상하지 못한 동작을 할 가능성이 없지는 않다. 난 만약의 상황을 방지하고 싶으니 이를 수정하겠다.
코드를 아래와 같이 수정하여 해결을 시도했다.
// normalized
Vector3 moveDirection = (orientation.forward * verticalInput + orientation.right * horizontaInput).normalized;
m_characterController.Move(moveDirection * speed * Time.deltaTime);
테스트 결과 대각선 방향으로 이동해도 우리가 설정한 speed로 이동하는 것을 확인했다. 이로써 문제 해결인줄 알았으나 복병이 하나 남아있었다. 테스트 하는동안 묘하게 조작감이 불편했는데, Unity Input System이 보간해주는 속도를 전혀 이용하지 못했기 때문이다.
Unity Input System은 단순히 키를 누르면 1을 반환하고 떼면 0을 반환하지 않는다. 일정 시간을 가지고 0에서 1로, 1에서 으로 서서히 떨어트린다. 그 결과 우리는 부드럽게 정지하는 캐릭터를 손쉽게 만들 수 있다.
문제는 내가 moveDirection을 normalize했다는 점이다. 보간이 시작된 뒤로는 moveDirection의 norm이 1에서 0으로 서서히 감소할 것인데, 그것을 무시하고 normalize했으니 보간이 시작되어도 moveDirection은 항상 1의 norm을 가지게 된다.
그 결과는 전혀 부드럽지 못한 캐릭터 이동이다. 키에서 손을 떼면 플레이어는 여전히 10의 속도로 이동하다가, 보간이 끝나는 순간 뚝 하고 그 자리에서 멈춰버린다. 우리가 원하는 속도로 움직이게 하는데는 성공했지만, 이건 아니다.
moveDirection은 normalize하되, 올바른 norm을 구해서 곱하면 해결된다.
// normalized
Vector3 moveDirection = (orientation.forward * verticalInput + orientation.right * horizontaInput).normalized;
float moveSpeed = Mathf.Min((orientation.forward * verticalInput + orientation.right * horizontaInput).magnitude, 1.0f) * speed;
m_characterController.Move(moveDirection * moveSpeed * Time.deltaTime);
moveDirection의 norm을 구하여 1을 넘지 않도록 한 후, speed를 곱했다. 이로써 항상 우리가 설정한 속도로 움직이되, Unity Input System이 보간해주는 값도 여전히 이용할 수 있게 되었다.