我只能說這一章是我最不熟悉也最無法適應的,所以今天凌晨五點就起來開始寫文章,太有趣了。關於在 Unity 中實現旋轉的方式真的很多種,但怎樣旋轉才是最正確最穩定的一種方式,這我就真的只能說,我也不是很確定。我們知道四元數可以避免掉萬象鎖的問題,但就是難以理解也不好使用與計算,極為複雜,而使用 Euler 歐拉角雖然直觀容易使用與理解,但產生的萬象鎖且在Unity 旋轉有固定的方向性有些困擾,以及圍繞在哪個軸旋轉的問題也都需要經過思考。以下我把我使用過以及自己理解的方式與各位IT高人分享,也希望透過以下文章重新釐清旋轉的問題。之後有機會我會在更加深入討論,今天先針對一些簡單旋轉的問題來做探討。
首先我們新增一個物體叫做 Player ,並且將Main Camera 成為其 Child。同時注意到新增一個Sphere Child obj 來顯示Player 看出的方向。
這邊我新增一個圓球叫做 TestScene。很簡單目的就是要代表一個場景的感覺,而且上面圍繞許多不同角度的圓球,紅球為前後,藍球為上下,綠球為左右。黃球為該個不同角度看出的結果。上下各30, 60 度、左右各45, 90 度。上下四個左右五個共20個點。
將新增腳本 RotateObj放置於該 Player 上面。
Transform.Rotate(float angle_x, float angle_y,float angle_z, Space relativeTo = Space.Self);
Transform.Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
Transform.Rotate(Vector3 axis, Space relativeTo = Space.Self)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateObj : MonoBehaviour
{
public float rotateSpeed = 10f;
void Update()
{
transform.Rotate(Vector3.right * rotateSpeed * Time.deltaTime); // Vector3.right = Vector3(1, 0, 0)
}
}
回到 Unity 中執行,並調整該 RotateSpeed 的速度。
你可以發現到該Player先向下旋轉,這代表說在 Unity Scene 中旋轉X軸數值為正為向下,負為向上。首先看到Forward 的紅色圓球。最後看到Backward 的紅色圓球。
一開始:
最後:
一開始:
最後:
通常會需要專注在某一個物品或是方向時,我們會使用到 LookAt 的函數,看來好像很簡單就是看向某一個物件而已,但其實也有一些特殊的情況等,僅一種使用方法。 第一個參數是你想要看到的座標,第二個是你要基於哪個軸旋轉,預設為 Space.up。
Transform.LookAt(Vector3 position, Space relativeTo = Space.up)
public Transform targetObj;
private Quaternion originRotate;
private Quaternion getEndRotate;
private float lerpSubSpeed = 20.0f;
float lerp_speed = 0.0f;
float lerp_tm = 0.0f;
void Start()
{
originRotate = transform.rotation;
transform.LookAt(targetObj.position);
getEndRotate = transform.rotation;
transform.rotation = originRotate;
float rotateAngle = Quaternion.Angle(originRotate, getEndRotate);
lerp_speed = lerpSubSpeed / rotateAngle;
Debug.Log("Angle: " + rotateAngle.ToString() + ", speed: " + lerp_speed.ToString());
lerp_tm = 0.0f;
}
void Update()
{
lerp_tm += Time.deltaTime * lerp_speed;
transform.rotation = Quaternion.Lerp(originRotate, getEndRotate, lerp_tm);
if(lerp_tm >= 1) // need to know that Lerp is 0 ~ 1
{
transform.rotation = getEndRotate;
}
}
首先 x = 0, y = -30 度
x = 90, y = -30 度
x = -45, y = -30 度
以上都會緩慢的移動到目標,看來是沒有任何問題XDD
但請注意到自動讓系統使用 LookAt 會有最後一個參數調整的問題,也就是基於旋轉軸旋轉無法明確的決定。
public static Quaternion Euler(float x, float y, float z);
public static Quaternion Euler(Vector3 euler);
void Update()
{
Vector3 rotationVector = new Vector3(-60, 45, 0);
Quaternion rotation = Quaternion.Euler(rotationVector);
transform.rotation = rotation;
}
transform.rotation = Quaternion.Slerp();
Quaternion.Slerp(Vector3 StartRotation, Vector3 EndRotation, float t);
public Transform startRotate, endRotate;
public float time = 0.0f;
void Start()
{
}
void Update()
{
transform.rotation = Quaternion.Slerp(startRotate.rotation, endRotate.rotation, time);
time += Time.deltaTime/Quaternion.Angle(startRotate.rotation, endRotate.rotation);
}
transform.RotateAround();
public void RotateAround(Vector3 point, Vector3 axis, float angle)
public Transform SceneObj;
void Update()
{
SceneObj.RotateAround(Vector3.zero, Vector3.right, 20 * Time.deltaTime);
}
回到 Unity 執行後就會看到差異。可以看到左右綠球的位置不變,也可以透過右邊的 TestScene 看到僅X軸的數值在變動。注意到那是因為中心點為原點所以才可以有效的行程自體旋轉。
public Transform SceneObj;
void Update()
{
SceneObj.RotateAround(Vector3.zero, Vector3.up, 20 * Time.deltaTime);
}
回到Unity 就會看到這次以 Y 軸進行旋轉。上下藍球位置不變。可以透過右邊的 TestScene 看到僅Y軸的數值在變動。
void Update()
{
SceneObj.RotateAround(transform.position, Vector3.up, 20 * Time.deltaTime);
}
歐拉角旋轉有旋轉的最大數值,還有萬象鎖的問題,基本上我使用都會有一些問題,所以這邊還是建議四元數的旋轉會比較適當,但還是有像一開始對兩個方式說明的好處,因此適合使用哪一個都可以。
接下來我們實際透過 w, a, s, d 來使物體根據鍵盤案下的鍵來決定旋轉幾度。
float yRotation = 1f, xRotation = 1f;
void Start()
{
print("transform.eulerAngles.x: " + transform.eulerAngles.x);
print("transform.eulerAngles.y: " + transform.eulerAngles.y);
print("transform.eulerAngles.z: " + transform.eulerAngles.z);
}
void Update()
{
yRotation += Input.GetAxis("Horizontal");
xRotation -= Input.GetAxis("Vertical");
transform.eulerAngles = new Vector3(xRotation, yRotation, 0f);
}