Unity

유니티 수학 Mathf.Sin()과 Quaternion.Lerp()

박도치 2024. 1. 2. 22:53

프로젝트를 하면서 Mathf.Sin()과 Quaternion.Lerp() 를 사용할 일이 생겨 사용하는 김에 어떤것이고 어떨때 필요한지를 정리하기 위해 작성하는 포스트이다.

 

Mathf.Sin(), Math.Cos() 

 

Sin, Cos함수 Mathf 구조체 안에 있다.

 

Sin Cos 는 각각 각도에 따라 Sin은 높이를, Cos는 밑변을 나타내는 함수이다.

 

빗변의 길이는 1로 유니티에서 정해줬으며 이에 따른 함수 내에 매개변수로 각도를 정해줄 수 있다.

 

그렇다면 이를 주기적으로 변하게 하려면 어떤식으로 할 수 있는가? 그건 현재 게임 시간을 특정 수와 곱해주면 된다.

 

특정 수는 해당 물체의 속도가 될것이다. 왜냐하면 시간마다 각도가 변하는데 시간은 일정하게 흐르지만 이를 높은 수를 곱할수록 더 빠르게 각이 변하기 때문에 속도가 올라가는 것이다.

 

 

코드 적용

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class SwingObject : MonoBehaviour
{
    public float swingSpeed = 2.0f;  // 판자의 흔들리는 속도
    public float swingAngle = 30.0f; // 판자의 최대 흔들리는 각도

    void Update()
    {
        // swing
        float angle =  Mathf.Sin(Time.time * swingSpeed) * swingAngle;

        Debug.Log(angle);

        // 판자 rotation값 변화
        transform.rotation = Quaternion.Euler(angle, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z);
   

    }
}

 

 

위 코드는 판자가 그네처럼 움직이는 코드이다.

 

보면 float angle에 Sin값에 특정 각도를 곱해주고있다.

 

천천히 살펴보면 위에서 말했듯이 게임 시간에 특정 값(속도) 를 곱해주므로써 주기적으로 각을 높여주고 있고, 이 최대치를 정해주기 위해 swingAngle이라는 최댓값을 임의로 정해서 곱해주고 있다.

 

이를 현재 오브젝트의 rotation값에 Quaternion.Euler의 값을 각각 넣어주고있다.

 

그렇다면 Quaternion.Euler() 는 또 대체 무엇인가?

 

 

Quaternion

 

Quaternion이라고 하면 3D 컴퓨터 그래픽스에서 회전을 나타내는 수학적 개체를 말한다. 보통 3D 에서 회전을 표현하는데 사용된다.

 

Quaternion은 세 개의 표준 축(x, y, z)를 기준으로 회전을 표현하며 각 축을 따라 얼마나 회전되었는지를 나타내는데 사용된다.

 

Euler각 즉, 각 축을 따라 얼마나 회전되었는지를 나타내는 값으로 Quaternion.Euler를 사용한다.

 

 

Quaternion.Euler()

위에서 말했듯 Euler 각도는 특정 축을 중심으로 회전된 각도를 나타내는 방식이다. 

 

 

Quaternion.Euler(float x, float y, float z); 이렇게 나타내며 

 

x는 x축을 중심으로 하는 회전 각도, y는 y축을 중심으로 하는 회전 각도, z는 z축을 중심으로 하는 회전각도를 의미한다.

 

예를들어 Quaternion.Euler(0f, 45f, 0f); 가 있다고 치면 y축을 중심으로 회전하는 Quaternion을 말하는 것이다.

 

 그렇다면 위 코드에서 이부분이 어느정도는 해소될것이다.

 

transform.rotation = Quaternion.Euler(angle, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z);

 

현재 물체의 rotation값을 아까 Sin값으로 특정 값까지 각도를 올려주거나 내려주는 것을 반복하는 값을 x축으로 하여 x축을 중심으로 돌면서  나머지값은 transform.eulerAngles 을 통해 현재 값을 유지해주는 역할을한다.

 

그래서 해당 코드를 정리하자면 특정 1 ~ -1 사이를 반복하면서 일정한 속도로 특정 값만큼 각도가 변화하는 값을 x축을 중심으로 물체를 회전시키는 코드라고 볼 수 있다.

 

 

Quaternion.Lerp()

 

 Quaternion.Lerp 쿼터니언의 중간의 값을 반환하여 부드러운 효과를 준다. Quaternion.Lerp(시작 쿼터니언, 타겟 쿼터니언, 0~1까지 있는 시간) 

 

그래서 코루틴을 통해 시작 시간과 도착 시간을 정해 시작시간을 deltatime을 더해주면서 시작 시간이 도착 시간보다 작을 때 현재 쿼터니언에서 목표 쿼터니언까지 부드럽게 변화되게 된다.

 

  

using System.Collections;
using System.Collections.Generic;
using UnityEditor.Rendering;
using UnityEngine;

public class DoorObjectInteraction : MonoBehaviour
{
    [SerializeField] private float rotY = 0.5f;

    private Rigidbody _rigidBody;
    private GameObject _gameObject;

    private Quaternion _objectRotation;
    private bool _isInteracting = false;

    private void Start()
    {
        _rigidBody = GetComponent<Rigidbody>();
        _gameObject = _rigidBody.gameObject;
        _objectRotation = gameObject.transform.rotation;

        Debug.Log(_objectRotation.y);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Player"))
        {
            Debug.Log("???");
            //InteractDoor();
            StartCoroutine(InteractDoor());

        }
    }

    /// <summary>
    /// 코루틴을 이용해 문을 회전
    /// </summary>
    /// <returns></returns>
    private IEnumerator InteractDoor()
    {
        _isInteracting = true;

        Quaternion currentRotation = _gameObject.transform.rotation; // 오브젝트의 현재 rotaion값
        
        Quaternion targetRotation = Quaternion.Euler(currentRotation.eulerAngles + new Vector3(0f, -90f, 0f)); // 내가 원하는 rotation값

        float elapsedTime = 0f;
        float duration = 0.3f; // 회전에 걸리는 시간

        // 회전 시간
        while (elapsedTime < duration)
        {
            // Quaternion.Lerp 쿼터니언의 중간의 값을 반환하여 부드러운 효과를 줌, Quaternion.Lerp(시작 쿼터니언, 타겟 쿼터니언, 0~1까지 있는 시간)
            _gameObject.transform.rotation = Quaternion.Lerp(currentRotation, targetRotation, elapsedTime / duration);
            elapsedTime += Time.deltaTime;
            yield return null;
        }

        _gameObject.transform.rotation = targetRotation;
    }
}

 

 

해당 코드는 충돌시에 코루틴이 발동하며 현재 오브젝트의 rotation값과 내가 원하는 rotation값을 정해 이를 필요한 시간으로 부드럽게 변화시켜주는 역할을 한다.

 

실행하면 아래와 같다.

'Unity' 카테고리의 다른 글

유니티 SkyBox로 배경 꾸미기  (0) 2024.01.04
AddForce()  (0) 2024.01.03
로딩 구현하기  (0) 2023.12.28
[FSM] 플레이어 상태머신 (2)  (0) 2023.12.27
[FSM] 플레이어 StateMachine 만들어보기  (1) 2023.12.26