????????
目錄
一、關于范圍檢測的主要API:
1. 盒狀范圍檢測 Physics.OverlapBox
2. 球形范圍檢測 Physics.OverlapSphere
3. 膠囊范圍檢測 Physics.OverlapCapsule
4. 盒狀檢測 NonAlloc 版
5. 球形檢測 NonAlloc 版
6. 膠囊檢測 NonAlloc 版
二、關于API中的兩個重點參數
QueryTriggerInteraction 參數詳解?
1. QueryTriggerInteraction.UseGlobal
2. QueryTriggerInteraction.Collide
3. QueryTriggerInteraction.Ignore
LayerMask 在范圍檢測中的深度解析
1. 層級系統基礎
2. LayerMask 本質
層級選擇:精準定位目標層
基礎選擇方法
高級選擇方式
?層級合并:組合檢測
基本合并技巧
層級排除:精準過濾?
常見錯誤:
1. 層名拼寫錯誤(靜默失敗)
2. 位運算優先級錯誤
3. 掩碼值為0(不檢測任何層)
4. 混淆層索引和掩碼值 這個最常見
三、總結
?基本范圍檢測函數
高效 NonAlloc 版本
?關鍵參數詳解表
通用參數(所有檢測函數)
盒狀檢測特有參數
球形檢測特有參數
膠囊檢測特有參數
NonAlloc 版本特有參數
?LayerMask 操作指南
?高頻錯誤及解決方案
????????在前面的內容中,我們學習了關于碰撞的檢測相關,今天我們來看看指定范圍的檢測,這個檢測是什么呢?就是瞬時檢測指定空間內的碰撞器對象(無實體碰撞效果),適用于技能攻擊、區域觸發等場景。
? ? ? ? 那么被檢測的對象需要具備些什么條件呢?被檢測對象?必須掛載碰撞器(Collider)(無需 Rigidbody)。
一、關于范圍檢測的主要API:
1. 盒狀范圍檢測 Physics.OverlapBox
參數相關:
Collider[] Physics.OverlapBox(Vector3 center, // 盒子中心點(世界坐標)Vector3 halfExtents, // 盒子三邊尺寸(半長,非全尺寸)Quaternion orientation, // 盒子旋轉角度int layerMask = AllLayers, // 層級掩碼(默認所有層)QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:檢測范圍內的所有碰撞器組成的數組(無碰撞時返回空數組)
例如:
void CheckAttackRange()
{// 在玩家前方1米處創建2x2x2的檢測盒Vector3 center = transform.position + transform.forward;Vector3 size = new Vector3(2, 2, 2);int enemyLayer = 1 << LayerMask.NameToLayer("Enemy");Collider[] hits = Physics.OverlapBox(center,size / 2, // 注意:參數是半長尺寸transform.rotation,enemyLayer,QueryTriggerInteraction.Ignore);foreach (Collider col in hits){Enemy enemy = col.GetComponent<Enemy>();if(enemy != null) enemy.TakeDamage(10);}// 調試繪制Debug.DrawLine(transform.position, center, Color.red, 0.5f);
}
實際上你看不到他的檢測范圍的,你要自己想象。?
?
2. 球形范圍檢測 Physics.OverlapSphere
參數相關:
Collider[] Physics.OverlapSphere(Vector3 position, // 球心位置(世界坐標)float radius, // 球體半徑int layerMask = AllLayers, // 層級掩碼QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:球體內的所有碰撞器數組?
void DetectNearbyItems()
{// 檢測周圍5米內的可收集物品int itemLayer = LayerMask.GetMask("Collectibles");float detectRadius = 5f;Collider[] items = Physics.OverlapSphere(transform.position,detectRadius,itemLayer);foreach (Collider item in items){item.GetComponent<Collectible>().Highlight();}// 調試繪制Gizmos.color = Color.cyan;Gizmos.DrawWireSphere(transform.position, detectRadius);
}
這個球形的和上面的一樣,就只是參數不同而已,形狀不同,效果都是碰撞檢測,還有下面的膠囊檢測也是如此。?
3. 膠囊范圍檢測 Physics.OverlapCapsule
參數相關:
Collider[] Physics.OverlapCapsule(Vector3 point0, // 膠囊體底部球心Vector3 point1, // 膠囊體頂部球心float radius, // 膠囊半徑int layerMask = AllLayers, // 層級掩碼QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
例如:
void CheckCharacterHit()
{// 檢測玩家角色碰撞(高度2米,半徑0.5米)Vector3 feetPos = transform.position;Vector3 headPos = feetPos + Vector3.up * 2;float charRadius = 0.5f;int playerLayer = LayerMask.GetMask("Player");Collider[] hits = Physics.OverlapCapsule(feetPos,headPos,charRadius,playerLayer);if (hits.Length > 0){Debug.Log("Player character hit detected!");}
}
?????????上面的三個API只是最基本的范圍檢測,他們還有好兄弟。你不需要將返回值碰撞器數組存下來,可以直接在外部創建一個數組,然后裝入這個數組中即可:
4. 盒狀檢測 NonAlloc 版
int Physics.OverlapBoxNonAlloc(Vector3 center, // 盒子中心點Vector3 halfExtents, // 盒子半尺寸(長/寬/高各一半)Collider[] results, // 結果存儲數組(預分配)Quaternion orientation = Quaternion.identity, // 旋轉角度int layerMask = AllLayers, // 層級掩碼QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:實際檢測到的碰撞器數量(非數組長度)?
5. 球形檢測 NonAlloc 版
int Physics.OverlapSphereNonAlloc(Vector3 position, // 球心位置float radius, // 球體半徑Collider[] results, // 結果存儲數組int layerMask = AllLayers, // 層級掩碼QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:實際檢測到的碰撞器數量?
6. 膠囊檢測 NonAlloc 版
int Physics.OverlapCapsuleNonAlloc(Vector3 point0, // 膠囊底部球心Vector3 point1, // 膠囊頂部球心float radius, // 膠囊半徑Collider[] results, // 結果存儲數組int layerMask = AllLayers, // 層級掩碼QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:實際檢測到的碰撞器數量
使用手段都是和前三個差不多的
NonAlloc 方法核心優勢
特性 | 基礎方法 (OverlapX) | NonAlloc 方法 (OverlapXNonAlloc) |
---|---|---|
內存分配 | 每次調用創建新數組 | 使用預分配數組,無GC開銷 |
性能影響 | 高頻調用導致GC壓力 | 零內存分配,性能穩定 |
使用場景 | 低頻/單次檢測 | 高頻檢測(如Update中) |
返回值 | Collider[] 數組 | int (實際檢測數量) |
二、關于API中的兩個重點參數
QueryTriggerInteraction 參數詳解?
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
?是 Unity 物理檢測 API 中的一個重要參數,用于控制物理檢測如何處理觸發器(Trigger)。實際上一般默認的就OK
這個參數決定了在物理檢測(如范圍檢測、射線檢測等)中是否應該包括觸發器碰撞器:
控制檢測行為:指定是否將觸發器視為有效的碰撞對象
避免意外結果:防止觸發器干擾正常的物理檢測邏輯
靈活配置:可以覆蓋項目的全局設置
1. QueryTriggerInteraction.UseGlobal
行為:使用項目的全局物理設置
說明:檢測行為取決于 "Edit > Project Settings > Physics" 中的 "Queries Hit Triggers" 設置
推薦場景:
希望檢測行為與項目全局設置保持一致時
不特別關注觸發器處理方式時
2. QueryTriggerInteraction.Collide
行為:包含觸發器在檢測結果中
說明:無論項目全局設置如何,本次檢測都會將觸發器視為有效碰撞體
推薦場景:
需要檢測區域觸發器時(如進入安全區、收集區域)
需要響應觸發器事件時
3. QueryTriggerInteraction.Ignore
行為:忽略所有觸發器
說明:無論項目全局設置如何,本次檢測都會跳過所有觸發器
推薦場景:
只關心實體碰撞時(如攻擊檢測、物理碰撞)
避免觸發器干擾檢測結果時
示例:不同參數的效果
注: :這種寫法叫做可選參數。
// 檢測所有碰撞體(包括普通碰撞器和觸發器)
Collider[] allHits = Physics.OverlapSphere(position: transform.position,radius: 5f,layerMask: LayerMask.GetMask("Enemy", "Trap", "SafeZone"),queryTriggerInteraction: QueryTriggerInteraction.Collide
);
// 結果:包含敵人、陷阱區域、安全區// 只檢測實體碰撞體(忽略所有觸發器)
Collider[] physicalHits = Physics.OverlapSphere(position: transform.position,radius: 5f,layerMask: LayerMask.GetMask("Enemy", "Trap", "SafeZone"),queryTriggerInteraction: QueryTriggerInteraction.Ignore
);
// 結果:只包含敵人(陷阱區域和安全區是觸發器,被忽略)// 使用全局設置檢測
Collider[] globalSettingHits = Physics.OverlapSphere(position: transform.position,radius: 5f,layerMask: LayerMask.GetMask("Enemy", "Trap", "SafeZone"),queryTriggerInteraction: QueryTriggerInteraction.UseGlobal
);
// 結果:取決于項目設置中的"Queries Hit Triggers"選項
LayerMask 在范圍檢測中的深度解析
LayerMask 核心概念
1. 層級系統基礎
Unity 提供 32 個層級(0-31)
每個游戲對象分配到一個層級
層級用于邏輯分組(如:玩家、敵人、環境、UI等)
2. LayerMask 本質
32 位位掩碼(bitmask)
每個位對應一個層級(1=包含,0=排除)
示例:00000000 00000000 00000000 00000101 表示包含第0層和第2層
可以在這里進行創建層級
通過代碼來獲取層級:
// 單層級
int enemyLayer = LayerMask.NameToLayer("Enemy");
LayerMask enemyMask = 1 << enemyLayer;// 多層級組合
LayerMask enemyAndObstacleMask = (1 << LayerMask.NameToLayer("Enemy")) | (1 << LayerMask.NameToLayer("Obstacle"));// 排除特定層
LayerMask allExceptUI = ~(1 << LayerMask.NameToLayer("UI"));//或者
// 包含多個層級
LayerMask mask = LayerMask.GetMask("Enemy", "Projectile", "Destructible");// 等效于:
// (1 << LayerMask.NameToLayer("Enemy")) |
// (1 << LayerMask.NameToLayer("Projectile")) |
// (1 << LayerMask.NameToLayer("Destructible"))
接下來咱們講講為什么它會這么進行設計,和如何進行層級的選擇,合并,排除。
????????因為位運算很快,而且非常的適合做狀態合并與排除。你只需要將對應位置的數置0即排除,置1就合并了。利用,位與,位或進行操作。?
層級選擇:精準定位目標層
基礎選擇方法
// 選擇單個層級
LayerMask enemyMask = 1 << LayerMask.NameToLayer("Enemy");// 選擇多個層級
LayerMask combatMask = (1 << LayerMask.NameToLayer("Enemy")) | (1 << LayerMask.NameToLayer("Boss"));
高級選擇方式
// 使用 GetMask 更簡潔
LayerMask environmentMask = LayerMask.GetMask("Ground", "Water", "Wall");
?層級合并:組合檢測
基本合并技巧
// 創建基礎掩碼
LayerMask baseMask = LayerMask.GetMask("Player", "Enemy");// 動態添加層級
void AddLayerToMask(ref LayerMask mask, string layerName)
{int layer = LayerMask.NameToLayer(layerName);if (layer != -1) {mask |= (1 << layer); // 按位或操作}
}// 使用
AddLayerToMask(ref baseMask, "Projectile");
多掩碼組合
// 定義不同類別的掩碼
LayerMask characterMask = LayerMask.GetMask("Player", "NPC", "Enemy");
LayerMask environmentMask = LayerMask.GetMask("Ground", "Wall", "Water");
LayerMask interactableMask = LayerMask.GetMask("Chest", "Door", "Lever");// 組合掩碼
LayerMask fullDetectionMask = characterMask | environmentMask | interactableMask;
層級排除:精準過濾?
// 所有層
LayerMask allLayers = ~0; // 或 Physics.AllLayers// 排除 UI 層
int uiLayer = LayerMask.NameToLayer("UI");
LayerMask noUIMask = allLayers & ~(1 << uiLayer);// 排除多個層
int ignoreLayer1 = LayerMask.NameToLayer("IgnoreRaycast");
int ignoreLayer2 = LayerMask.NameToLayer("Water");
LayerMask filteredMask = allLayers & ~((1 << ignoreLayer1) | (1 << ignoreLayer2));
假設你要排除的層級:00000000 00000000 00000000 00001001
取反后? ? ? ? ? ? ? ? ? ? ? ? ?11111111? ?11111111? ?11111111? ?11110110
全層級? ? ? ? ? ? ? ? ? ? ? ? ?11111111? ?11111111? ?11111111? ?11111111
相與后,便排除了指定層級
常見錯誤:
1. 層名拼寫錯誤(靜默失敗)
// 錯誤:層名拼錯無提示
LayerMask mask = 1 << LayerMask.NameToLayer("Enemi"); // 返回 -1// 解決方案:驗證層名
int GetLayerSafe(string layerName)
{int layer = LayerMask.NameToLayer(layerName);if (layer == -1){Debug.LogError($"Layer '{layerName}' does not exist!");return 0; // 默認層}return layer;
}
2. 位運算優先級錯誤
// 錯誤:運算優先級問題
LayerMask wrongMask = 1 << 8 | 1 << 9; // 實際是 (1 << 8) | (9)!// 正確:使用括號明確優先級
LayerMask correctMask = (1 << 8) | (1 << 9);
3. 掩碼值為0(不檢測任何層)
// 常見于動態構建掩碼失敗
LayerMask emptyMask = 0; // 不檢測任何層// 防護:添加默認層
if (mask == 0)
{mask = 1 << 0; // 至少包含默認層Debug.LogWarning("Empty layer mask, using default layer");
}
4. 混淆層索引和掩碼值 這個最常見
// 錯誤:傳入層索引而非掩碼
int enemyLayer = LayerMask.NameToLayer("Enemy");
Physics.Raycast(..., enemyLayer); // 應該傳入 1 << enemyLayer// 正確:始終使用位掩碼
Physics.Raycast(..., 1 << enemyLayer);
三、總結
?基本范圍檢測函數
?函數名? | ?返回值? | ?核心功能? |
---|---|---|
Physics.OverlapBox | Collider[] | 檢測盒狀區域內的碰撞器 |
Physics.OverlapSphere | Collider[] | 檢測球形區域內的碰撞器 |
Physics.OverlapCapsule | Collider[] | 檢測膠囊區域內的碰撞器 |
高效 NonAlloc 版本
?函數名? | ?返回值? | ?核心優勢? |
---|---|---|
Physics.OverlapBoxNonAlloc | int | 零GC分配,適合高頻調用 |
Physics.OverlapSphereNonAlloc | int | 預分配數組,性能穩定 |
Physics.OverlapCapsuleNonAlloc | int | 返回實際碰撞數量而非數組 |
?關鍵參數詳解表
通用參數(所有檢測函數)
?參數名? | ?類型? | ?說明? |
---|---|---|
layerMask | int | 層級掩碼(位運算值),決定檢測哪些層 |
queryTriggerInteraction | enum | 觸發器處理方式:UseGlobal -用項目設置Collide -包含觸發器Ignore -忽略觸發器 |
盒狀檢測特有參數
?參數名? | ?類型? | ?說明? | ?注意事項? |
---|---|---|---|
center | Vector3 | 盒子中心點(世界坐標) | |
halfExtents | Vector3 | 三邊尺寸的一半?(非全尺寸) | 例:2x2x2盒子需傳入(1,1,1) |
orientation | Quaternion | 盒子的旋轉角度 | 默認Quaternion.identity |
球形檢測特有參數
?參數名? | ?類型? | ?說明? |
---|---|---|
position | Vector3 | 球心位置(世界坐標) |
radius | float | 球體半徑 |
膠囊檢測特有參數
?參數名? | ?類型? | ?說明? |
---|---|---|
point0 | Vector3 | 膠囊底部球心(世界坐標) |
point1 | Vector3 | 膠囊頂部球心(世界坐標) |
radius | float | 膠囊半徑 |
NonAlloc 版本特有參數
?參數名? | ?類型? | ?說明? |
---|---|---|
results | Collider[] | 預分配的碰撞器數組(避免GC分配) |
?LayerMask 操作指南
?操作類型? | ?代碼示例? | ?說明? |
---|---|---|
單層選擇 | 1 << LayerMask.NameToLayer("Enemy") | 位左移構建掩碼 |
多層合并 | LayerMask.GetMask("Enemy", "Boss") | 官方推薦的多層獲取方式 |
動態添加層 | `mask | = (1 << LayerMask.NameToLayer("Projectile"))` |
排除特定層 | LayerMask filteredMask = ~0 & ~(1 << LayerMask.NameToLayer("UI")) | 取反+位與實現層排除 |
所有層 | Physics.AllLayers ?或?~0 | 32位全1掩碼 |
?高頻錯誤及解決方案
?錯誤類型? | ?錯誤示例? | ?正確寫法? | ?解決方案? |
---|---|---|---|
層名拼寫錯誤 | NameToLayer("Enemi") | 驗證層名是否存在 | 添加層名檢查邏輯 |
位運算優先級錯誤 | `1 << 8 | 1 << 9` | `(1 << 8) |
空掩碼 | LayerMask mask = 0 | 添加默認層兜底 | mask = mask==0 ? 1<<0 : mask |
混淆層索引與掩碼 | Physics.Raycast(..., enemyLayer) | Physics.Raycast(..., 1<<enemyLayer) | 始終使用位掩碼格式 |
盒狀尺寸參數錯誤 | OverlapBox(center, fullSize, ...) | OverlapBox(center, fullSize/2, ...) | 牢記用半長尺寸 |