Unity3D數學第一篇:向量與點、線、面(基礎篇)
Unity3D數學第二篇:旋轉與歐拉角、四元數(核心變換篇)
Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)
Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)
Unity3D數學第五篇:幾何計算與常用算法(實用算法篇)
第三篇:坐標系與變換矩陣(空間轉換篇)
在 3D 游戲開發中,我們不斷地在不同的“參考系”或“視角”之間切換。一個物體的“前方”可能相對于它自身,也可能相對于整個世界;鼠標點擊的屏幕位置,最終要對應到 3D 世界中的某個點。這些轉換的背后,離不開坐標系 (Coordinate Systems) 和 變換矩陣 (Transformation Matrices) 的強大作用。
本篇教程將深入探討 Unity 中各種常見的坐標系,揭示它們之間的轉換機制,并為你揭開變換矩陣這一“幕后英雄”的面紗。理解這些概念,將讓你對 3D 空間中的一切變換擁有更深刻的洞察力。
1. 深入理解坐標系:3D 空間的“視角”
想象一個房間,你可以在房間的中心描述一件家具的位置(例如,“它在我前面兩米”),也可以相對于房間的墻角來描述(例如,“它在東墻角往北三米,往西兩米”)。這兩種描述方式,分別對應著不同的坐標系。
在 3D 游戲世界中,存在多種相互關聯的坐標系,它們服務于不同的目的。
1.1 局部坐標系 (Local Space / Object Space)
-
概念: 每個獨立的 3D 物體都有其自身的局部坐標系。這個坐標系的原點通常位于物體的中心點(或稱為軸心點 Pivot),而它的 X、Y、Z 軸則沿著物體自身的特定方向。
- 例如,一個汽車模型,它的局部 Y 軸通常指向它的“上方”(車頂),局部 Z 軸指向它的“前方”(車頭),局部 X 軸指向它的“右方”。
-
特性: 當物體自身旋轉或移動時,它的局部坐標系也跟著它一起旋轉和移動。因此,一個點在物體局部坐標系中的坐標是固定不變的。
-
Unity 中的體現:
-
Transform.localPosition
:表示物體相對于其父對象的局部位置。如果物體沒有父對象,則等同于transform.position
。 -
Transform.localRotation
:表示物體相對于其父對象的局部旋轉。 -
Transform.localScale
:表示物體相對于其父對象的局部縮放。 -
transform.forward
、transform.up
、transform.right
:這些便捷屬性返回的是物體在世界坐標系下其局部 Z、Y、X 軸的方向向量。例如,transform.forward
就是物體當前面向的世界方向。
-
1.2 世界坐標系 (World Space)
-
概念: 整個 3D 場景的全局、固定參考系。它的原點通常位于
(0, 0, 0)
,X、Y、Z 軸指向固定方向。在 Unity 中,通常:-
X 軸: 指向右方 (Red Axis)
-
Y 軸: 指向上方 (Green Axis)
-
Z 軸: 指向前方 (Blue Axis)
-
-
特性: 世界坐標系是所有物體共享的“大地圖”。所有物體最終都會被放置在世界坐標系中的某個位置和姿態。
-
Unity 中的體現:
-
Transform.position
:表示物體在世界坐標系中的位置。 -
Transform.rotation
:表示物體在世界坐標系中的旋轉。 -
Transform.lossyScale
:表示物體在世界坐標系中的最終縮放(包含了父級的縮放影響)。
-
1.3 父子關系與層級變換
在 Unity 中,游戲對象可以有父子關系。當一個對象成為另一個對象的子對象時,它的局部坐標系就變成了相對于其父對象的坐標系。子對象的任何變換都會受到父對象變換的影響。
-
示例: 如果一個手臂是身體的子對象,當身體旋轉時,手臂也會跟著旋轉(因為手臂的局部坐標系隨著身體的世界坐標系一起旋轉了)。而手臂自身的局部旋轉,則是相對于它當前所處的“父級空間”進行的。
-
Transform.SetParent()
方法可以用來建立和解除父子關系,這在運行時動態組合對象時非常有用,例如拾取武器或附件。
為什么要區分局部坐標系和世界坐標系?
理解這些坐標系的關鍵在于它們處理問題的方式:
-
局部坐標系方便描述物體自身的屬性(例如,我向前走,就是沿著我的局部 Z 軸),因為它不受外部環境變化的影響。
-
世界坐標系方便描述物體在整個場景中的絕對位置和關系(例如,哪個物體離世界原點最近),因為它是一個統一的參考標準。
在游戲開發中,我們經常需要在兩者之間進行轉換。
1.4 屏幕坐標系 (Screen Space)
-
概念: 你的游戲屏幕(或窗口)上的 2D 像素坐標系。
-
原點通常在屏幕的左下角
(0, 0)
。 -
X 軸向右延伸,Y 軸向上延伸。
-
Z 軸通常表示深度,即點離攝像機的距離。
-
-
特性: 像素單位,直接對應屏幕上的視覺呈現。
-
Unity 中的體現:
-
Input.mousePosition
:鼠標在屏幕上的像素坐標。 -
Input.GetTouch(0).position
:觸控點在屏幕上的像素坐標。 -
UI Canvas 的
Screen Space - Overlay
模式下的 UI 元素位置。
-
1.5 視口坐標系 (Viewport Space)
-
概念: 相機在渲染時看到的**“視口”**的 2D 歸一化坐標系。
-
原點在視口的左下角
(0, 0)
。 -
右上角是
(1, 1)
。 -
X、Y 值范圍是
0.0
到1.0
。 -
Z 軸同樣表示深度,即點離攝像機的距離。
-
-
特性: 獨立于屏幕分辨率。無論屏幕多大,視口左下角始終是
(0,0)
,右上角始終是(1,1)
。這對于判斷物體是否在屏幕內非常有用。 -
Unity 中的體現:
-
Camera.WorldToViewportPoint()
:世界坐標轉視口坐標。 -
Camera.ViewportToWorldPoint()
:視口坐標轉世界坐標。 -
Camera.ViewportToScreenPoint()
:視口坐標轉屏幕坐標。
-
1.6 其他重要但無需深究的坐標系
-
攝像機空間 (Camera Space / View Space): 以攝像機自身為原點和軸向的 3D 坐標系。所有物體在渲染前都會被轉換到這個空間。
-
裁剪空間 (Clip Space): 在攝像機空間之后,點被投影到一個立方體(歸一化設備坐標,NDC)中。這個空間用于剔除視錐體外的點。
-
歸一化設備坐標 (Normalized Device Coordinates - NDC): 經過裁剪空間后,所有可見點被映射到一個標準的立方體中,X, Y, Z 軸范圍都是
[-1, 1]
。
這些更深層次的坐標系主要在圖形渲染管線內部使用,作為開發者,通常只需要理解它們的存在和作用流程,而無需手動操作它們。了解它們能讓你更好地理解 3D 世界最終如何映射到 2D 屏幕上。
2. 坐標系轉換:在不同“視角”之間穿梭
在游戲開發中,我們經常需要在不同坐標系之間進行點的轉換,例如將一個世界坐標的點轉換為局部坐標,或者將鼠標的屏幕坐標轉換為 3D 世界中的點擊點。Unity 提供了方便的 API 來實現這些轉換。
2.1 局部坐標系與世界坐標系之間的轉換
這些方法都可以在任何 Transform
組件上調用。
-
Transform.TransformPoint(Vector3 position)
:局部點轉世界點-
將一個在當前
Transform
的局部坐標系中定義的點 (position
) 轉換到世界坐標系中。 -
應用: 計算角色前方 N 米處的世界坐標、計算子彈從槍口(作為局部點)射出的世界起始位置。
C#
// Unity // 假設你在物體 A 上,想知道物體 A 的局部坐標 (0, 0, 5) 在世界中的位置 Vector3 localPoint = new Vector3(0, 0, 5); // 物體 A 前方 5 米處 Vector3 worldPoint = transform.TransformPoint(localPoint); Debug.Log($"局部點 {localPoint} 在世界中是 {worldPoint}");
-
-
Transform.InverseTransformPoint(Vector3 position)
:世界點轉局部點-
將一個在世界坐標系中定義的點 (
position
) 轉換到當前Transform
的局部坐標系中。 -
應用: 判斷一個世界中的點相對于物體的具體方位(例如,敵人相對于我在哪里)、計算局部碰撞點。
C#
// Unity // 假設你想知道世界坐標 (10, 0, 0) 相對于當前物體的位置 Vector3 worldPoint = new Vector3(10, 0, 0); Vector3 localPoint = transform.InverseTransformPoint(worldPoint); Debug.Log($"世界點 {worldPoint} 相對于我是在 {localPoint}");
-
-
Transform.TransformDirection(Vector3 direction)
:局部方向轉世界方向-
將一個在當前
Transform
的局部坐標系中定義的方向向量 (direction
) 轉換到世界坐標系中。 -
重要: 這個方法只處理旋轉,不考慮平移和縮放。它將局部方向向量的方向轉換到世界空間,但不會改變其長度。
-
應用: 獲取物體當前的世界前方 (
transform.TransformDirection(Vector3.forward)
等同于transform.forward
),獲取物體自身某個方向在世界中的表示。
C#
// Unity // 獲取當前物體局部右方在世界中的方向 Vector3 localRight = Vector3.right; Vector3 worldRight = transform.TransformDirection(localRight); Debug.Log($"我的局部右方在世界中是 {worldRight}");
-
-
Transform.InverseTransformDirection(Vector3 direction)
:世界方向轉局部方向-
將一個在世界坐標系中定義的向量 (
direction
) 轉換到當前Transform
的局部坐標系中。 -
同樣,只處理旋轉,不考慮平移和縮放。
-
應用: 判斷世界中的某個力或方向向量對于物體自身來說是哪個方向(例如,世界重力對于斜坡上的角色來說是哪個局部方向)。
C#
// Unity // 假設你想知道世界坐標的 Vector3.up (世界向上) 對于當前物體是哪個局部方向 Vector3 worldUp = Vector3.up; Vector3 localUp = transform.InverseTransformDirection(worldUp); Debug.Log($"世界向上對于我來說是局部 {localUp}");
-
-
Transform.TransformVector(Vector3 vector)
和Transform.InverseTransformVector(Vector3 vector)
:- 這兩個方法與
TransformDirection
類似,但它們會考慮縮放。當處理法線(需要逆轉置矩陣)或非單位長度的向量時,需要特別注意。在大多數情況下,處理方向用TransformDirection
即可,除非你明確需要考慮縮放對向量的影響。
- 這兩個方法與
2.2 世界坐標系與屏幕/視口坐標系之間的轉換
這些方法通常在 Camera
組件上調用。
-
Camera.WorldToScreenPoint(Vector3 position)
:世界點轉屏幕點-
將 3D 世界坐標系中的一個點 (
position
) 轉換到 2D 屏幕坐標系中。返回的Vector3
的x
和y
是像素坐標,z
是該點距離攝像機的深度。 -
應用:
-
將 3D 物體在屏幕上方的位置顯示一個血條 UI。
-
判斷一個 3D 物體是否在屏幕內(檢查
x
,y
是否在0
到Screen.width
/Screen.height
之間,且z > 0
)。
-
C#
// Unity public Transform target3DObject; void Update() {Vector3 screenPos = Camera.main.WorldToScreenPoint(target3DObject.position);Debug.Log($"3D 物體 {target3DObject.name} 在屏幕上的位置是 {screenPos}");// 判斷是否在屏幕內:if (screenPos.z > 0 && screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height) {Debug.Log("物體在屏幕內!");} else {Debug.Log("物體在屏幕外!");} }
-
-
Camera.ScreenToWorldPoint(Vector3 position)
:屏幕點轉世界點-
將 2D 屏幕坐標系中的一個點 (
position
) 轉換到 3D 世界坐標系中。 -
關鍵: 由于 2D 屏幕點缺少深度信息,你必須手動為
position.z
賦值,才能確定它在 3D 空間中的深度。 -
應用:
-
點擊屏幕獲取 3D 世界中的一個點(例如,點擊地面進行移動)。
-
將 UI 元素的位置映射到 3D 空間中的特定深度。
-
C#
// Unity // 鼠標點擊屏幕,獲取點擊處在世界中的位置 void Update() {if (Input.GetMouseButtonDown(0)) {Vector3 mouseScreenPos = Input.mousePosition;// 必須提供 Z 深度信息!這里假設我們想在相機前方 10 米處獲取世界點mouseScreenPos.z = 10f; // 這里的 z 是指離相機近裁剪面的距離Vector3 worldClickPoint = Camera.main.ScreenToWorldPoint(mouseScreenPos);Debug.Log($"鼠標點擊屏幕 {Input.mousePosition} 對應世界點 {worldClickPoint}");} }
注意: 如果你需要精確點擊 3D 物體表面,通常會結合射線檢測 (Raycasting) 來獲取深度,這比固定
z
值更精確。我們會在第四篇詳細講解。 -
-
Camera.WorldToViewportPoint(Vector3 position)
:世界點轉視口點-
將 3D 世界坐標系中的一個點 (
position
) 轉換到 2D 視口坐標系中。返回的Vector3
的x
和y
范圍是0.0
到1.0
,z
是深度。 -
應用:
-
判斷物體是否在屏幕內(通常比
WorldToScreenPoint
更方便,因為它不依賴Screen.width/height
)。 -
創建位于屏幕相對位置的 UI 元素(例如,血條總在敵人上方,但不能超出屏幕)。
-
C#
// Unity public Transform targetObject; void Update() {Vector3 viewportPos = Camera.main.WorldToViewportPoint(targetObject.position);if (viewportPos.z > 0 && viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1) {Debug.Log("物體在視口內!");} else {Debug.Log("物體在視口外!");} }
-
-
Camera.ViewportToWorldPoint(Vector3 position)
:視口點轉世界點-
將 2D 視口坐標系中的一個點 (
position
) 轉換到 3D 世界坐標系中。 -
同樣需要手動提供
position.z
深度。 -
應用: 將一個位于屏幕中央(
0.5, 0.5
)的 UI 元素映射到 3D 世界中的某個深度。
-
3. 變換矩陣:空間轉換的幕后英雄
在所有的坐標系轉換背后,真正執行“數學魔法”的是變換矩陣 (Transformation Matrices)。雖然 Unity 為我們封裝了大多數底層矩陣操作,但理解它們的存在和作用原理,能讓你更深刻地理解 3D 圖形學,并在遇到復雜問題時游刃有余。
3.1 什么是變換矩陣?
-
概念: 在 3D 圖形中,一個矩陣 (Matrix) 是一個矩形排列的數字集合。一個 4x4 的矩陣可以同時表示 3D 空間中的平移 (Translation)、旋轉 (Rotation) 和縮放 (Scaling) 這三種基本變換。
-
矩陣乘法: 當一個 3D 點(表示為齊次坐標下的向量)與一個變換矩陣相乘時,就可以得到變換后的新點。
- 例如,
NewPoint = Matrix * OldPoint
。
- 例如,
-
關鍵特性:不滿足交換律!
MatrixA * MatrixB
不等于MatrixB * MatrixA
。這意味著變換的順序至關重要。先旋轉再平移,和先平移再旋轉,會得到完全不同的結果。這解釋了為什么 Unity 中Transform
組件的position
和rotation
操作是分開的,以及為什么父子變換的順序會影響最終結果。
3.2 矩陣如何工作?(簡要原理)
一個 4x4 的變換矩陣通常結構如下:
| Sx 0 0 Tx |
| 0 Sy 0 Ty |
| 0 0 Sz Tz |
| 0 0 0 1 |
-
其中
Sx, Sy, Sz
控制縮放。 -
矩陣左上角的 3x3 部分(包含 0 和對角線上的值)控制旋轉。
-
Tx, Ty, Tz
控制平移。
當一個 3D 點 (x, y, z, 1)
(這里的 1
是齊次坐標的約定)與這個矩陣相乘時,就能同時實現平移、旋轉和縮放。
3.3 MVP 矩陣:3D 世界到 2D 屏幕的渲染管線
在 3D 渲染管線中,一個 3D 模型從其自身定義到最終呈現在屏幕上,會經歷一系列的坐標系轉換,這些轉換都由特定的變換矩陣完成。這就是著名的 MVP 矩陣。
-
模型矩陣 (Model Matrix / World Matrix):局部空間 -> 世界空間
-
作用: 將 3D 模型在它自己的局部坐標系中定義的頂點,轉換到世界坐標系中。
-
每個物體都有一個模型矩陣,它由該物體的
Transform.position
、Transform.rotation
和Transform.localScale
決定。 -
例如,一個角色模型,它的模型矩陣決定了它在世界中的位置、方向和大小。
-
-
視圖矩陣 (View Matrix):世界空間 -> 攝像機空間
-
作用: 將世界坐標系中的所有點,轉換到攝像機(或觀察者)的局部坐標系中。這相當于從攝像機的角度“看”世界。
-
視圖矩陣實際上是攝像機自身變換矩陣的逆矩陣。
-
它模擬了攝像機的位置和朝向。
-
-
投影矩陣 (Projection Matrix):攝像機空間 -> 裁剪空間/NDC 空間
-
作用: 將攝像機空間中的 3D 點,投影到 2D 屏幕上,并進行深度擠壓和透視變換。
-
它定義了攝像機的視野 (Field of View - FOV)、近裁剪面 (Near Clip Plane) 和遠裁剪面 (Far Clip Plane)。
-
兩種主要類型:
-
透視投影 (Perspective Projection): 模擬人眼看世界的效果,遠的物體小,近的物體大,有透視感。
-
正交投影 (Orthographic Projection): 沒有透視感,所有物體大小不變,常用于 2D 游戲、工程視圖或特殊的 3D 效果。
-
-
投影矩陣的輸出通常是裁剪空間,然后通過除以 W 分量(透視除法)進入歸一化設備坐標 (NDC) 空間,X、Y、Z 軸范圍都是
[-1, 1]
。
-
渲染管線中的流程:
頂點數據 (局部坐標) -> 模型矩陣 -> 世界坐標 -> 視圖矩陣 -> 攝像機空間 -> 投影矩陣 -> 裁剪空間 -> 透視除法 -> NDC 空間 -> 視口變換 -> 屏幕坐標 -> 最終渲染
為什么要理解 MVP 矩陣?
雖然 Unity 幫你處理了所有這些復雜的矩陣乘法,但了解 MVP 矩陣的工作原理,能讓你:
-
理解 3D 渲染的底層邏輯: 當你在 Shader 中處理頂點或像素時,你是在哪個坐標系下操作?這直接影響你的計算結果。
-
調試渲染問題: 當物體顯示不正確時,可能是哪個矩陣出了問題?
-
實現自定義渲染效果: 如果你需要編寫自定義的渲染管道或著色器,對 MVP 矩陣的理解是必不可少的。
4. 坐標系與變換矩陣在 Unity 中的高級應用
理解坐標系和矩陣的工作方式后,我們可以解決一些實際的游戲開發問題。
4.1 鼠標點擊精確拾取 3D 物體(結合射線)
這是最常見的應用之一。鼠標點擊的 2D 屏幕點,如何找到它在 3D 世界中擊中的物體?
C#
// Unity
void Update() {if (Input.GetMouseButtonDown(0)) {// 1. 將鼠標的屏幕坐標轉換為一條射線// Camera.main.ScreenPointToRay() 會根據相機位置和方向,從屏幕點發射一條射線Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);// 2. 聲明一個 RaycastHit 結構體來存儲射線檢測結果RaycastHit hit;// 3. 執行射線檢測// Physics.Raycast 會沿著射線方向檢測碰撞體if (Physics.Raycast(ray, out hit, 100f)) { // 100f 是射線的最大檢測距離Debug.Log($"鼠標點擊了物體: {hit.collider.gameObject.name},在世界坐標: {hit.point}");// 你可以對 hit.collider.gameObject 進行操作,例如選中、高亮} else {Debug.Log("沒有點擊到任何 3D 物體。");}}
}
這里的核心就是將 2D 的屏幕坐標,通過攝像機的投影信息反推回 3D 世界中的一條射線。
4.2 UI 元素跟隨 3D 物體(血條、名稱板)
讓一個 UI 元素(例如角色的血條或名稱板)始終位于其對應的 3D 角色上方。
C#
// Unity
public Transform target3DCharacter; // 要跟隨的 3D 角色
public RectTransform uiElementRectTransform; // 要跟隨的 UI 元素的 RectTransformvoid Update() {if (target3DCharacter == null || uiElementRectTransform == null || Camera.main == null) return;// 1. 將 3D 角色頭頂的世界坐標轉換為屏幕坐標// 假設血條在角色頭頂上方 2 個單位處Vector3 worldPositionAboveCharacter = target3DCharacter.position + Vector3.up * 2f;Vector3 screenPosition = Camera.main.WorldToScreenPoint(worldPositionAboveCharacter);// 2. 判斷物體是否在攝像機前方且在屏幕內(防止血條顯示在屏幕外或背后)if (screenPosition.z > 0 &&screenPosition.x >= 0 && screenPosition.x <= Screen.width &&screenPosition.y >= 0 && screenPosition.y <= Screen.height) {uiElementRectTransform.gameObject.SetActive(true); // 顯示 UI 元素// 3. 將屏幕坐標轉換為 UI Canvas 的 RectTransform 坐標// UI 坐標通常是相對于 Canvas 的,需要特殊處理Vector2 localUIPos;RectTransformUtility.ScreenPointToLocalPointInRectangle(uiElementRectTransform.parent as RectTransform, // UI 元素的父級 Canvas RectTransformscreenPosition,Camera.main.WorldToScreenPoint(Camera.main.transform.position).z, // 假設 Z 深度相同out localUIPos);// 如果 Canvas 是 Screen Space - Camera 模式,第三個參數是 Camera.main// 如果 Canvas 是 Screen Space - Overlay 模式,第三個參數是 null// 鑒于目前是 Screen Space - Overlay,直接使用 screenPosition 即可// 或者更簡單的,直接賦值,Unity UI 系統會處理好:uiElementRectTransform.position = screenPosition;} else {uiElementRectTransform.gameObject.SetActive(false); // 隱藏 UI 元素}
}
注意: 對于 Unity UI,RectTransformUtility.ScreenPointToLocalPointInRectangle
通常用于將屏幕點轉換為 Canvas 內部的局部坐標,這取決于你的 Canvas 渲染模式(Screen Space - Overlay
、Screen Space - Camera
或 World Space
)。這里為了簡化,如果 Canvas 是 Screen Space - Overlay
模式,直接將 uiElementRectTransform.position = screenPosition;
即可,Unity 會處理其與屏幕坐標的映射。
4.3 相機邊界限制
限制相機只能在特定區域內移動,或者當玩家靠近屏幕邊緣時,相機自動平移。
C#
// Unity
public Transform player;
public float edgeOffset = 0.1f; // 屏幕邊緣觸發區域 (0-1 范圍)
public float cameraMoveSpeed = 5f;void Update() {// 1. 將玩家位置轉換為視口坐標Vector3 viewportPos = Camera.main.WorldToViewportPoint(player.position);Vector3 cameraMoveDirection = Vector3.zero;// 2. 檢查玩家是否接近視口邊緣if (viewportPos.x < edgeOffset) {cameraMoveDirection += Vector3.left;} else if (viewportPos.x > 1f - edgeOffset) {cameraMoveDirection += Vector3.right;}if (viewportPos.y < edgeOffset) {cameraMoveDirection += Vector3.down;} else if (viewportPos.y > 1f - edgeOffset) {cameraMoveDirection += Vector3.up;}// 3. 移動相機Camera.main.transform.position += cameraMoveDirection.normalized * cameraMoveSpeed * Time.deltaTime;
}
這里利用視口坐標系的歸一化特性,方便地判斷玩家相對于屏幕邊緣的位置。
5. 常見面試題與深入思考
5.1 面試問答
-
問:請解釋局部坐標系和世界坐標系的區別。在 Unity 中,當物體存在父子關系時,
transform.position
和transform.localPosition
有何不同?-
解析:
-
局部坐標系: 物體自身的坐標系,原點在物體軸心,軸向與物體自身方向一致。描述物體自身的幾何數據。
-
世界坐標系: 整個場景的全局、固定坐標系,原點在
(0,0,0)
,軸向固定。描述物體在整個世界中的絕對位置。 -
transform.position
: 表示物體在世界坐標系中的絕對位置。 -
transform.localPosition
: 表示物體在父對象局部坐標系中的相對位置。如果物體沒有父對象,則localPosition
等同于position
。當父對象移動或旋轉時,子對象的localPosition
不變,但其position
會隨父對象而改變。
-
-
-
問:如何在 Unity 中將一個世界坐標的點轉換到某個物體的局部坐標系?請舉例說明應用場景。
-
考察點:
InverseTransformPoint
的使用及其應用。 -
解析: 使用
transform.InverseTransformPoint(worldPoint)
方法。-
應用場景:
-
判斷一個世界中的目標點相對于當前物體的方位。例如,一個 AI 想要知道玩家在它自身的左側還是右側、前方還是后方,就需要將玩家的世界位置轉換為 AI 的局部坐標,然后判斷 X、Z 分量的正負。
-
獲取一個物體被擊中時,局部碰撞點在哪里,這在制作特效(例如粒子效果從特定部位發出)或計算局部傷害時很有用。
-
-
-
-
問:請簡要描述 MVP 矩陣在 3D 渲染管線中的作用。它們分別負責什么轉換?
-
考察點: 對 3D 圖形渲染基礎流程的理解,矩陣的抽象作用。
-
解析:
-
MVP 矩陣是 3D 世界中的點最終如何被渲染到 2D 屏幕上的關鍵。
-
模型矩陣 (Model Matrix): 將模型自身的局部坐標轉換為世界坐標。它決定了物體在世界中的位置、旋轉和縮放。
-
視圖矩陣 (View Matrix): 將世界坐標轉換為攝像機(觀察者)的局部坐標。它模擬了攝像機的位置和朝向。
-
投影矩陣 (Projection Matrix): 將攝像機空間中的 3D 點投影到 2D 屏幕(歸一化設備坐標),并進行透視變換和裁剪。它定義了攝像機的視野 (FOV)、近遠裁剪面,決定了最終畫面的透視感或正交感。
-
-
總結: 模型矩陣定位物體;視圖矩陣選擇觀察視角;投影矩陣定義如何將 3D 世界映射到 2D 屏幕。
-
-
問:如何在 Unity 中讓一個 UI 元素(例如血條)始終跟隨一個 3D 角色顯示在它的頭頂?你會用到哪些坐標系轉換?
-
考察點: 綜合運用
WorldToScreenPoint
和 UI 坐標系轉換的理解。 -
解析:
-
首先,獲取 3D 角色頭頂的世界坐標。
-
使用
Camera.main.WorldToScreenPoint()
將這個世界坐標轉換為屏幕坐標(像素坐標)。 -
然后,將這個屏幕坐標賦值給 UI 元素的
RectTransform.position
。 -
額外考慮: 需要判斷
WorldToScreenPoint
返回的z
值是否大于 0(在攝像機前方),以及x, y
是否在屏幕范圍內,以決定是否顯示 UI 元素,避免顯示在屏幕外或角色背后。對于不同 Canvas 渲染模式下的 UI 定位,可能還需要額外的RectTransformUtility
轉換。
-
-
總結與展望
本篇教程深入探討了 3D 游戲開發中至關重要的坐標系和變換矩陣:
-
我們詳細區分了局部坐標系、世界坐標系、屏幕坐標系和視口坐標系,理解了它們各自的用途。
-
掌握了 Unity 中局部與世界、世界與屏幕/視口之間進行轉換的各種 API,讓你能夠靈活處理位置和方向的轉換。
-
揭示了變換矩陣的底層原理,包括其表示平移、旋轉、縮放的能力,以及不滿足交換律的重要性。
-
深入理解了 MVP 矩陣 (模型、視圖、投影矩陣) 在 3D 渲染管線中的核心作用,讓你對 3D 畫面如何呈現在屏幕上有了更清晰的認識。
對坐標系和變換的理解,是你在 Unity 中構建復雜層級結構、實現相機邏輯、處理 UI 交互以及深入圖形渲染的基石。
在下一篇《射線與碰撞檢測(交互基礎篇)》中,我們將結合你已經掌握的向量、旋轉、坐標系知識,深入學習如何在 3D 世界中進行精確的交互檢測,例如鼠標點擊拾取、子彈命中判斷以及各種物理碰撞。
現在,你對坐標系和變換矩陣的概念是否已經更加清晰了呢?在 Unity 開發中,你是否曾遇到過因坐標系混淆而導致的問題?
Unity3D數學第一篇:向量與點、線、面(基礎篇)
Unity3D數學第二篇:旋轉與歐拉角、四元數(核心變換篇)
Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)
Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)
Unity3D數學第五篇:幾何計算與常用算法(實用算法篇)