Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)

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 (盒子碰撞體):

    • 最簡單、計算最快的碰撞體之一。

    • 適用于大部分矩形、立方體或近似形狀的物體(墻壁、箱子)。

    • 可以通過 sizecenter 屬性調整大小和中心偏移。

  • SphereCollider (球體碰撞體):

    • 計算也非常高效。

    • 適用于球形或近似球形的物體(球、角色頭部、一些圓形投擲物)。

    • 通過 radiuscenter 調整。

  • CapsuleCollider (膠囊體碰撞體):

    • 圓柱體兩端帶半球的形狀。

    • 非常適合角色控制器:因為它可以很好地模擬人體形狀,并且在斜坡上滾動而不是卡住,方便處理角色移動。

    • 通過 radiusheightdirection (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)

當兩個實體碰撞體發生物理接觸時觸發(至少一個物體需要有 RigidbodyIs 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.OverlapSpherePhysics.RaycastAll 這樣的方法,每次調用時都會分配內存來創建新的數組。如果這些操作在 UpdateFixedUpdate 中頻繁調用,可能會導致內存垃圾 (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.DrawLineGizmos 來幫助你。

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.DrawLineGizmos,能夠讓你清晰地看到射線、碰撞體、檢測范圍等在 3D 空間中的實際表現,從而大大提高調試效率。


6. 常見面試題與深入思考

6.1 面試問答

  1. 問:請解釋 Physics.Raycast()Physics.OverlapSphere() 的主要區別和各自的典型應用場景。

    • 考察點: 對射線檢測和范圍檢測不同用途的理解。

    • 解析:

      • Physics.Raycast()

        • 區別: 從一個點向一個方向發射一條無限細的“線”,檢測它是否與第一個遇到的碰撞體相交。

        • 應用: 鼠標點擊拾取、射擊游戲子彈命中、AI 視線檢測、單點障礙物檢測。它適合點對點的精確打擊或探測。

      • Physics.OverlapSphere()

        • 區別: 在一個給定的中心點周圍檢測一個球形區域內所有存在的碰撞體。它不涉及運動或方向,只是一個靜態的范圍查詢。

        • 應用: 范圍傷害(AoE)技能、AI 索敵(檢測附近敵人)、拾取區域檢測。它適合檢測一個區域內的所有目標。

  2. 問:Collider 組件的 Is Trigger 屬性有什么作用?當兩個碰撞體發生接觸時,何時會觸發 OnCollisionEnter,何時會觸發 OnTriggerEnter

    • 考察點: 對碰撞體模式(實體/觸發器)和物理回調的理解。

    • 解析:

      • Is Trigger 作用: 勾選后,Collider 會變為觸發器。觸發器只檢測物體的進入、停留、離開,但不產生物理阻礙或反彈。未勾選時,是實體碰撞體,會參與物理模擬,產生阻礙和反彈。

      • OnCollisionEnter 觸發條件:

        • 兩個都是實體碰撞體

        • 至少一個碰撞體掛載了 Rigidbody 組件。

        • 它們之間發生了物理接觸(碰撞)。

      • OnTriggerEnter 觸發條件:

        • 至少一個碰撞體勾選了 Is Trigger

        • 至少一個碰撞體掛載了 Rigidbody 組件。

        • 它們之間發生了接觸(穿越)。

      • 總結: OnCollision 處理物理世界的“撞擊”,OnTrigger 處理邏輯世界的“進入/離開區域”。

  3. 問:在頻繁進行物理查詢(例如在 UpdateFixedUpdate 中)時,你如何優化內存分配以避免 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);

  4. 問:你如何可視化調試射線和碰撞范圍,以便在 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) 在物理模擬中的關鍵作用,以及碰撞體與剛體的搭配規則。

  • 學會了如何利用 OnCollisionOnTrigger 回調來響應物理事件和邏輯事件。

  • 探索了更高級的碰撞檢測方法,如帶厚度的射線 (*Cast)范圍檢測 (Overlap*),以及**NonAlloc 版本**進行性能優化的重要性。

  • 強調了使用 Debug.DrawLineGizmos 進行可視化調試的重要性。

射線與碰撞檢測是構建游戲交互的核心,無論你的游戲類型如何,它們都將是你最常使用的工具之一。

在下一篇《幾何計算與常用算法(實用算法篇)》中,我們將脫離 Unity 的特定組件,回歸 3D 數學本身,探討一些更通用的幾何計算方法和實用算法,例如向量投影、點到線/平面的距離、插值以及其他在游戲邏輯中廣泛應用的數學技巧。

現在,你對 Unity 的射線和碰撞系統是否已經有了更清晰和全面的認識?在你的開發經歷中,有沒有遇到過哪些復雜的碰撞檢測問題?

Unity3D數學第一篇:向量與點、線、面(基礎篇)

Unity3D數學第二篇:旋轉與歐拉角、四元數(核心變換篇)

Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)

Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)

Unity3D數學第五篇:幾何計算與常用算法(實用算法篇)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917281.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917281.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917281.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據處理和統計分析——09 數據分組

1 聚合 1.1 簡介 在SQL中我們經常使用GROUP BY將某個字段&#xff0c;按不同的取值進行分組&#xff0c;在Pandas中也有groupby()函數&#xff1b;分組之后&#xff0c;每組都會有至少1條數據&#xff0c;將這些數據進一步處理返回單個值的過程就是聚合&#xff0c;比如分組之后…

【數據結構與算法】數據結構初階:排序內容加餐(一)——快速排序:三路劃分、自省排序

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題 &#x1f349;學習方向&#xff1a;C/C方向 ??人生格言&#xff1a;為天地立心&#xff0c;為生民立命&#xff0c;為…

MySqL(加餐)

范式第一范式數據庫表的每一列都是不可分割的原子數據項&#xff0c;而不能是集合&#xff0c;數組&#xff0c;對象等非原子數據。在關系型數據庫的設計中&#xff0c;滿足第一范式是對關系模式的基本要求。不滿足第一范式的數據庫就不能被稱為關系數據庫。第一范式實際上只要…

【redis】基于工業界技術分享的內容總結

Redis 實踐指南與核心概念 一、Java 中常用的 Redis 使用場景與實踐 緩存&#xff08;Caching&#xff09; 場景&#xff1a;熱點數據、頻繁訪問的數據&#xff0c;如商品詳情、用戶信息。通過緩存減少數據庫壓力&#xff0c;提高系統響應速度。 工業界實踐&#xff1a; 淘寶…

服務端之nestJS常用異常類及封裝自定義響應模塊

MENU前言常用異常類&#xff08;由nestjs/common提供&#xff09;示例自定義異常&#xff08;可選&#xff09;自定義響應模塊前言 在NestJS中&#xff0c;nestjs/common提供了大量的內置異常類&#xff0c;主要用于在控制器、服務等層拋出特定的HTTP錯誤響應。 常用異常類&…

數據鏈路層、NAT、代理服務、內網穿透

目錄 一. 以太網 以太網幀格式 二. MAC地址 三. MTU 四. ARP協議 五. NAT NAPT 六. 代理服務器 正向代理 反向代理 七. 內網穿透 八. 內網打洞 一. 以太網 ? "以太網" 不是一種具體的網絡, 而是一種技術標準; 既包含了數據鏈路層的內 容, 也包含了一些物理層…

Rust在CentOS 6上的移植

Rust已不支持Cent OS 6 rhel是Redhat 發布的Red Hat Enterprise Linux的簡稱&#xff0c;使用rhel源代碼編譯的CentOS&#xff0c;最新的版本是CentOS 7&#xff0c;于2024年停止支持。而更古老的CentOS 6&#xff0c;則在2020年就已經結束了。 而面對如此老舊的系統&#xf…

C++音視頻開發:基礎面試題

音視頻領域技術門檻高&#xff0c;學習資料稀缺&#xff0c;體系化書籍和開發工具有限&#xff0c;新手入門困難。音視頻開發涉及眾多任務&#xff1a;音頻&#xff08;采集、編解碼、降噪等&#xff09;、視頻&#xff08;采集、編解碼、圖像處理&#xff09;、實時傳輸&#…

C++刷題 - 7.27

貪心算法的詳細邏輯這個問題的最優解可以用 貪心算法 在 O(N) 時間 內解決。它的核心思想是&#xff1a;每次操作盡可能覆蓋最長的連續非零區間&#xff0c;并通過數學分析發現&#xff1a;最小操作次數等于所有“上升臺階”的高度差之和。1. 直觀理解假設 steps [1, 2, 3, 2,…

音頻3A處理簡介之AGC(自動增益控制)

在音頻通話和視頻會議中&#xff0c;音頻自動增益控制AGC模塊的主要作用&#xff1a;? 穩定音頻信號的輸出電平。無論麥克風采集信號的強弱&#xff08;如用戶離麥克風遠近程度不同&#xff09;&#xff0c;盡可能保證音頻采集模塊的輸出音量保持相對一致&#xff0c;不會偏大…

web前端打包apk包

我用的是HBuilder工具,可視化更便捷&#xff0c;目前我這操作的apk包是不需要上架的&#xff0c;所以跟實際需要上架的可能還有些出入 首先先新建個項目&#xff0c;選擇5App模式 把目前需要打包的內容上傳到服務器&#xff0c;我們以嵌套的形式進行打包&#xff0c;找到index.…

Ansible提權sudo后執行報錯

1.問題 配置了sudo提權信息后&#xff0c;執行ansible-play報錯&#xff0c;報錯信息如下&#xff1a;2.原因 sudo沒有執行**/bin/sh的權限&#xff0c;而ansible腳本中依賴/bin/sh**&#xff0c;所以報錯了&#xff1a; 查看日志sudo tail -f /var/log/secure3.解決方式 修改*…

.NET報表控件ActiveReports發布v19.0——正式兼容 .NET 9

ActiveReports 是一款專注于 .NET 和 .NET Core 平臺的報表控件。通過拖拽式報表設計器&#xff0c;可以快速地設計 Excel表格、Word文檔、圖表、數據過濾、數據鉆取、精準套打等類型報表&#xff0c;全面滿足 WinForm、ASP.NET、ASP.NET MVC、WPF 平臺中各種報表的開發需要。同…

SCI論文選詞煉句

標準句子不能啰嗦&#xff1b;詞不能有問題&#xff0c;得是SCI中經常出現的&#xff0c;符合上下文的。SCI論文中常出現的摸棱兩可的詞單詞涵義例子Architecture指 整體系統設計方案&#xff0c;如網絡層次結構、模塊組合、激活函數選擇等深度學習模型架構Structure更泛泛&…

Qt deleteLater 延遲刪除原理

deleteLater 調用 事件發送 void QObject::deleteLater() {QCoreApplication::postEvent(this, new QDeferredDeleteEvent()); }首先該對象繼承QObject調用deleteLater&#xff0c; 內部會發送刪除事件QCoreApplication::postEvent(this, new QDeferredDeleteEvent()) 到事件循…

TypeScript SDK 升級:通過 Upload Relay 賦能更多應用

自 3 月主網上線以來&#xff0c;Walrus 開發者社區持續展現出強勁的發展勢頭&#xff1a; 當前 Walrus 已存儲超 758 TB 數據&#xff0c;為數百個項目提供支持。在 2025 年 6 月舉辦的 Sui Overflow 黑客松上&#xff0c;Walrus 成為最受歡迎的數據層。該賽事共收到 599 個項…

C#線程同步(二)鎖

目錄 1.lock 2.Monitor 3.鎖的其它要注意的問題 3.1同步對象的選擇 3.2什么時候該上鎖 3.3鎖和原子性 3.4嵌套鎖 3.5 死鎖 3.6 性能 4.Mutex 5.Semaphore 1.lock 讓我們先看一段代碼&#xff1a; class ThreadUnsafe {static int _val1 1, _val2 1;static void G…

鴻蒙智能居家養老系統構思(續二)—— 適老化烹飪中心詳細構思

一、背景在“寫給華為鴻蒙智家 —— 智能居家養老系統構思”一文中&#xff0c;結合對居家養老的理解及個人體驗&#xff0c;提出了基于鴻蒙OS實現居家養老系統的粗略構思。其中包含“吃得好”。當老人到了不能隨性外出活動、只能在家消耗時光時&#xff0c;除了一些看看電視、…

高斯透鏡公式(調整鏡頭與感光元件之間的距離時,使得不同距離的物體在感光元件上形成清晰的影像)

當使用定焦鏡頭時&#xff0c;仍然可以調整鏡頭與感光元件&#xff08;或膠片&#xff09;之間的距離時&#xff0c;使得不同距離的物體在感光元件上形成清晰的影像。對此可以用高斯透鏡公式進行解釋&#xff1a; 一、透鏡成像的基本原理 在光學中&#xff0c;一個基本的公式是…

預過濾環境光貼圖制作教程:第三階段 - GGX 分布預過濾

核心目標 GGX 分布是 PBR 中模擬粗糙表面高光反射的主流模型,其核心是通過統計分布描述微表面的朝向概率。本階段的目標是: 基于第一階段生成的環境圖集,預計算 6 個級別的 GGX 過濾結果(對應不同粗糙度); 使用蒙特卡洛采樣(Monte Carlo Sampling)加速 GGX 卷積計算;…