Unity3D數學第一篇:向量與點、線、面(基礎篇)
Unity3D數學第二篇:旋轉與歐拉角、四元數(核心變換篇)
Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)
Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)
Unity3D數學第五篇:幾何計算與常用算法(實用算法篇)
在前幾篇中,我們學習了如何用向量表示位移和方向,如何用四元數處理旋轉,以及如何在不同坐標系之間進行轉換。現在,我們將這些知識融會貫通,來解決 3D 游戲中最常見的交互問題:物體之間是否接觸?在哪里接觸? 這就是射線檢測 (Raycasting) 和碰撞檢測 (Collision Detection) 的核心任務。
1. 射線的概念與作用:精確的“探針”
在 3D 空間中,一條射線 (Ray) 是一個具有起點 (Origin) 和方向 (Direction) 的無限長直線。你可以把它想象成一束激光,從一個點筆直地射向某個方向。
Unity 中使用 UnityEngine.Ray
結構體來表示射線:
C#
// Unity
// 定義一條從世界坐標 (0, 0, 0) 向上發射的射線
Ray myRay = new Ray(Vector3.zero, Vector3.up);// 定義一條從當前物體位置向前發射的射線
Ray forwardRay = new Ray(transform.position, transform.forward);
1.1 為什么需要射線?
在游戲開發中,射線是進行精確、點對點檢測的利器,尤其適用于以下場景:
-
鼠標/觸控拾取: 點擊屏幕上的 3D 物體。
-
射擊游戲: 判斷子彈是否命中敵人或障礙物。
-
AI 視線/障礙物檢測: AI 角色判斷前方是否有障礙物或敵人。
-
交互高亮: 當玩家準星對準某個可互動物體時,高亮顯示。
-
地形檢測: 角色在空中時,檢測下方是否有地面。
-
物理模擬輔助: 在自定義物理行為中,判斷某個方向上是否有阻礙。
1.2 Physics.Raycast()
:發射射線進行檢測
Unity 的 Physics
類提供了多種射線檢測方法,其中最常用的是 Physics.Raycast()
。
C#
// Unity
// Physics.Raycast 的最常用重載
public static bool Raycast(Ray ray, // 要發射的射線out RaycastHit hitInfo, // 輸出參數,用于存儲碰撞信息float maxDistance = Mathf.Infinity, // 射線的最大檢測距離int layerMask = DefaultRaycastLayers // 要檢測的物理層
);
-
返回值
bool
: 如果射線擊中任何碰撞體,返回true
;否則返回false
。 -
Ray ray
: 要發射的射線實例,包含起點和方向。 -
out RaycastHit hitInfo
: 這是一個輸出參數,如果射線擊中物體,所有關于碰撞的詳細信息都會填充到這個RaycastHit
結構體中。 -
maxDistance
: 射線的最大檢測距離。只檢測這個距離范圍內的碰撞。默認為無限遠 (Mathf.Infinity
)。 -
layerMask
: 一個位掩碼 (bitmask),用于指定射線只檢測哪些物理層 (Layer) 上的物體。這是非常重要的性能優化和邏輯控制手段。
1.3 RaycastHit
結構體:碰撞的詳細信息
當 Physics.Raycast()
返回 true
時,RaycastHit
結構體中包含了以下有用的信息:
-
hitInfo.collider
:被射線擊中的 Collider 組件。 -
hitInfo.transform
:被射線擊中的 GameObject 的 Transform 組件。 -
hitInfo.gameObject
:被射線擊中的 GameObject。 -
hitInfo.point
:射線與碰撞體相交的世界坐標點。 -
hitInfo.normal
:碰撞點處被擊中表面的法線向量(一個單位向量,垂直于表面,指向外)。 -
hitInfo.distance
:從射線起點到碰撞點的距離。
示例:鼠標點擊拾取物體
C#
// Unity
void Update() {// 檢查鼠標左鍵是否按下if (Input.GetMouseButtonDown(0)) {// 1. 從攝像機向鼠標位置發射一條射線// Camera.main.ScreenPointToRay() 內部處理了屏幕坐標到世界射線的轉換Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit; // 用于存儲碰撞信息// 2. 發射射線,檢測最遠 100 米范圍內的所有碰撞體if (Physics.Raycast(ray, out hit, 100f)) {// 射線擊中了物體Debug.Log("你點擊了: " + hit.collider.gameObject.name);Debug.Log("點擊點世界坐標: " + hit.point);Debug.Log("擊中表面的法線: " + hit.normal);// 示例:給被點擊的物體一個力Rigidbody hitRb = hit.collider.GetComponent<Rigidbody>();if (hitRb != null) {// 沿著擊中點的法線方向施加一個力hitRb.AddForce(hit.normal * 100f, ForceMode.Impulse);}} else {// 沒有擊中任何物體Debug.Log("你點擊了空地。");}}
}
1.4 Physics.RaycastAll()
:獲取所有碰撞
如果一條射線可能穿透多個物體(例如,穿透性子彈),或者你需要檢測射線路徑上所有的物體,可以使用 Physics.RaycastAll()
。它會返回一個 RaycastHit
數組。
C#
// Unity
// 發射一條射線,獲取所有碰撞信息
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit[] hits = Physics.RaycastAll(ray, 10f); // 檢測 10 米內所有碰撞// 遍歷所有碰撞結果
foreach (RaycastHit hit in hits) {Debug.Log("命中物體: " + hit.collider.gameObject.name + ",距離: " + hit.distance);
}
// 注意:RaycastAll 返回的結果是按距離從近到遠排序的。
1.5 物理層 (LayerMask) 的重要性
layerMask
參數是進行射線檢測時非常重要的優化手段。它允許你只檢測特定層級上的物體。
-
設置層級: 在 Unity Editor 中,選擇一個 GameObject,在 Inspector 面板的頂部
Layer
下拉菜單中選擇或添加層級。 -
創建 LayerMask:
C#
// Unity int layerToDetect = LayerMask.GetMask("Ground", "Enemy"); // 只檢測 "Ground" 和 "Enemy" 層 // 或者通過位運算 int groundLayer = LayerMask.NameToLayer("Ground"); int enemyLayer = LayerMask.NameToLayer("Enemy"); int combinedLayerMask = (1 << groundLayer) | (1 << enemyLayer); // 將兩個層的位進行或運算// 發射射線時使用 LayerMask if (Physics.Raycast(ray, out hit, 100f, layerToDetect)) {// ... }
-
忽略特定層級:
C#
// 檢測除了 "Player" 層以外的所有層 int ignorePlayerLayer = ~LayerMask.GetMask("Player"); if (Physics.Raycast(ray, out hit, 100f, ignorePlayerLayer)) {// ... }
-
性能考量: 合理使用
layerMask
可以極大地減少射線檢測的計算量,因為它避免了對不相關層級上的所有碰撞體進行不必要的幾何測試。這是優化復雜場景性能的關鍵一環。
2. 碰撞檢測器 (Colliders):3D 物體的“形狀”
射線檢測是“點-線”與“面”的碰撞。而更廣義的碰撞檢測,則是兩個或多個碰撞體 (Collider) 之間的相互接觸。
Unity 的碰撞檢測系統依賴于添加到 GameObject 上的 Collider 組件。這些組件定義了物體的物理形狀,即使物體的網格模型非常復雜,碰撞體通常是簡化的幾何形狀,以提高性能。
2.1 常見碰撞體類型
Unity 提供了多種內置的碰撞體類型:
-
BoxCollider
(盒子碰撞體):-
最簡單、計算最快的碰撞體之一。
-
適用于大部分矩形、立方體或近似形狀的物體(墻壁、箱子)。
-
可以通過
size
和center
屬性調整大小和中心偏移。
-
-
SphereCollider
(球體碰撞體):-
計算也非常高效。
-
適用于球形或近似球形的物體(球、角色頭部、一些圓形投擲物)。
-
通過
radius
和center
調整。
-
-
CapsuleCollider
(膠囊體碰撞體):-
圓柱體兩端帶半球的形狀。
-
非常適合角色控制器:因為它可以很好地模擬人體形狀,并且在斜坡上滾動而不是卡住,方便處理角色移動。
-
通過
radius
、height
和direction
(X, Y, Z 軸) 調整。
-
-
MeshCollider
(網格碰撞體):-
直接使用物體的網格模型作為碰撞形狀。
-
最精確,但也是性能開銷最大的碰撞體類型。
-
警告: 除非必要,不建議在移動的物體上使用
MeshCollider
,尤其是不勾選Convex
選項的非凸 MeshCollider。它會顯著增加物理計算量。 -
通常用于: 不動的復雜地形、大型建筑等作為靜態碰撞體。
-
Convex
屬性: 勾選后,Unity 會計算一個凸包 (Convex Hull) 作為碰撞體。這會大大降低計算復雜度,允許MeshCollider
與其他MeshCollider
發生碰撞(否則兩個非凸 MeshCollider 無法互相碰撞)。
-
-
WheelCollider
(車輪碰撞體): 專門用于模擬車輛車輪的物理行為,具有懸掛、摩擦等特性。 -
TerrainCollider
(地形碰撞體): 專門用于 Unity 地形 (Terrain) 組件的碰撞檢測。
2.2 碰撞體屬性
-
Is Trigger
(是否為觸發器):-
勾選: 碰撞體變為觸發器。觸發器只檢測物體的進入、停留、離開,但不產生物理反彈或阻礙。它只會觸發
OnTriggerEnter/Stay/Exit
回調。 -
不勾選: 碰撞體是實體碰撞體。它會產生物理交互,阻礙其他物體,并觸發
OnCollisionEnter/Stay/Exit
回調。 -
應用:
-
觸發器: 區域進入檢測(如進入一個房間觸發劇情)、拾取物品(進入拾取范圍)。
-
實體碰撞體: 玩家與墻壁的碰撞、子彈擊中敵人產生反彈或傷害。
-
-
2.3 剛體 (Rigidbody) 的作用
要使碰撞體能夠進行物理模擬(受力、重力、反彈、滑動等),它必須依附于一個帶有 Rigidbody
(剛體) 組件的 GameObject。
-
沒有
Rigidbody
的碰撞體:-
只能作為靜態碰撞體或運動學碰撞體 (Kinematic Collider)。它們不會響應物理力,但可以被帶有
Rigidbody
的物體撞擊或被射線檢測到。 -
適用于地面、墻壁、不可移動的障礙物等。
-
-
有
Rigidbody
的碰撞體:-
會受到物理引擎的控制,響應重力、力、扭矩等。
-
是動態碰撞體。
-
適用于玩家角色、箱子、球等需要物理模擬的物體。
-
Is Kinematic
屬性: 勾選后,剛體將不受物理引擎控制,但仍然可以通過transform
手動移動它,且它會影響其他非運動學剛體。這是一種特殊的運動學剛體,常用于需要手動控制但又要參與物理交互的物體(如可移動的平臺)。
-
3. 碰撞回調函數:響應碰撞事件
當兩個帶有碰撞體的物體發生碰撞或觸發事件時,Unity 會調用相應的回調函數。這些函數需要在你的腳本中定義。
3.1 碰撞事件 (Collision Events)
當兩個實體碰撞體發生物理接觸時觸發(至少一個物體需要有 Rigidbody
且 Is Trigger
未勾選)。
-
OnCollisionEnter(Collision collision)
: 首次接觸時調用。 -
OnCollisionStay(Collision collision)
: 接觸過程中每幀調用。 -
OnCollisionExit(Collision collision)
: 停止接觸時調用。
Collision
參數: 包含了碰撞的詳細信息,例如:
-
collision.gameObject
:與當前物體發生碰撞的另一個 GameObject。 -
collision.collider
:與當前物體發生碰撞的另一個 Collider。 -
collision.contacts
:一個ContactPoint
數組,包含所有接觸點的詳細信息(位置、法線)。 -
collision.relativeVelocity
:碰撞發生時的相對速度。
示例:物體碰撞反彈
C#
// Unity
void OnCollisionEnter(Collision collision) {if (collision.gameObject.CompareTag("Wall")) {Debug.Log("我撞到墻了!");// 獲取撞擊點的法線,用于計算反彈Vector3 collisionNormal = collision.contacts[0].normal;// 如果是球體,可以根據入射速度和法線計算反彈速度// 例如:rb.velocity = Vector3.Reflect(rb.velocity, collisionNormal);}
}
3.2 觸發事件 (Trigger Events)
當一個或兩個碰撞體是觸發器 (Is Trigger
勾選) 時觸發。這些事件不會產生物理阻礙,只用于檢測進入/離開某個區域。
-
OnTriggerEnter(Collider other)
: 首次進入觸發器時調用。 -
OnTriggerStay(Collider other)
: 停留在觸發器內每幀調用。 -
OnTriggerExit(Collider other)
: 離開觸發器時調用。
Collider other
參數: 觸發事件的另一個 Collider 組件。
示例:拾取物品
C#
// Unity (掛在拾取物品上,并勾選其 Collider 的 Is Trigger)
void OnTriggerEnter(Collider other) {if (other.CompareTag("Player")) {Debug.Log("玩家進入了拾取區域,拾取物品!");// 銷毀物品或添加到玩家背包Destroy(gameObject);}
}
3.3 碰撞矩陣 (Collision Matrix)
在 Unity 的 Project Settings -> Physics (或 Physics 2D) 中,有一個 Layer Collision Matrix
(層碰撞矩陣)。這個矩陣允許你精確控制哪些層之間的物體可以相互碰撞或觸發事件。
-
作用: 這是一個重要的性能優化和邏輯隔離工具。你可以禁用不必要的層間碰撞,從而減少物理引擎的計算量。
-
示例:
-
禁用“Player”層與“SelfProjectile”(玩家發射的子彈)層的碰撞,避免玩家被自己的子彈擊中。
-
禁用“UI”層與“Environment”層的碰撞,防止射線檢測 UI 時錯誤地擊中背景物體。
-
4. 其他高級碰撞檢測方法
除了 Physics.Raycast
和碰撞回調,Unity 還提供了更靈活的檢測方法。
4.1 Physics.SphereCast()
/ BoxCast()
/ CapsuleCast()
:帶厚度的射線
這些方法就像發射一個有形狀的“探針”,而不是一條無限細的射線。它們檢測一個球體、盒子或膠囊體沿著一個方向掃過時是否與任何物體發生碰撞。
C#
// Unity
// Physics.SphereCast 示例:檢查前方是否能通過
public float sphereRadius = 0.5f;
public float maxDistance = 1f;
public LayerMask obstacleLayer;void Update() {// 從當前位置向前發射一個球體RaycastHit hit;if (Physics.SphereCast(transform.position, sphereRadius, transform.forward, out hit, maxDistance, obstacleLayer)) {Debug.Log("前方有障礙物被球體檢測到: " + hit.collider.gameObject.name);// 通常用于角色控制器,避免卡住}
}
-
應用:
-
角色控制器: 檢測角色前方是否有空間移動,避免穿墻。
-
掃雷: 坦克或大型載具在移動前檢測路徑是否暢通。
-
AoE 技能預判: 技能釋放前檢測范圍內是否有敵人。
-
4.2 Physics.OverlapSphere()
/ OverlapBox()
/ OverlapCapsule()
:范圍檢測
這些方法檢測一個特定形狀的區域內,當前有哪些碰撞體存在。它們不涉及運動,只是一個靜態的區域查詢。
C#
// Unity
// Physics.OverlapSphere 示例:檢測半徑內所有敵人
public float detectionRadius = 5f;
public LayerMask enemyLayer;void Update() {if (Input.GetKeyDown(KeyCode.Space)) {// 在當前位置周圍檢測半徑 5 米內的所有敵人Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);if (hitColliders.Length > 0) {Debug.Log("檢測到附近的敵人:");foreach (Collider collider in hitColliders) {Debug.Log(collider.gameObject.name);// 示例:對敵人施加效果// collider.GetComponent<EnemyHealth>().TakeDamage(10);}} else {Debug.Log("附近沒有敵人。");}}
}
-
應用:
-
AoE 技能: 施放范圍傷害技能時,檢測范圍內的所有目標。
-
收集物品: 玩家進入特定范圍自動拾取。
-
AI 索敵: AI 在自身周圍檢測是否有敵人進入其警戒范圍。
-
4.3 物理查詢優化:非分配版本 (NonAlloc
)
像 Physics.OverlapSphere
和 Physics.RaycastAll
這樣的方法,每次調用時都會分配內存來創建新的數組。如果這些操作在 Update
或 FixedUpdate
中頻繁調用,可能會導致內存垃圾 (Garbage Collection - GC) 產生,從而引發卡頓。
Unity 提供了這些方法的 NonAlloc
版本,它們接受一個預先創建好的數組作為參數,將結果填充到該數組中,從而避免了運行時內存分配。
C#
// Unity (NonAlloc 示例)
private Collider[] _overlapResults = new Collider[50]; // 預先分配一個足夠大的數組void FixedUpdate() {// 檢測周圍敵人,并避免 GCint numColliders = Physics.OverlapSphereNonAlloc(transform.position, detectionRadius, _overlapResults, enemyLayer);for (int i = 0; i < numColliders; i++) {Debug.Log("NonAlloc 命中敵人: " + _overlapResults[i].gameObject.name);}
}
對于性能敏感的游戲,在物理查詢頻繁的地方使用 NonAlloc
方法是非常重要的優化手段。
5. 可視化調試:Debug.DrawLine 與 Gizmos
在開發和調試射線與碰撞檢測時,將它們可視化出來至關重要。Unity 提供了 Debug.DrawLine
和 Gizmos
來幫助你。
5.1 Debug.DrawLine()
/ Debug.DrawRay()
這些方法允許你在 Scene 視圖中繪制臨時的線條和射線,方便你觀察它們的起點、方向和長度。只在運行游戲時可見。
C#
// Unity
void Update() {// 繪制一條從當前位置向前延伸 10 米的紅線Debug.DrawLine(transform.position, transform.position + transform.forward * 10f, Color.red);// 繪制一條射線 (與 Debug.DrawLine 類似,但更強調起點和方向)Debug.DrawRay(transform.position, transform.forward * 10f, Color.blue);// 繪制射線檢測結果RaycastHit hit;if (Physics.Raycast(transform.position, transform.forward, out hit, 10f)) {Debug.DrawLine(transform.position, hit.point, Color.green); // 命中部分綠色Debug.DrawRay(hit.point, hit.normal, Color.yellow); // 繪制法線}
}
5.2 OnDrawGizmos()
OnDrawGizmos()
是一個特殊的 Unity 回調函數,它允許你在 Scene 視圖中繪制持久的調試輔助線。即使游戲不運行,你也可以看到它們。
-
Gizmos.color
: 設置繪制顏色。 -
Gizmos.DrawWireSphere()
/DrawSphere()
: 繪制空心/實心球體。 -
Gizmos.DrawWireCube()
/DrawCube()
: 繪制空心/實心立方體。 -
Gizmos.DrawRay()
: 繪制射線。 -
Gizmos.matrix
: 用于在特定變換空間中繪制 Gizmos,例如繪制一個物體局部坐標系下的盒子。
示例:可視化檢測范圍
C#
// Unity
public float detectionRadius = 5f;
public float raycastDistance = 10f;// 在 Scene 視圖中繪制輔助線,無論游戲是否運行
void OnDrawGizmos() {// 繪制球形檢測范圍Gizmos.color = Color.yellow;Gizmos.DrawWireSphere(transform.position, detectionRadius);// 繪制前方射線檢測路徑Gizmos.color = Color.red;Gizmos.DrawRay(transform.position, transform.forward * raycastDistance);
}
OnDrawGizmosSelected()
: 只有當 GameObject 被選中時才繪制 Gizmos。這對于避免 Scene 視圖過于混亂非常有用。
合理使用 Debug.DrawLine
和 Gizmos
,能夠讓你清晰地看到射線、碰撞體、檢測范圍等在 3D 空間中的實際表現,從而大大提高調試效率。
6. 常見面試題與深入思考
6.1 面試問答
-
問:請解釋
Physics.Raycast()
和Physics.OverlapSphere()
的主要區別和各自的典型應用場景。-
考察點: 對射線檢測和范圍檢測不同用途的理解。
-
解析:
-
Physics.Raycast()
:-
區別: 從一個點向一個方向發射一條無限細的“線”,檢測它是否與第一個遇到的碰撞體相交。
-
應用: 鼠標點擊拾取、射擊游戲子彈命中、AI 視線檢測、單點障礙物檢測。它適合點對點的精確打擊或探測。
-
-
Physics.OverlapSphere()
:-
區別: 在一個給定的中心點周圍檢測一個球形區域內所有存在的碰撞體。它不涉及運動或方向,只是一個靜態的范圍查詢。
-
應用: 范圍傷害(AoE)技能、AI 索敵(檢測附近敵人)、拾取區域檢測。它適合檢測一個區域內的所有目標。
-
-
-
-
問:
Collider
組件的Is Trigger
屬性有什么作用?當兩個碰撞體發生接觸時,何時會觸發OnCollisionEnter
,何時會觸發OnTriggerEnter
?-
考察點: 對碰撞體模式(實體/觸發器)和物理回調的理解。
-
解析:
-
Is Trigger
作用: 勾選后,Collider
會變為觸發器。觸發器只檢測物體的進入、停留、離開,但不產生物理阻礙或反彈。未勾選時,是實體碰撞體,會參與物理模擬,產生阻礙和反彈。 -
OnCollisionEnter
觸發條件:-
兩個都是實體碰撞體。
-
至少一個碰撞體掛載了
Rigidbody
組件。 -
它們之間發生了物理接觸(碰撞)。
-
-
OnTriggerEnter
觸發條件:-
至少一個碰撞體勾選了
Is Trigger
。 -
至少一個碰撞體掛載了
Rigidbody
組件。 -
它們之間發生了接觸(穿越)。
-
-
總結:
OnCollision
處理物理世界的“撞擊”,OnTrigger
處理邏輯世界的“進入/離開區域”。
-
-
-
問:在頻繁進行物理查詢(例如在
Update
或FixedUpdate
中)時,你如何優化內存分配以避免 GC 抖動?請舉例說明。-
考察點: 對
NonAlloc
方法的理解和性能優化意識。 -
解析:
-
Physics.RaycastAll()
、Physics.OverlapSphere()
等方法在每次調用時會創建新的數組來存儲結果,這會導致內存垃圾 (GC Allocations) 的產生。當這些操作頻繁執行時,GC 會導致游戲卡頓。 -
為了避免 GC 抖動,應該使用這些方法的
NonAlloc
版本:Physics.RaycastNonAlloc()
、Physics.OverlapSphereNonAlloc()
等。 -
優化方法: 預先在類中聲明一個足夠大的數組(例如
private Collider[] _resultsBuffer = new Collider[50];
),然后在每次查詢時將結果填充到這個預分配的數組中,而不是讓 Unity 每次都創建新數組。這樣可以消除運行時內存分配。 -
示例:
int numHits = Physics.OverlapSphereNonAlloc(transform.position, radius, _resultsBuffer, layerMask);
-
-
-
問:你如何可視化調試射線和碰撞范圍,以便在 Scene 視圖中看到它們?
-
考察點: 對 Unity 調試工具的掌握。
-
解析:
-
Debug.DrawLine()
/Debug.DrawRay()
: 用于在運行時(游戲模式下)在 Scene 視圖中繪制臨時的線條或射線。它們在下一幀會被清除,適合觀察動態的軌跡。 -
OnDrawGizmos()
/OnDrawGizmosSelected()
: 用于在 Unity Editor 的 Scene 視圖中繪制持久的輔助圖形 (Gizmos),即使游戲沒有運行。OnDrawGizmos()
始終繪制,OnDrawGizmosSelected()
僅當 GameObject 被選中時繪制。適合可視化碰撞體范圍、檢測半徑、AI 視野等。 -
通過設置
Gizmos.color
可以改變繪制顏色,Gizmos.DrawWireSphere()
、Gizmos.DrawRay()
等方法用于繪制不同形狀。
-
-
6.2 深入思考:物理查詢的幾何原理
雖然 Unity 封裝了大部分底層細節,但理解物理查詢的幾何原理可以幫助你更好地解決問題:
-
射線與平面相交: 幾何上,這涉及到解一個線性方程組,找到直線與平面的交點。
-
射線與球體相交: 這通常歸結為解一個二次方程。球體的公式是 (x?cx)2+(y?cy)2+(z?cz)2=r2,射線的參數方程是 P=O+tD。將射線方程代入球體方程,會得到一個關于參數 t 的二次方程,解出 t 即可得到交點。
-
AABB 盒(軸對齊包圍盒)碰撞: 這是游戲中最常用的包圍盒類型。兩個 AABB 盒碰撞的判斷非常高效,只需要判斷它們在 X、Y、Z 三個軸上的投影是否重疊。如果所有軸都重疊,則發生碰撞。
BoxCollider
在底層就是使用 AABB 或 OBB (Oriented Bounding Box) 進行優化計算的。
了解這些幾何原理,可以幫助你在需要實現自定義碰撞檢測(例如非標準形狀的區域檢測、射線穿透不規則多邊形)時,能夠從數學層面構建解決方案。
總結與展望
本篇教程帶你深入了解了 3D 游戲開發中不可或缺的射線與碰撞檢測:
-
我們學習了射線 (Ray) 的概念、如何利用
Physics.Raycast()
進行精確的單點檢測,以及如何使用RaycastHit
獲取詳細的碰撞信息。 -
深入理解了 Unity 中各種碰撞體 (Collider) 的類型(
Box
,Sphere
,Capsule
,Mesh
等)及其應用場景,尤其是Is Trigger
屬性的妙用。 -
掌握了剛體 (Rigidbody) 在物理模擬中的關鍵作用,以及碰撞體與剛體的搭配規則。
-
學會了如何利用
OnCollision
和OnTrigger
回調來響應物理事件和邏輯事件。 -
探索了更高級的碰撞檢測方法,如帶厚度的射線 (
*Cast
) 和范圍檢測 (Overlap*
),以及**NonAlloc
版本**進行性能優化的重要性。 -
強調了使用
Debug.DrawLine
和Gizmos
進行可視化調試的重要性。
射線與碰撞檢測是構建游戲交互的核心,無論你的游戲類型如何,它們都將是你最常使用的工具之一。
在下一篇《幾何計算與常用算法(實用算法篇)》中,我們將脫離 Unity 的特定組件,回歸 3D 數學本身,探討一些更通用的幾何計算方法和實用算法,例如向量投影、點到線/平面的距離、插值以及其他在游戲邏輯中廣泛應用的數學技巧。
現在,你對 Unity 的射線和碰撞系統是否已經有了更清晰和全面的認識?在你的開發經歷中,有沒有遇到過哪些復雜的碰撞檢測問題?
Unity3D數學第一篇:向量與點、線、面(基礎篇)
Unity3D數學第二篇:旋轉與歐拉角、四元數(核心變換篇)
Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)
Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)
Unity3D數學第五篇:幾何計算與常用算法(實用算法篇)