[Unity] Slerp 기반 회전 로직 개선

shin0112·2025년 11월 17일

Unity

목록 보기
9/13

✨ 들어가며

캐릭터 선택 화면에서 모델 회전 로직을 정리한다.

기존에는 각도를 60도씩 즉시 변경하는 방식을 사용했다.
동작은 문제없었지만,
UI 흐름이 “툭” 하고 바뀌는 느낌이라 시각적인 연결감이 부족했다.

이를 개선하기 위해 부드러운 회전 애니메이션을 적용하고,
코루틴을 활용해 자연스럽게 회전하도록 구조를 변경했다.

1️⃣ 기존 구조 — 즉시 회전 방식

이전 방식은 단순히 각도에 ±60도를 더해 회전시키는 구조였다.

public void RotateRight()
{
    Vector3 rotation = _characterContainer.localEulerAngles + new Vector3(0, 60f, 0f);
    _characterContainer.localEulerAngles = rotation;

    _curSelectIndex = (_curSelectIndex + 1) % Define.CharacterCount;
    Logger.Log($"현재 캐릭터 번호: {_curSelectIndex}");
}

public void RotateLeft()
{
    Vector3 rotation = _characterContainer.localEulerAngles + new Vector3(0, -60f, 0f);
    _characterContainer.localEulerAngles = rotation;

    _curSelectIndex = (_curSelectIndex + Define.CharacterCount - 1) % Define.CharacterCount;
    Logger.Log($"현재 캐릭터 번호: {_curSelectIndex}");
}

각도가 즉시 갱신되기 때문에 빠르게는 동작했지만,
시각적으로는 모델이 순간이동하는 것처럼 보여 자연스럽지 않았다.

2️⃣ 코루틴 도입 — 회전 애니메이션 처리

회전을 부드럽게 만들기 위해 Slerp 기반의 보간 코루틴을 사용했다.
다만 코루틴을 반복 실행하면 중첩되어 회전이 꼬일 수 있기 때문에,
현재 회전이 진행 중인지 확인하는 로직이 필요했다.

IEnumerator _rotateCoroutine;

private void StartRotate()
{
    // 이미 진행 중인 코루틴이 있으면 중단
    if (_rotateCoroutine != null)
    {
        StopCoroutine(_rotateCoroutine);
    }

    // 새로운 회전 코루틴 시작
}

코루틴을 변수로 관리하면
한 번에 하나의 회전만 수행할 수 있고,
기존 회전이 도중에 종료돼도 다음 회전이 정상적으로 시작된다.

3️⃣ 목표 각도 계산 방식 변경

기존에는 매번 +60 또는 -60을 더하는 방식이었다.
하지만 회전 중간에 코루틴이 끊기면
현재 각도가 중간값으로 남게 되어 누적 오차가 발생했다.

예시:

  • 목표 각도: 60 → 120도
  • 중단 시점: 34.1도
  • 다음 회전 시 +60 → 94.1도 (실제 목표는 120도)

이를 방지하기 위해
캐릭터 인덱스 기반의 고정 각도 계산으로 변경했다.

float targetAngle = _curSelectIndex * 60f;

이제 버튼을 여러 번 눌러도
항상 인덱스에 따라 정확한 목표 각도로 이동한다.

4️⃣ Slerp을 이용한 부드러운 회전

Quaternion.Slerp을 사용해
시작 각도에서 목표 각도로 자연스럽게 회전하도록 구현했다.

private void StartRotate()
{
    if (_rotateCoroutine != null)
        StopCoroutine(_rotateCoroutine);

    float targetAngle = _curSelectIndex * 60f;
    _rotateCoroutine = RotateCoroutine(targetAngle);
    StartCoroutine(_rotateCoroutine);
}

private IEnumerator RotateCoroutine(float value)
{
    float elapsed = 0f;

    Quaternion start = _characterContainer.localRotation;
    Quaternion end = Quaternion.Euler(0, value, 0);

    while (elapsed < _rotateDuration)
    {
        elapsed += Time.deltaTime;
        float t = elapsed / _rotateDuration;
        _characterContainer.localRotation = Quaternion.Slerp(start, end, t);
        yield return null;
    }

    _characterContainer.localRotation = end;
}

회전이 부드럽게 이어지고,
중간에 끊겨도 다음 입력에서 정확한 위치로 정렬된다.

✏️ 마치며

  • 즉시 회전보간 기반 부드러운 회전으로 전환
  • 코루틴을 변수로 관리해 중복 실행 방지
  • 인덱스 기반 목표 각도로 오차 없는 회전 유지

단순히 60도 회전을 보간했을 뿐이지만,
조작 시 반응감과 완성도가 눈에 띄게 개선됐다.
확실히 애니메이션이 들어가야 완성도가 높아 보인다.

0개의 댓글