文章目錄
- 7.1 超過光速的移動
- 7.2 初識Transform類
- 7.3 Transform的屬性
- 7.3.1 設定坐標
- 7.3.2 設定旋轉角度
- 7.3.3 設定縮放
- 7.3.4 設定朝向
- 7.3.5 設定父節點
- 7.4 Transform的函式
- 7.4.1 移動物體
- 7.4.2 旋轉物體
- 7.4.3 方向轉換計算
- 7.4.4 坐標轉換計算
- 7.4.5 查找子物體
- 7.4.6 判斷是否是子節點
7.1 超過光速的移動
我:“皮皮,你知道真空光速是多少嗎?”
皮皮:“你以為我是百科全書呀?不過我知道,目前所知的物理定律里,光速是無法超越的,連我們貓族也無法超越這個速度,”
我:“真空中的光速是299792458米/秒,大約300000米/毫秒,在Unity中,你信不信我可以超越光速?”
皮皮:“真的假的?”
我打開Unity,說:“真的,不信我可以證明給你看,”
首先,創建一個Cube,

接著,創建一個腳本TransformTest.cs,

代碼如下:
using System.Diagnostics;
using UnityEngine;
public class TransformTest : MonoBehaviour
{
void Update()
{
// 檢測空白鍵按下
if (Input.GetKeyDown(KeyCode.Space))
{
// 使用Stopwatch測量運行時間
Stopwatch sw = new Stopwatch();
// 時間測量開始
sw.Start();
// 執行設定坐標
SetPos();
// 時間測量結束
sw.Stop();
// 輸出日志
UnityEngine.Debug.Log("總耗時: " + sw.ElapsedMilliseconds);
}
}
/// <summary>
/// 設定坐標
/// </summary>
void SetPos()
{
// 臨時快取transform物件
Transform selfTransform = transform;
// 回圈執行10000次,看總耗時,在計算單次的運行時間
for (int i = 0; i < 100000; ++i)
{
selfTransform.position = Vector3.one * i;
}
}
}
注:上面代碼中,我用到了
Stopwatch這個類,用它可以精確測量函式的運行時間,
將TransformTest腳本掛到Cube上,

運行Unity,按下空白鍵,可以在console視窗中看到日志輸出,

由此可得,執行10000次的position操作,耗時8毫秒,折算一下,1毫秒可以執行position操作1250次,而真空光速是約300000米/毫秒,300000除以1250等于240,也就是說,只要一次position操作的距離超過240即可超過光速啦,如下:
// 設定初始坐標為Vector3.zero,即(0, 0, 0)
transform.position = Vector3.zero;
//從坐標(0, 0, 0)移動到(241, 0, 0),根據上面的計算,這個移動已經超過光速了
transform.position = new Vector3(241, 0, 0);
皮皮:“真是不可思議呀,電腦的運行速度這么快,”
我:“不過這是虛擬世界里的速度,真實的物理世界,光速還是無法超越的,另外需要注意一點,不同計算機的CPU主頻不同,運行速度不同,比如上面執行10000次的position操作耗時8毫秒,可能別的性能差一點的電腦就要耗時14毫秒,那么最后算出值就不同了,”
皮皮:“剛剛看代碼,里面設定position坐標要通過Transform物件,這個Transform是什么呀?”
7.2 初識Transform類
我:“Transform是Unity中非常非常重要的一個類,所有的GameObject物件都有一個Transform組件,你創建一個空物體的時候,就會看到它就已經自帶了一個Transform組件”

我指著Inspector視圖中的Transform組件,“你猜猜這個Transform組件到底是用來干嘛的?”
皮皮:“我看出來了,它是用來設定坐標、旋轉角度和縮放的,”
我:“沒錯,一個GameObject必然會有一個坐標、旋轉角度和縮放,所以Transform組件是必須的,不過呢,后面Unity官方引入了ECS框架,在ECS框架中,一個物體Entity可以沒有Transform,現在你只需記住,Transform組件可以用來設定游戲物件的坐標、旋轉角度和縮放,”
更深入一些,我們看一下Transform常用的屬性和函式吧,
7.3 Transform的屬性
| 屬性 | 資料型別 | 描述 |
|---|---|---|
| position | Vector3 | 在世界空間中的坐標 |
| localPosition | Vector3 | 相對于父節點的區域坐標,如果沒有父節點,則localPosition等于position |
| eulerAngles | Vector3 | 世界坐標系中的旋轉(歐拉角) |
| localEulerAngles | Vector3 | 相對于父節點的區域旋轉(歐拉角),如果沒有父節點,則localEulerAngles等于eulerAngles |
| rotation | Quaternion | 世界坐標系中的旋轉(四元數) |
| localRotation | Quaternion | 相對于父節點的旋轉(四元數),如果沒有父節點,則localRotation等于rotation |
| right | Vector3 | 區域坐標系的x軸方向向量 |
| up | Vector3 | 區域坐標系的y軸方向向量 |
| forward | Vector3 | 區域坐標系的z軸方向向量 |
| localScale | Vector3 | 相對于父節點的縮放比例 |
| parent | Transform | 父節點的Transform組件 |
| root | Transform | 根節點的Transform組件 |
| childCount | int | 子節點數量 |
| lossyScale | Vector3 | 全域縮放比例(只讀) |
| worldToLocalMatrix | Matrix4x4 | 矩陣變換的點從世界坐標轉為自身坐標(只讀) |
| localToWorldMatrix | Matrix4x4 | 矩陣變換的點從自身坐標轉為世界坐標(只讀) |
皮皮:“這么多我該怎么記呀?”
我:“多寫多練,用多了你就記住啦,我給你示范幾個例子吧,”
7.3.1 設定坐標
設定世界坐標:position
// 設定世界坐標
transform.position = new Vector3(100, 0, 0);
設定區域坐標:localPosition
// 設定區域坐標
transform.localPosition = new Vector3(100, 0, 0);
皮皮:“什么是世界坐標和區域坐標呀?”
我:“世界坐標就是以世界坐標系為參考的坐標,區域坐標(或叫本地坐標)就是以本地坐標系為參考的坐標,三維坐標系由x、y、z個軸組成,我們一般分為左手坐標系和右手坐標系,老皮,你看看Unity采用的是左手坐標系還是右手坐標系呢?”

皮皮舉起了它的爪子,分不清左右,
我:“哈哈哈,我直接告訴你吧,Unity采用的是左手坐標系,不管是世界坐標系還是區域坐標系,它們都是左手坐標系,通過坐標系,我們就可以使用坐標值表示任意一個位置,世界有一個坐標系,游戲物件本身也有一個坐標系,游戲物件可以嵌套形成父子節點關系,當游戲物件有父節點的時候,相對父節點的坐標就是區域坐標localPosition,它相對世界坐標系的坐標就是世界坐標position,當游戲物件沒有父節點的時候,或者可以理解為它此時的父節點就是世界,此時區域坐標localPosition就會等于世界坐標position,”
皮皮指著Inspector視圖問:“那Inspector視圖中的Position到底是世界坐標還是區域坐標呀?”

我:“Inspector視圖中的Position顯示的是區域坐標,如果游戲物件沒有父節點,那么區域坐標就會等于世界坐標,此時Position顯示的既是區域坐標也是世界坐標,”
皮皮:“那Rotation和Scale也是同理嗎?”
我:“是的,你已經學會觸類旁通了呀,不錯不錯,”
7.3.2 設定旋轉角度
設定旋轉角度有兩種方式,一種是歐拉角,一種是四元數,
設定區域歐拉角旋轉:localEulerAngles
// 設定區域歐拉角
transform.localEulerAngles = new Vector3(100, 0, 0);
設定區域四元數旋轉:localRotation
// 設定區域四元數旋轉
transform.localRotation = Quaternion.Euler(new Vector3(100, 0, 0));
皮皮:“為什么要弄兩套旋轉方法呢?”
我:“這里就要理解歐拉角的原理以及它的問題,它的主要問題會引發萬向鎖問題,還有,它做差值運算不合理,為了解決這些問題,人們發明了四元數來表示旋轉,后面我再單獨講講這部分的內容,這里你只需看懂如何通過代碼設定旋轉角度即可,”
7.3.3 設定縮放
設定縮放,為原始的2倍
transform.localScale = Vector3.one * 2;
皮皮:“我看到縮放還有一個叫lossyScale,它與localScale有什么關系呢?”
我:“localScale是本地坐標系中的縮放,lossyScale是世界坐標系中的縮放,類似于localPosition與position的關系,不過lossyScale是只讀的,我們不能對它進行賦值,實際專案中lossyScale比較少用到呢,”
7.3.4 設定朝向
設定物體的x軸與向量(1, 1, 0)朝向一致,
transform.right = new Vector3(1, 1, 0);
設定物體的y軸與世界坐標系的y軸朝向一致,
transform.up = Vector3.up;
設定物體的z軸與主攝像機的z軸反方向,
transform.forward = -Camera.main.transform.forward;
7.3.5 設定父節點
// 創建父游戲物件 parentGo
GameObject parentGo = new GameObject("parentGo");
// 創建子游戲物件 childGo
GameObject childGo = new GameObject("childGo");
// 設定 childGo 的父物件為 parentGo
childGo.transform.parent = parentGo.transform;
注意,parent是一個Transform,不是GameObject哦,
7.4 Transform的函式
| 函式 | 說明 |
|---|---|
| Translate | 用來移動物體的函式 |
| Rotate | 用來旋轉物體的函式 |
| RotateAround | 讓物體以某一點為軸心成圓周運動 |
| LookAt | 讓物體的z軸看向目標物體 |
| TransformDirection | 從本地坐標到世界坐標變換方向 |
| InverseTransformDirection | 從世界坐標到本地坐標變換方向,與TransformDirection相反 |
| TransformPoint | 將基于當前游戲物件的區域坐標轉化為基于世界坐標系的坐標 |
| InverseTransformPoint | 將基于世界坐標系的坐標轉換為基于當前物件的區域坐標 |
| DetachChildren | 分離子物體,所有子物體解除父子關系 |
| Find | 通過名字查找子物體并回傳它 |
| SetParent | 設定父節點 |
| IsChildOf | 判斷自身是否是某個Transform的子節點 |
皮皮:“按照慣例,show me code,”
7.4.1 移動物體
函式原型:
public void Translate(float x, float y, float z);
public void Translate(float x, float y, float z, [DefaultValue("Space.Self")] Space relativeTo);
public void Translate(Vector3 translation);
public void Translate(Vector3 translation, [DefaultValue("Space.Self")] Space relativeTo);
public void Translate(float x, float y, float z, Transform relativeTo);
public void Translate(Vector3 translation, Transform relativeTo);
示例:
向本地坐標系的x軸正方向移動1米
m_selfTrans.Translate(1, 0, 0, Space.Self);
注:Unity中,坐標軸上的1個單位長度的距離表示真實世界中的1米距離
皮皮:“舉手提問,這個Translate移動與直接設定localPosition坐標有什么區別呢?”
我:“設定localPosition是直接設定最終的目標位置,而Translate方法相當于是一個增量操作,是基于當前坐標進行一個增量移動,”
皮皮:“提問,引數Space relativeTo是什么意思呀?”
我:“這個是參考系,Space是一個列舉,很好理解,Space.World是以世界坐標系為參考,Space.Self是以本地坐標系為參考,”
public enum Space
{
World = 0,
Self = 1
}
我:“皮皮,現在你猜猜,下面這個多載函式的第二個引數Transform relativeTo是表示什么?”
public void Translate(Vector3 translation, Transform relativeTo);
皮皮:“不用猜,參考系,以它的坐標系為參考,”
我:“厲害哦,變通能力越來越強了,”
7.4.2 旋轉物體
函式原型:
public void Rotate(float xAngle, float yAngle, float zAngle);
public void Rotate(Vector3 eulers, [DefaultValue("Space.Self")] Space relativeTo);
public void Rotate(Vector3 eulers);
public void Rotate(float xAngle, float yAngle, float zAngle, [DefaultValue("Space.Self")] Space relativeTo);
public void Rotate(Vector3 axis, float angle, [DefaultValue("Space.Self")] Space relativeTo);
public void Rotate(Vector3 axis, float angle);
示例:
圍繞本地坐標系的y軸順時針旋轉1度
transform.Rotate(0, 1, 0);
我:“老皮,考考你,這個Rotate方法與直接設定localEulerAngles有什么區別?”
皮皮:“你這么問,我就知道了,Rotate方法是增量操作,上面講Translate的時候說過,”
我:“看來我不用多解釋啦,聰明聰明,”
皮皮:“我看到還有一個RotateAround方法,它與Rotate有什么區別呢?”
函式原型:
public void RotateAround(Vector3 point, Vector3 axis, float angle);
public void RotateAround(Vector3 axis, float angle);
我:“你知道地球自轉和公轉嗎?”
皮皮:“知道呀,我們古老的喵星也有自轉和公轉,在遙遠的拉姆達星系,喵星圍繞著巨大的魯特恒星旋轉,在大約4000千萬年前… … ”
皮皮突然捂住自己的嘴,“糟了,泄露機密了,”
我:“哈哈哈,不要怕,我不會出賣你們的,喵星人早已經是人類的好朋友了,而且你也沒說你們星系的具體坐標呢,”
皮皮:“打住,回歸正題!”
我:“嘛,這個RotateAround就是類似公轉的效果,”
RotateAround也是一個增量操作,引數point是圍繞的坐標點,引數axis是旋轉的軸,angle是旋轉的角度,
例:
using UnityEngine;
public class TransformTest : MonoBehaviour
{
private Transform m_selfTrans;
void Awake()
{
// 快取transform
m_selfTrans = transform;
}
void Update()
{
// 圍繞中心點Vector3.zero, 繞著軸Vector3.up旋轉,旋轉角度1度
m_selfTrans.RotateAround(Vector3.zero, Vector3.up, 1);
}
}
運行效果:

7.4.3 方向轉換計算
本地方向向量 轉 世界方向向量
函式原型:
public Vector3 TransformDirection(float x, float y, float z);
public Vector3 TransformDirection(Vector3 direction);
示例:
計算本地坐標下的forward向量(即本地坐標系的z軸的正方向向量)在世界坐標系下的向量
Vector3 worldObjForward = transform.TransformDirection(transform.forward);
世界方向向量 轉 本地方向向量
函式原型:
public Vector3 InverseTransformDirection(Vector3 direction);
public Vector3 InverseTransformDirection(float x, float y, float z);
示例:
計算世界坐標系下的forward向量在本地坐標系下的向量
Vector3 localForward = transform.InverseTransformDirection(Vector3.forward);
7.4.4 坐標轉換計算
本地坐標 轉 世界坐標
函式原型:
public Vector3 TransformPoint(float x, float y, float z);
public Vector3 TransformPoint(Vector3 position);
示例:
計算區域坐標(10, 0, 0)在世界坐標系下的坐標
Vector3 worldPos = transform.TransformPoint(10, 0, 0);
世界坐標 轉 本地坐標
函式原型:
public Vector3 InverseTransformPoint(float x, float y, float z);
public Vector3 InverseTransformPoint(Vector3 position);
示例:
計算世界坐標系下的(10, 0, 0)在本地坐標系下的坐標
Vector3 localPos = transform.InverseTransformPoint(10, 0, 0);
7.4.5 查找子物體
函式原型:
public Transform Find(string n);
示例:
創建節點,節點結構如下

root節點掛TransformTest腳本,腳本代碼如下
using UnityEngine;
public class TransformTest : MonoBehaviour
{
Transform m_selfTrans;
void Awake()
{
// 快取自身的transform
m_selfTrans = transform;
}
void Start()
{
// 查找a節點
var a = m_selfTrans.Find("a");
// 查找c節點
var c = m_selfTrans.Find("a/b/c");
//查找e節點
var d = m_selfTrans.Find("d");
}
}
7.4.6 判斷是否是子節點
函式原型:
public bool IsChildOf([NotNull] Transform parent);
示例:
GameObject a = new GameObject("a");
GameObject b = new GameObject("b");
b.transform.parent = a.transform;
// 設定父節點也可以用SetParent方法
// b.transform.SetParent(a.transform, false);
if(b.transform.IsChildOf(a.transform))
{
Debug.Log("a 是 b 的子節點");
}
《學Unity的貓》——第八章:Unity預設檔案,無限紙團噴射機
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/227864.html
標籤:其他
