【unity知識】unity使用AABB(軸對齊包圍盒)和OBB(定向包圍盒)優化碰撞檢測

文章目錄

  • 前言
  • 一、AABB(軸對齊包圍盒)
    • 1、基本概念
    • 2、數學表示
    • 3、Unity中的實現
    • 4、實際應用示例
  • 二、OBB(有向包圍盒)
    • 1、Physics.ComputePenetration (Unity 物理引擎)
      • 1.1 基本概念
      • 1.2 Unity中的實現
      • 1.3 實際應用示例
    • 2、OBB (SAT) 手動實現
      • 2.1 基本概念
      • 2.2 數學表示
      • 2.3 Unity中的實現
      • 2.4 實際應用示例
    • 3、如何選擇?
  • 三、選擇建議:
  • 專欄推薦
  • 完結

前言

在Unity游戲開發中,碰撞檢測是至關重要的功能。為了優化性能,開發者通常使用包圍盒(Bounding Box)技術作為初步的碰撞檢測手段。本文將深入探討兩種核心包圍盒技術:AABB(軸對齊包圍盒)和OBB(有向包圍盒)。

一、AABB(軸對齊包圍盒)

1、基本概念

AABB(Axis-Aligned Bounding Box)是最簡單的包圍盒形式,其特點是:

  • 始終與世界坐標系的X/Y/Z軸對齊

  • 不隨物體旋轉而改變方向

  • 計算簡單,性能高效

2、數學表示

一個AABB可以用兩個點表示:

  • 最小點(min):包含X/Y/Z的最小值

  • 最大點(max):包含X/Y/Z的最大值

或者用中心點和半尺寸表示:

  • 中心點(center)

  • 半尺寸(extents):從中心到各邊界的距離

3、Unity中的實現

Unity內置了Bounds結構體來處理AABB:

// 創建AABB
Bounds bounds = new Bounds(center, size);// 檢測AABB相交
bool isIntersecting = bounds.Intersects(otherBounds);

4、實際應用示例

// 獲取預制體的AABB包圍盒
Bounds GetPrefabBounds(GameObject prefab)
{// 優先使用渲染器包圍盒Renderer renderer = prefab.GetComponentInChildren<Renderer>();if (renderer != null) return renderer.bounds;// 其次嘗試碰撞器Collider collider = prefab.GetComponentInChildren<Collider>();if (collider != null) return collider.bounds;// 默認返回單位大小return new Bounds(Vector3.zero, Vector3.one);
}/// <summary>
/// AABB碰撞檢測,檢查新物體是否與已放置物體發生重疊
/// </summary>
/// <param name="position">新物體的中心位置</param>
/// <param name="halfExtents">新物體的半尺寸(從中心到各邊界的距離)</param>
/// <param name="existingBounds">已放置物體的包圍盒列表</param>
/// <returns>true表示有重疊,false表示無重疊</returns>
bool CheckAABBOverlap(Vector3 position, Vector3 halfExtents, List<Bounds> existingBounds)
{// 1. 創建新物體的包圍盒// Bounds構造函數需要中心點和完整尺寸,所以將halfExtents乘以2Bounds newBounds = new Bounds(position, halfExtents * 2);// 2. 遍歷所有已放置物體的包圍盒foreach (var bounds in existingBounds){// 3. 檢查新物體與當前已放置物體是否相交if (bounds.Intersects(newBounds)){// 4. 如果相交則立即返回true(發生重疊)return true;}}// 5. 遍歷結束未發現重疊,返回falsereturn false;
}

調用

// 存儲已放置物體的位置和大小(用于碰撞檢測)
List<Bounds> placedBounds = new List<Bounds>();
//預制體
public GameObiect prefab;Vector3 position = 預制體準備放置的位置;
// 獲取預制體大小
Bounds bounds = GetPrefabBounds(prefab);//獲取包圍盒(Bounds)的半尺寸
halfExtents = bounds.extents;// 檢查是否與其他物體重疊
if (CheckOverlap(position, halfExtents, placedBounds)) return;// 實例化預制體
GameObject propInstance = Instantiate(prefab, position, Quaternion.identity);// 添加到已放置列表
placedBounds.Add(new Bounds(position, halfExtents * 2));

它沒有考慮物體的旋轉。當物體旋轉后,使用 Bounds.Intersects() 進行軸對齊包圍盒檢測會導致誤判。

二、OBB(有向包圍盒)

1、Physics.ComputePenetration (Unity 物理引擎)

1.1 基本概念

Physics.ComputePenetration 是 Unity 物理引擎提供的一個方法,用于精確計算兩個碰撞體之間的穿透情況。它比簡單的 Bounds.Intersects 或 Collider.bounds 檢測更準確,特別是對于旋轉后的物體或非軸對齊的碰撞體。

1.2 Unity中的實現

public static bool Physics.ComputePenetration(Collider colliderA,      // 第一個碰撞體Vector3 positionA,       // 第一個碰撞體的位置Quaternion rotationA,    // 第一個碰撞體的旋轉Collider colliderB,      // 第二個碰撞體Vector3 positionB,       // 第二個碰撞體的位置Quaternion rotationB,    // 第二個碰撞體的旋轉out Vector3 direction,   // 穿透方向(從A指向B)out float distance       // 穿透深度
);

返回值

  • true → 兩個碰撞體發生穿透

  • false → 兩個碰撞體沒有穿透

1.3 實際應用示例

bool CheckOverlap(Vector3 position, Quaternion rotation, Vector3 size, List<GameObject> placedObjects)
{// 創建一個臨時 BoxCollider 用于檢測GameObject tempObj = new GameObject("TempCollider");BoxCollider testCollider = tempObj.AddComponent<BoxCollider>();testCollider.size = size; // 注意:size 是完整尺寸(不是 halfExtents)// 檢查與所有已放置物體的碰撞foreach (GameObject obj in placedObjects){Collider[] objColliders = obj.GetComponentsInChildren<Collider>();foreach (Collider col in objColliders){Vector3 dir;float dist;if (Physics.ComputePenetration(testCollider, position, rotation,col, col.transform.position, col.transform.rotation,out dir, out dist)){Destroy(tempObj);return true; // 發生重疊}}}Destroy(tempObj);return false; // 無重疊
}

調用

// 修改為存儲實際物體
List<GameObject> placedObjects = new List<GameObject>();if (CheckOverlap(position, rotation, halfExtents * 2, placedObjects)) return;GameObject propInstance = Instantiate(prefab, position, rotation);// 實例化后添加
placedObjects.Add(propInstance);

2、OBB (SAT) 手動實現

2.1 基本概念

OBB(Oriented Bounding Box)是更精確的包圍盒:

  • 可以隨物體旋轉/縮放

  • 方向與物體的本地坐標軸一致

  • 使用分離軸定理(SAT)進行相交檢測

2.2 數學表示

一個OBB需要存儲:

  • 中心點(center)

  • 半尺寸(extents)

  • 旋轉(rotation)

2.3 Unity中的實現

Unity沒有直接提供OBB結構,但可以自定義實現:

using UnityEngine;/// <summary>
/// 有向包圍盒(Oriented Bounding Box)結構體
/// </summary>
public struct OBB
{public Vector3 center;    // 包圍盒中心點public Vector3 extents;   // 包圍盒半尺寸(從中心到各邊的距離)public Quaternion rotation; // 包圍盒旋轉/// <summary>/// 檢測兩個OBB是否相交/// </summary>/// <param name="other">另一個OBB</param>/// <returns>true表示相交,false表示不相交</returns>public bool Intersects(OBB other){// 使用分離軸定理(SAT)進行相交檢測return SATIntersection(this, other);}/// <summary>/// 分離軸定理(SAT)實現/// </summary>private static bool SATIntersection(OBB a, OBB b){// 獲取兩個OBB的旋轉矩陣Matrix4x4 aRot = Matrix4x4.Rotate(a.rotation);Matrix4x4 bRot = Matrix4x4.Rotate(b.rotation);// 需要測試的15條分離軸:// 6條來自兩個OBB的局部坐標軸// 9條來自兩兩軸的叉積Vector3[] axes = new Vector3[15];// A的3個局部軸(X/Y/Z)axes[0] = aRot.GetColumn(0).normalized; // A的X軸axes[1] = aRot.GetColumn(1).normalized; // A的Y軸axes[2] = aRot.GetColumn(2).normalized; // A的Z軸// B的3個局部軸(X/Y/Z)axes[3] = bRot.GetColumn(0).normalized; // B的X軸axes[4] = bRot.GetColumn(1).normalized; // B的Y軸axes[5] = bRot.GetColumn(2).normalized; // B的Z軸// 計算A和B各軸的叉積(9條軸)// 這些軸是兩個OBB各邊平面法線的方向axes[6] = Vector3.Cross(axes[0], axes[3]).normalized;  // A.X × B.Xaxes[7] = Vector3.Cross(axes[0], axes[4]).normalized;  // A.X × B.Yaxes[8] = Vector3.Cross(axes[0], axes[5]).normalized;  // A.X × B.Zaxes[9] = Vector3.Cross(axes[1], axes[3]).normalized;  // A.Y × B.Xaxes[10] = Vector3.Cross(axes[1], axes[4]).normalized; // A.Y × B.Yaxes[11] = Vector3.Cross(axes[1], axes[5]).normalized; // A.Y × B.Zaxes[12] = Vector3.Cross(axes[2], axes[3]).normalized; // A.Z × B.Xaxes[13] = Vector3.Cross(axes[2], axes[4]).normalized; // A.Z × B.Yaxes[14] = Vector3.Cross(axes[2], axes[5]).normalized; // A.Z × B.Z// 在每條分離軸上做投影測試foreach (var axis in axes){// 忽略長度接近0的無效軸(叉積結果可能是零向量)if (axis.sqrMagnitude < 0.001f) continue;// 如果在當前軸上投影不重疊,則存在分離軸,OBB不相交if (!OverlapOnAxis(a, b, axis))return false;}// 所有軸上都重疊,則OBB相交return true;}/// <summary>/// 檢測兩個OBB在指定軸上的投影是否重疊/// </summary>private static bool OverlapOnAxis(OBB a, OBB b, Vector3 axis){// 計算兩個OBB在當前軸上的投影半徑float aProj = GetProjectionRadius(a, axis);float bProj = GetProjectionRadius(b, axis);// 計算兩個中心點在當前軸上的距離float centerDistance = Mathf.Abs(Vector3.Dot(axis, b.center - a.center));// 如果中心距離小于投影半徑之和,則投影重疊return centerDistance <= (aProj + bProj);}/// <summary>/// 計算OBB在指定軸上的投影半徑/// </summary>private static float GetProjectionRadius(OBB obb, Vector3 axis){// 獲取OBB的旋轉矩陣Matrix4x4 rot = Matrix4x4.Rotate(obb.rotation);// 計算OBB三個軸向的向量(考慮半尺寸)Vector3 xAxis = rot.GetColumn(0) * obb.extents.x;Vector3 yAxis = rot.GetColumn(1) * obb.extents.y;Vector3 zAxis = rot.GetColumn(2) * obb.extents.z;// 投影半徑 = 各軸在測試軸上投影長度的絕對值之和return Mathf.Abs(Vector3.Dot(xAxis, axis))+ Mathf.Abs(Vector3.Dot(yAxis, axis))+ Mathf.Abs(Vector3.Dot(zAxis, axis));}
}

2.4 實際應用示例

/// <summary>
/// 檢查新物體是否與已放置物體發生OBB重疊
/// </summary>
/// <param name="position">新物體的中心位置</param>
/// <param name="halfExtents">新物體的半尺寸</param>
/// <param name="rotation">新物體的旋轉</param>
/// <param name="existingOBBs">已放置物體的OBB列表</param>
/// <returns>true表示有重疊,false表示無重疊</returns>
bool CheckOverlap(Vector3 position, Vector3 halfExtents, Quaternion rotation, List<OBB> existingOBBs)
{// 創建新物體的OBBOBB newOBB = new OBB(){center = position,    // 設置中心點extents = halfExtents, // 設置半尺寸rotation = rotation   // 設置旋轉};// 遍歷所有已放置物體的OBBforeach (var obb in existingOBBs){// 如果與任一已放置物體相交,返回trueif (newOBB.Intersects(obb))return true;}// 沒有發現重疊return false;
}

調用

// 存儲已放置物體的OBB列表
List<OBB> placedOBBs = new List<OBB>();// 嘗試在指定位置放置物體
if (!CheckOverlap(position, halfExtents, rotation, placedOBBs))
{// 如果沒有重疊,則實例化新物體Instantiate(prop.prefab, position, rotation);// 將新物體的OBB添加到已放置列表placedOBBs.Add(new OBB(){center = position,    // 記錄中心位置extents = halfExtents, // 記錄半尺寸rotation = rotation   // 記錄旋轉});
}

3、如何選擇?

? 使用 OBB (SAT) 的情況

  • 需要檢測 大量簡單形狀(如地牢墻壁、道具擺放)。

  • 在 非物理系統 中使用(如自定義碰撞邏輯)。

  • 追求 極致性能(如子彈碰撞檢測)。

? 使用 Physics.ComputePenetration 的情況

  • 需要檢測 復雜形狀(如 MeshCollider)。

  • 需要 穿透信息(如角色控制器解決卡頓)。

  • 已在使用 Unity 物理系統(如 Rigidbody 物體)。

三、選擇建議:

  • 對靜態物體或簡單碰撞使用AABB

  • 對動態旋轉物體使用OBB

  • 可采用兩階段檢測:先用AABB快速篩選剔除絕對不相交的物體,再用OBB精確判斷


專欄推薦

地址
【unity游戲開發入門到精通——C#篇】
【unity游戲開發入門到精通——unity通用篇】
【unity游戲開發入門到精通——unity3D篇】
【unity游戲開發入門到精通——unity2D篇】
【unity實戰】
【制作100個Unity游戲】
【推薦100個unity插件】
【實現100個unity特效】
【unity框架/工具集開發】
【unity游戲開發——模型篇】
【unity游戲開發——InputSystem】
【unity游戲開發——Animator動畫】
【unity游戲開發——UGUI】
【unity游戲開發——聯網篇】
【unity游戲開發——優化篇】
【unity游戲開發——shader篇】
【unity游戲開發——編輯器擴展】
【unity游戲開發——熱更新】
【unity游戲開發——網絡】

完結

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果學習過程中遇到任何問題,也歡迎你評論私信找我。

贈人玫瑰,手有余香!如果文章內容對你有所幫助,請不要吝嗇你的點贊評論和關注,你的每一次支持都是我不斷創作的最大動力。當然如果你發現了文章中存在錯誤或者有更好的解決方法,也歡迎評論私信告訴我哦!
在這里插入圖片描述

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

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

相關文章

Numpy科學計算與數據分析專題

Numpy科學計算與數據分析 1. Numpy入門&#xff1a;數組操作與科學計算基礎 2. Numpy入門&#xff1a;多平臺安裝與基礎環境配置 3. Numpy數組創建與應用入門 4. Numpy數組屬性入門&#xff1a;形狀、維度與大小 5. Numpy數組索引與切片入門 6. Numpy數組操作入門&#xff1a;…

齊護機器人小智AI_MCP圖形化編程控制Arduino_ESP32

齊護機器人小智AI_MCP圖形化編程控制Arduino_ESP32 齊護AiTall在項目實踐里&#xff0c;我們常常期望達成這樣一種場景&#xff1a;借助智能體&#xff08;例如小智 AI&#xff09;來遠程操控其他開發板上的設備&#xff0c;這類似于智能家居系統中智能音箱與各類家電的互動模式…

CPO-SVM分類預測+特征貢獻SHAP分析,通過特征貢獻分析增強模型透明度,Matlab代碼實現,引入SHAP方法打破黑箱限制,提供全局及局部雙重解釋視角

代碼功能 該Matlab代碼實現了一個基于CPO-SVM冠豪豬算法優化支持向量機的數據分類模型&#xff0c;結合了SHAP可解釋性分析&#xff0c;CPO選擇最佳的SVM參數c和g。 SVM模型有兩個非常重要的參數C與gamma。其中 C是懲罰系數&#xff0c;即對誤差的寬容度。c越高&#xff0c;說明…

Failed to restart docker.service: Unit docker.service is masked.

docker.service 被標記為 "masked" 意味著 systemd 已阻止該服務被啟動或運行。這通常發生在 Docker Desktop 安裝過程中,因為它使用自己的服務管理機制。以下是解決方法: 解決方案: 解除服務的 mask 狀態: bash sudo systemctl unmask docker.service sudo sys…

2025 藍橋杯C/C++國B 部分題解

P12836 [藍橋杯 2025 國 B] 翻倍 題目描述 給定 nnn 個正整數 A1,A2,…,AnA_1, A_2, \ldots, A_nA1?,A2?,…,An?&#xff0c;每次操作可以選擇任意一個數翻倍。 請輸出讓序列單調不下降&#xff0c;也就是每個數都不小于上一個數&#xff0c;最少需要操作多少次&#xff1f;…

os標準庫

os標準庫os包提供了操作系統函數&#xff0c;但和操作系統無關。 os包的接口規定為在所有操作系統中都是一致的。 設計為Unix風格的。1. 權限說明 os標準庫有大量的文件操作&#xff0c;在創建文件等操作中&#xff0c;需要指的perm。 在go語言中perm是一個uint32類型 在go語言…

QtC++ 中使用 qtwebsocket 開源庫實現基于websocket的本地服務開發詳解

前言 當前實時通信功能越來越受到重視&#xff0c;無論是在線聊天、實時數據監控還是多人協作工具&#xff0c;都離不開高效、穩定的實時通信技術。WebSocket 作為一種全雙工通信協議&#xff0c;為實時通信提供了良好的解決方案。而在 QtC 開發環境中&#xff0c;qtwebsocket …

小程序實時保存優化

背景。避免數據存儲后丟失。要求實時保存。問題&#xff1a;保存時出現卡斷&#xff0c;輸入的內容會被抹除。問題原因。輸入頻繁速度塊&#xff0c;會影響cpu處理速度。解決方案。用戶停止輸入500ms后開始保存&#xff0c;否則不保存。這里是保存方法&#xff1a;當500ms以內有…

國產化Excel處理組件Spire.XLS教程:使用 C# 將 DataTable 導出為 Excel 文件

在 C# 中將 DataTable 導出為 Excel 文件&#xff0c;是 .NET 開發中常見的任務&#xff0c;廣泛應用于報表生成、日志導出、系統間數據共享等場景。通過使用獨立的組件庫&#xff0c;開發者可以輕松將 DataTable 數據寫入 Excel 文件&#xff0c;并應用格式設置&#xff0c;生…

C語言學習筆記——編譯和鏈接

目錄1 C程序的執行流程2 翻譯環境2.1 預編譯2.2 編譯2.2.1 詞法分析2.2.2 語法分析2.2.3 語法分析2.3 匯編2.4 鏈接1 C程序的執行流程 用戶編寫好的C程序不能直接被計算機識別并執行&#xff0c;在執行前&#xff0c;要先將源文件和頭文件進行編譯&#xff0c;生成目標文件&am…

Flink-1.19.0源碼詳解9-ExecutionGraph生成-后篇

《Flink-1.19.0源碼詳解8-ExecutionGraph生成-前篇》前篇已從Flink集群端調度開始解析ExecutionGraph生成的源碼&#xff0c;解析了ExecutionGraph的ExecutionJobVertex節點、ExecutionVertex節點、IntermediateResult數據集、IntermediateResultPartition數據集分區與封裝Task…

19、閾值分割+blob分析

目錄 一、仿射變換 1.變換矩陣 2.在矩陣的基礎上添加各種變換形式 3.開始變換 4.計算變換矩陣參數 新算子 二、閾值分割 新算子 三、blob分析案例 1.焊點 2.石頭 3.木材 4.車牌 5.骰子 新算子 一、仿射變換 1.變換矩陣 // 產生仿射變換矩陣hom_mat2d_identity…

破解 Django N+1 查詢困境:使用 select_related 與 prefetch_related 實踐指南

破解 Django N+1 查詢困境:使用 select_related 與 prefetch_related 實踐指南 開篇引入 數據庫查詢性能常常是 Web 應用性能瓶頸中的重中之重。Django ORM 以簡潔直觀的 API 層將 Python 代碼與數據庫打通,卻也可能因默認的惰性加載帶來 N+1 查詢問題,造成不必要的網絡往…

深入解析K-means聚類:從原理到調優實戰

一、聚類分析與K-means的核心價值在無監督學習領域&#xff0c;聚類分析是探索數據內在結構的核心技術。?K-means算法因其簡潔高效成為最廣泛使用的聚類方法&#xff0c;在客戶分群、圖像壓縮、生物信息學等領域應用廣泛。其核心目標是將數據集劃分為K個簇&#xff0c;實現“簇…

數據結構基礎:哈希表、排序和查找算法

目錄 一、哈希表 1.哈希算法 2.哈希碰撞 3.哈希表 4.哈希表相關操作 哈希表插入 哈希表遍歷 元素查找 哈希表銷毀 二、排序算法 1. 排序算法對比 2. 排序算法實現 冒泡排序 選擇排序 插入排序 希爾排序 快速排序 三、查找算法 1. 查找算法對比 2. 查找算法實…

Linux內核參數調優:為K8s節點優化網絡性能

在高并發微服務環境中&#xff0c;網絡性能往往成為K8s集群的瓶頸。本文將深入探討如何通過精細化的Linux內核參數調優&#xff0c;讓你的K8s節點網絡性能提升30%以上。引言&#xff1a;為什么網絡調優如此重要&#xff1f;作為一名在生產環境中維護過數千節點K8s集群的運維工程…

全家桶” 戰略如何重塑智能服務標準?無憂秘書 AI + 智腦 + 數字人協同模式的底層架構解析

在數字化浪潮的推動下&#xff0c;企業對智能化服務的需求日益增長。然而&#xff0c;單一的技術或產品往往難以滿足復雜場景下的多樣化需求。近年來&#xff0c;“全家桶”戰略成為科技行業的一大趨勢&#xff0c;通過整合多維度技術與服務&#xff0c;為企業提供全方位的支持…

前端后端之爭?JavaScript和Java的特性與應用場景解析

一、名字相似&#xff0c;本質迥異 1.1 歷史淵源與命名背景 在編程世界中&#xff0c;很少有兩種語言像JavaScript和Java這樣&#xff0c;僅僅因為名字的相似性就引發了無數初學者的困惑。然而&#xff0c;這種相似性純屬巧合——或者說是一種營銷策略的產物。 JavaScript誕…

【文獻分享】Machine learning models提供數據和代碼

數據輸入及前期信息&#xff1a;ChronoGauge 需要一個基因表達矩陣&#xff0c;其中包括來自多個時間進程 RNA-測序實驗的觀測數據&#xff0c;用于訓練&#xff0c;并且需要有關每個基因在連續光照&#xff08;LL&#xff09;條件下經過光暗&#xff08;LD&#xff09;周期調整…

PHP MySQL Delete 操作詳解

PHP MySQL Delete 操作詳解 引言 在Web開發中&#xff0c;數據庫是存儲和管理數據的重要工具。PHP作為一種流行的服務器端腳本語言&#xff0c;與MySQL數據庫結合使用可以高效地處理數據。本文將詳細介紹PHP中如何使用DELETE語句刪除MySQL數據庫中的數據。 什么是DELETE語句&am…