C# 유니티 - 회전과 쿼터니언

Se0ng_1l·2022년 7월 25일
0
post-thumbnail

이 글은
https://www.inflearn.com/course/유니티-게임-프로그래밍-에센스
강의를 요약한 강의노트 입니다.
유니티를 처음 접하시는 분들이거나 좀 더 기반을 다지고 싶으신 분들에게 👍강력하게 추천합니다.

📌회전과 쿼터니언

오브젝트의 회전은 vector로 지정할 수 없다.
에디터에서는 x, y, z로 보여주기 때문에 vector로 착각할수 있지만
유니티에서 직관적으로 보기 편하도록 제공한것이다.

❌transform.rotation = new Vector3(30, 30, 30);

그럼 어떻게 사용해야하나

답은 쿼터니언을 사용하는 것

📌오일러각

오일러각이란, 회전하기 전 상태에서 회전한 다음 상태가 되려면
세 번(x축, y축, z축)나누어 각각 얼마만큼 회전하면 되는지 계산하여 회전을 표현하는 방식입니다.

오일러각의 문제점

짐벌락 :
3d오브젝트의 x, y, z축 중 한 축이 90도로 이전과 이후의 축 중 하나가
겹쳐 상실되는 현상
ex) z축으로 90도 회전했을때 이전의 y축이 x축과 겹쳐 차원 하나가 상실된다.

이를 해결하기 위해 사용하는 것이 쿼터니언이다.

📌쿼터니언

  1. 쿼터니언은 x, y, z, w를 다뤄 4차원이다.
  2. 쿼터니언을 사용하면 오브젝트를 한번의 연산으로 회전시키기 때문에 짐벌락현상이 발생하지 않고 90도 회전을 수행할 수 있다.
  3. 쿼터니언은 계산이 복잡하므로 유니티는 내부에서 쿼터니언으로 처리하지만 인스펙터 창에서는 트랜스폼 컴포넌트의 회전을 vector3로 다룬다.
  4. 우리가 직접 유니티의 쿼터니언 내부를 조작할 수는 없다.
    우리가 해야할 것은 Vector3를 쿼터니언으로 다룰 수 있어야 한다.

📌쿼터니언 vs 짐벌락

짐벌락

Code

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetRotation : MonoBehaviour
{
    int i = 0;
    Quaternion a = Quaternion.Euler(new Vector3(45, 0 ,0));
    Quaternion b = Quaternion.Euler(new Vector3(0, 90 ,0));
    Quaternion c = Quaternion.Euler(new Vector3(0, 0 ,60));
    void Update()
    {
        for (; i < 3; i++)
        {
            if (i == 0)
                transform.rotation *= a;
            if (i == 1)
                transform.rotation *= b;
            if (i == 2)
                transform.rotation *= c;
        }
    }
}

오브젝트의 Transform의 Rotation을 보면 45, 90, 60이 아니다.

쿼터니언

Code

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetRotation : MonoBehaviour
{
    void Start()
    {
        Quaternion newRotation = Quaternion.Euler(new Vector3(45, 90, 60));

        transform.rotation = newRotation;
    }
}

쿼터니언을 사용했더니 45, 90, 60으로 회전된 모습을 볼 수 있다.

📌관련함수

lookrotation

방향 혹은 오브젝트를 주면 주면 해당 위치를 보도록 회전을 시켜주는 함수
오브젝트의 얼굴은 z축이다.

void Start()
{
    Vector3 direction = targetTransform.position - transform.position;
    //Quaternion newRotation = Quaternion.Euler(new Vector3(45, 90, 60));
    Quaternion targetRotation = Quaternion.LookRotation(direction); // 직접 new Vector3로 넣어도 된다.
    transform.rotation = targetRotation;
}

Lerp

중간값, 두개의 값을 주면 두 값의 사이값을 리턴해주는 함수
Lerp(a, b, percent) a와 b사이의 값중 percent의 지점을 리턴
ex)
0.5f => a = 30 b = 60=> return 45
0 => return a
1 => return b

void Start()
{
    Quaternion aRotation = Quaternion.Euler(new Vector3(30, 0, 0));
    Quaternion bRotation = Quaternion.Euler(new Vector3(60, 0, 0));
    Quaternion targetRotation = Quaternion.Lerp(aRotation, bRotation, 0.5f);
    transform.rotation = targetRotation;
}

Rotate

회전을 더 시키는 방법

void Start()
{
    Quaternion targetRotation = Quaternion.Euler(new Vector3(45, 0, 0));
    transform.rotation = targetRotation;

    transform.Rotate(new Vector3(30, 0, 0));
}

eulerAngles

쿼터니언을 Vector3값으로 리턴
Rotate처럼 활용하기
1. 오브젝트의 트랜스폼으로부터 rotation을 쿼터니언으로 저장
2. eulerAngles를 사용해 Vector3로 바꾸기
3. 회전할 만큼 Vector3연산하기
4. 쿼터니언으로 변환한 후 다시 오브젝트에 적용시키기

void Start()
{
    Quaternion originalRotation = transform.rotation;
    Vector3 originalRotationInVector3 = originalRotation.eulerAngles;
    Vector3 targetRotationVec = originalRotationInVector3 + new Vector3(30, 0, 0);
    
    Quaternion targetRotation = Quaternion.Euler(targetRotationVec);
    transform.rotation = targetRotation;
}

쿼터니언 간의 연산

쿼터니언은 기본적으로 행렬로 이루어져있다.
행렬은 성분의 값을 증가시킬때 곱셈을 사용한다.
그래서 곱셈으로 연산을 해야 더 회전을 시켜줄 수 있다.

void Start()
{
    Quaternion originalRotation = Quaternion.Euler(new Vector3(45, 0, 0));
    Quaternion plusRotation = Quaternion.Euler(new Vector3(30, 0, 0));
    Quaternion targetRotation = originalRotation * plusRotation;
    transform.rotation = targetRotation;
}

⚠️주의 사항

Rotate함수로 회전을 시킬 경우
원하는 각도만큼 회전이 안되는 경우가 있다.
이유는 회전은 오브젝트 중심 즉 로컬을 기준으로 회전하기 때문에 발생하는 문제다.

아래는 Vector3(30, 45, 60)을 x축으로 30도 회전시킨 예시이다.

왼쪽
Rotate함수를 사용해 30도 회전시킨 모습, Vector3(40.5053596,79.7150192,80.5376892)
오른쪽
에디터에서 직접 30도 회전시킨 모습,
Vector3(60, 45, 60)

profile
치타가 되고 싶은 취준생

0개의 댓글