참고
- 쿼터니언으로 구현한 단진자의 운동
- 쿼터니언으로 구현한 단진자의 운동 확장
- 독립된 단진자의 운동
- 런타임에 단진자 운동 설정하기
빨간색 Sphere를 pivot으로 하는 단진자를 Quaternion.Lerp를 이용해서 구현해보자.
쿼터니언의 Lerp 메서드
Quaternion.Lerp(start, end, float t = [0 ~ 1])은 0에 가까우면 start의 각도가, 1에 가까우면 end의 각도가 된다.
값이 0.5라면 start와 end의 중심이 된다.
단진자는 가운데로 갈수록 속도가 커지고 양 옆에서는 속도가 0이 된다.
그리고 이것이 계속 반복된다.
이러한 조건을 잘 만족하는 함수는 삼각함수이다.
그런데 sinΘ 함수는 -1 ~ 1사이의 값을 가지므로, 전체 값에서 1을 증가시킨다.
그러면 sinΘ + 1 = 0 ~ 2가 되는데, 쿼터니언의 Lerp는 [0 ~ 1] 사이의 값을 넣어줘야 하므로 2로 나눈다.
위치가 일정하게 제대로 호출되기 위해서 FixedUpdate에 위의 내용을 구현하면 다음과 같다.
void FixedUpdate()
{
startRot += (Time.fixedDeltaTime * speed);
pivot.rotation
= Quaternion.Lerp(startQt, endQt, (Mathf.Sin(startRot - Mathf.PI / 2) + 1.0f) / 2.0f);
}
Mathf.PI / 2 (= 90º)를 빼는 이유는 최초의 startRot이 0일 때, (sin(0) + 1) / 2 = 0.5가 되기 때문이다.
따라서 -90º만큼 빼줘야 startQt부터 시작하게 된다.
이제 각도 Θ에 대해 알아보자.
위 식에서 Θ는 pivot의 중심 아래로 내려오는 벡터와 (pivot → 오브젝트) 벡터가 만드는 각도다.
식으로 나타내면 다음과 같다.
float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);
그리고 시작점에서 최대 2Θ 만큼 각이 변하게 될 것이다.
쿼터니언의 곱셈으로 오브젝트의 각을 변화할 수 있으므로, 시작점과 끝점의 쿼터니언을 구할 수 있다.
여기서 pivot의 Rotation은 모두 0이라고 가정한다.
Quaternion getRotateQuaternion(float angle)
{
Quaternion current = Quaternion.Euler(new Vector3(0, 0, 0));
Quaternion quaternion = Quaternion.Euler(new Vector3(angle, 0, 0));
return current * quaternion;
}
void Start()
{
float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);
startQt = getRotateQuaternion(pivot, 0);
endQt = getRotateQuaternion(pivot, 2 * angle * dir);
}
그런데 위의 경우는 오브젝트가 왼쪽에 있다고 가정한 경우이다.
오른쪽인 경우도 보완하면 아래와 같은 식이 만들어진다. (오른쪽에 있다면 -2Θ 만큼 변화)
void Start()
{
float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);
float dir = (pivot.position.z < this.transform.position.z) ? 1.0f : -1.0f;
startQt = getRotateQuaternion(pivot, 0);
endQt = getRotateQuaternion(pivot, 2 * angle * dir);
}
FixedUpdate에서 pivot이 회전하도록 하였으므로, 오브젝트를 pivot의 자식으로 만든다.
게임을 실행하면 오브젝트가 왼쪽이든 오른쪽이든 잘 이동하는 것을 알 수 있다.
라인 렌더러 관련된 코드는 전체 코드를 참고하자.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SimpleHarmonicOscillator : MonoBehaviour
{
public Transform pivot;
public float speed = 2.0f;
Quaternion startQt, endQt;
public GameObject lineRenderer;
LineRenderer[] lrs;
float startRot = 0.0f;
void setLineRenderer(LineRenderer lr, Color color)
{
lr.startWidth = lr.endWidth = .1f;
lr.material.color = color;
lr.positionCount = 2;
}
Quaternion getRotateQuaternion(float angle)
{
Quaternion current = Quaternion.Euler(new Vector3(0, 0, 0));
Quaternion quaternion = Quaternion.Euler(new Vector3(angle, 0, 0));
return current * quaternion;
}
void Start()
{
float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);
float dir = (pivot.position.z < this.transform.position.z) ? 1.0f : -1.0f;
startQt = getRotateQuaternion(0);
endQt = getRotateQuaternion(2 * angle * dir);
lrs = lineRenderer.GetComponentsInChildren<LineRenderer>();
setLineRenderer(lrs[0], Color.blue);
lrs[0].SetPosition(0, pivot.position);
}
void Update()
{
lrs[0].SetPosition(1, this.transform.position);
}
void FixedUpdate()
{
startRot += (Time.fixedDeltaTime * speed);
pivot.rotation
= Quaternion.Lerp(startQt, endQt, (Mathf.Sin(startRot - Mathf.PI / 2) + 1.0f) / 2.0f);
}
}
위의 실행결과는 아래의 unitypackage에서 확인 가능하다.
Unity Plus:
Unity Pro:
Unity 프리미엄 학습:
'개발 > Unity' 카테고리의 다른 글
유니티 - 독립된 단진자의 운동 (Independent Simple Pendulum with Unity LerpAngle) (0) | 2023.03.29 |
---|---|
유니티 - 쿼터니언으로 구현한 단진자의 운동 확장 (Expanded Simple Pendulum with Unity Quaternion) (0) | 2023.03.29 |
유니티 - 세 점을 지나는 평면 구하기 (Creating a Plane from 3 Dots) (0) | 2023.03.27 |
유니티 - 스트로보 효과 (Stroboscopic Effect with Unity) (0) | 2023.03.26 |
유니티 - Unity에서 리액트로 이벤트 호출하기 (Communication from Unity to React) (0) | 2023.03.25 |
댓글