Player 회전

1. transform.LookAt()

플레이어가 이동하는 방향으로 회전하게 만들고 싶어서 먼저, transform.LookAt를 사용하였다.

public void PlayerMove()
{
    _rigidbody.MovePosition(transform.position + moveDirection * moveSpeed * Time.deltaTime);
    transform.LookAt(transform.position + moveDirection);
}


하지만 transform.LookAt을 사용할 시에 위 gif와 같이 부드러운 회전이 아닌 키보드 입력을 받는 그대로 transform이 확 돌아가 버린다.

2. Quaternion.Lerp()

부드러운 이동을 생각했을 때, 자연스럽게 선형 보간이 생각났다.

  • 선형 보간(Linear Interpolation)
    : 선형 보간이란 한 점과 또 다른 한 점의 값이 주어졌을 때, 그 두 점 사이의 값을 추정하기 위해서 직선 거리에 따라 선형적으로 계산하는 방법이다.

    그 계산은 비례식으로 다음과 같이 나타낼 수 있다.

    f(A) = Y f(A1) + X f(A2) (단, X + Y = 1)

선형 보간은 Vector3.Lerp로 이미 제공되어있다.

//Player 회전 메소드를 따로 만들었다.
public void PlayerRotate()
{
    Vector3 targetDirection = moveDirection.normalized;
    Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
    Quaternion interpolateRotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

    transform.rotation = interpolateRotation;
}

시작점을 현재 player의 rotaion값(transform.rotation), 회전하고 싶은 위치를 targetRotation으로 두어 그 둘의 값을 선형적으로 보간해주었다. player의 현재 위치가 transform.rotation = interpolateRotation 구문으로 실시간으로 업데이트 되기때문에 지속적으로 rotation * Time.deltaTime 만큼 가까워지게 된다. Time.deltaTime을 곱해준 이유는 고정적인 값으로 보간을 하는 것보다 매 프레임마다의 시간을 곱해주는 것이 보간이 더욱 자연스럽게 된다.

여기서 예상치 못한 오류가 발생했는데, 위 gif를 보면 알 수 있다시피 player의 transform이 자꾸 원래대로 돌아간다. 이는 player의 입력 값을 inputSystem을 사용하는 과정에서 발생하는 오류였다.

_moveAction.performed += context =>
{
    direction = context.ReadValue<Vector2>();
    moveDirection = new Vector3(direction.x, 0, direction.y).normalized;
    moveDirection.y = 0;
};

_moveAction.canceled += context =>
{
    moveDirection = Vector3.zero;
};

위 코드는 awake()의 일부이다. 위 코드 중 moveDirection = Vector3.zero;로 초기화 하였기 떄문에 발생한 문제인데, input 값이 없어지면 자동으로 일어나는 action이기 때문이다. 그렇다고 회전이 zero 값으로 돌아가는 오류를 고치기 위해서 위에 초기화하는 코드를 삭제하면

위 gif와 같이 키보드에 입력을 주지 않아도 계속해서 player object가 움직이게 된다. 이는 이동키의 값을 Vector2 형태로 value 값으로 받아오기 때문에 발생한 현상이다. moveDirection의 값을 0으로 초기화 하면서 회전을 되돌아가지 않게 하려면 어떻게 해야할까 또 고민하게 되었다.

3. horizontal값과 vertical값 분리 및 저장

zero값으로 돌아오는 것을 방지하기 위해 따로 값을 저장하였다.
zero값으로 돌아오는게 문제라면 초기화하기 전의 값을 따로 저장하면 될 것 같다고 생각했다.그래서 코드를 다음과 같이 수정하였다.

_moveAction.performed += context =>
{
    direction = context.ReadValue<Vector2>();
    horizontalMovement = direction.x;
    verticalMovement = direction.y;
    moveDirection = new Vector3(horizontalMovement, 0, verticalMovement).normalized;
    moveDirection.y = 0;
};

_moveAction.canceled += context =>
{
    moveDirection = Vector3.zero;
};

horizontalMovement와 verticalMovement는 필드에 미리 float 타입으로 정의한 변수이다. 이처럼 값을 따로 저장하고

public void PlayerRotate()
{
    Vector3 targetDirection = new Vector3(horizontalMovement, 0, verticalMovement);
    Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
    Quaternion interpolateRotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

    _rigidbody.MoveRotation(interpolateRotation);
}

targetDirection을 새로 저장해주었다. PlayerMove에서 rigidbody를 사용하였으니, 회전에서도 rigidbody를 사용해야 움직이면서 회전을 할 수 있다. 이렇게 해주니 다음과 같은 결과가 나왔다.

이전처럼 회전을 급박하게 하지는 않으나 부드러운 움직임은 여전히 나지 않고 회전이 계속 고정적인 위치로 돌아간다. 회전을 하다가 키보드를 떼는 순간의 위치에서 멈추게 하고 싶은데, 고정적인 자리로 스스로 돌아가게 되는 것이다.

4. bool을 이용한 상태 체크

키보드에 손을 떼는 순간이라는 아이디어를 생각했을 때, bool 값을 만들어서 손에서 땠을 때와 안땠을 때를 구분하면 되지 않을까란 생각을 하였다.

그 아이디어를 살려 만든 코드가 다음과 같다.

//isRotate값은 bool 타입으로 선언된 필드값이다. default = false;
//isRotate는 _moveAction.performed에서 true가 되고 _moveAction.cancled에서 false값이 된다.
public void PlayerRotate()
{
    if (isRotate)
    {
        Vector3 targetDirection = new Vector3(horizontalMovement, 0, verticalMovement);
        Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
        Quaternion interpolateRotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

        _rigidbody.MoveRotation(interpolateRotation);
    }
}

rotate를 하고 있다는 bool값을 만들어서 키보드에 손을 떼자마자 rotate를 하고 있지 않다고 알리고 그 즉시 회전을 중단하게 만든 것이다.

그 결과 더 이상 3번과 같은 문제점이 발생하지 않았다.

3d 환경에서 rotation을 자주 해보지 않아서 그런지 부드러운 회전을 만드는 과정이 꽤 길었다.

profile
NEWB!

0개의 댓글