Transform
GameObject和Transform
Transform是每個GameObject都具有的屬性,Transform是GameObject的一個特殊的Component。嘗試刪除Transform時會發現,Unity並沒有提供可以刪除Transform組件的選項,也就是說GameObject必須具有Transform組件。同樣的,也沒有任何方法添加Transform組件,也就是說GameObject只能具有一個Transform組件。
一個GameObject只對應一個Transform,一個Transform也只對應一個GameObject。在腳本中,我們可以直接通過gameObject獲得對應Transform組件,也可以通過transform獲得對應的GameObject,同時,用GetComponent方法也可以獲得Transform組件:
public GameObject MonoBehaviour.gameObject{get;}
public Transform MonoBehaviour.transform{get;}
public Tranform GameObject.tranform{get;}
public GameObject Tranform.gameObject{get;}
gameObjcet.tranform == gameObject.GetComponent<Transform>();
tranform == gameObject.tranform.gameObject.GetComponent<Tranform>();
在Inspector中,每個GameObject的第一個組件都是Transform。Transform暴露出來的屬性包括Position、Rotation和Scale,顧名思義,它們就是物體的位置、旋轉、縮放信息。在Scene中移動、旋轉、縮放物體,可以觀察到Inspector中的Transform改變,而在Inspector中改變這三個屬性,也可以在Scene和Game面板中看到修改的效果。
默認情況下,position、rotation均爲零向量,scale爲(1,1,1)。在Inspector面板中對Transform使用Reset即可使三個屬性重置爲默認值。注意,使本地座標歸零的結果是使該物體與父物體座標系重合。
複習本地座標系與世界座標系:
本地座標系指物體的父節點的模型座標空間,如果物體沒有父節點,則本地座標系等於世界座標空間。
本地座標是在本地座標系中的座標,而世界空間座標是在世界座標系中的座標。
Transform和座標信息
transform.position = new Vector3(1,2,3);
transform.localPosition = new Vector3(1,2,3);
transform.position += new Vector3(1,0,0);
transform.localPosition += new Vector3(-1,0,0);
通過position可以訪問物體的世界座標,通過localPosition可以訪問物體的本地座標。
由於Vector3重載了加減運算,故可以使用“+=”來修改position。
除了直接對position修改外,還可以調用Transform的函數:
public void Transform.Translate(float x, float y, float z, Space relativeTo = Space.Self);
public void Transform.Translate(Vector3 translation, Space relativeTo = Space.Self);
Space是一個枚舉類型,具有兩個枚舉值:Space.Self和Space.World。前者使移動作用於本地座標,後者使移動作用於世界座標。
由於在Vector3類中,x、y、z屬性是隻讀的,有時會造成麻煩,我們可以自己擴展方法,如下:
public static class Extensions
{
public static void SetPositionX(this Transform t, float newX)
{
t.position = new Vector3(newX, t.position.y, t.position.z);
}
}
擴展已有類時,我們需要一個靜態類中的靜態方法,這個方法的第一個參數增加this關鍵字,第一個參數的類型就是我們想要擴展的類型,這樣我們就可以通過Transform對象直接調用這個方法了:
transform.SetPositionX(1);
public Quaternion Transform.rotation;
public Quaternion Transform.localRotation;
通過Transform的屬性獲取軸:
public vector3 Transform.up{get; private set;}
public vector3 Transform.forward{get; private set;}
public vector3 Transform.right{get; private set;}
通過Transform的方法旋轉物體:
public void Transform.Rotate(Vector3 eulers, Space relativeTo = Space.Self);
public void Transform.Rotate(float x, float y, float z, Space relativeTo = Space.Self);
public void Transform.Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
public void Transform.RotateAround(Vector3 point, Vector3 axis, float angle);
public void Transform.LookAt(Vector3 worldPosition, Vector3 worldUp = Vector3.up);
public void Transform.LookAt(Transform target, Vector3 worldUp = Vector3.up);
Rotate函數具有兩種完全不一致的效果,很容易使人產生困惑。在前兩種重載中,物品以當前方向爲基準重新進行歐拉變換,即沿z軸->y軸->x軸的順序依次旋轉指定角度,得到新的方向。而後一種重載中,物品繞自身的axis方向的軸,遵循左手螺旋定則的旋轉angle角度。
RotateAround函數中,物體繞目標座標在axis方向的軸旋轉angle度,旋轉過程中座標和自身方向都會改變。
LookAt函數可以使物體的正面朝向某個點或Transform,所謂正面即z軸的正方向。在不使用第二個參數的情況下,物體只繞y軸旋轉,而在使用第二個參數的情況下,物體的y軸正方向會指向第二個參數的方向。
Unity中沒有使物體朝向某個方向的函數,但這個方法在移動時又十分常用,我們可以擴展一個LookTo方法來快捷的使用這個功能:
public static class Extensions
{
public static void LookTo(this Transform t, Vector3 direction, Vector3 worldUp = Vector3.up)
{
t.LookAt(t.position + direction,worldUp);
}
}
public vector3 Transform.localScale;
public vector3 Transform.lossyScale{get; private set;}
注意,世界座標的大小是隻讀的。
推薦儘量少使用改變縮放的功能:一些縮放可以改用動畫系統實現,還有更多情況下,模型或圖片的大小應該由美術確定好後直接導入,GameObject的Scale應該儘量都保持(1,1,1)。
Transform和樹型結構
讓相關的GameObject儘量按照樹型結構組織,可以有效的提高搜索物體的效率,也減少不必要的內存損耗。
public Transform Transform.parent{get; private set;}
public Transform Transform.root{get; private set;}
public bool Transform.IsChildOf(Transform parent);
public void SetParent(Transform parent, bool worldPositionStays = true);
注:當沒有父節點時parent返回null,在SetParent中輸入參數null也可以讓物體不再有父節點,在IsChildOf中輸入參數null可以判斷物體是否沒有父節點。
root代表最終根節點,可以返回自己。
SetParent函數的第二個參數爲false時,物體會改變在世界座標系中的位置,在新的座標系中保持和在原本座標系中一致的本地座標,即和新父物體的相對位置等於和舊父物體的相對位置。
public Transform Transform.Find(string n);
public Transform Transform.GetChild(int index);
public void Transform.DetachChildren();
使用Find可以根據物體名稱搜索此節點派生出的所有後繼節點,是GameObject.Find的範圍縮小版本。
GetChild是速度最快的查找子物體的方法,所以讓Prefabs或者GameObject以一個固定的順序排列是一個很好的習慣。
DetachChildren可以一次性斷開對所有子物體的連接,並使子物體移動到同一層級,即如果本物體有父物體,斷開連接後子物體會成爲原本父物體的子物體,成爲本物體的兄弟節點。