目录
[1. 盒状范围检测 Physics.OverlapBox](#1. 盒状范围检测 Physics.OverlapBox)
[2. 球形范围检测 Physics.OverlapSphere](#2. 球形范围检测 Physics.OverlapSphere)
[3. 胶囊范围检测 Physics.OverlapCapsule](#3. 胶囊范围检测 Physics.OverlapCapsule)
[4. 盒状检测 NonAlloc 版](#4. 盒状检测 NonAlloc 版)
[5. 球形检测 NonAlloc 版](#5. 球形检测 NonAlloc 版)
[6. 胶囊检测 NonAlloc 版](#6. 胶囊检测 NonAlloc 版)
[QueryTriggerInteraction 参数详解](#QueryTriggerInteraction 参数详解)
[1. QueryTriggerInteraction.UseGlobal](#1. QueryTriggerInteraction.UseGlobal)
[2. QueryTriggerInteraction.Collide](#2. QueryTriggerInteraction.Collide)
[3. QueryTriggerInteraction.Ignore](#3. QueryTriggerInteraction.Ignore)
[LayerMask 在范围检测中的深度解析](#LayerMask 在范围检测中的深度解析)
[1. 层级系统基础](#1. 层级系统基础)
[2. LayerMask 本质](#2. LayerMask 本质)
[1. 层名拼写错误(静默失败)](#1. 层名拼写错误(静默失败))
[2. 位运算优先级错误](#2. 位运算优先级错误)
[3. 掩码值为0(不检测任何层)](#3. 掩码值为0(不检测任何层))
[4. 混淆层索引和掩码值 这个最常见](#4. 混淆层索引和掩码值 这个最常见)
[高效 NonAlloc 版本](#高效 NonAlloc 版本)
[NonAlloc 版本特有参数](#NonAlloc 版本特有参数)
[LayerMask 操作指南](#LayerMask 操作指南)
在前面的内容中,我们学习了关于碰撞的检测相关,今天我们来看看指定范围的检测,这个检测是什么呢?就是瞬时检测指定空间内的碰撞器对象(无实体碰撞效果),适用于技能攻击、区域触发等场景。
那么被检测的对象需要具备些什么条件呢?被检测对象 必须挂载碰撞器(Collider)(无需 Rigidbody)。
一、关于范围检测的主要API:
1. 盒状范围检测 Physics.OverlapBox
参数相关:
cpp
Collider[] Physics.OverlapBox(
Vector3 center, // 盒子中心点(世界坐标)
Vector3 halfExtents, // 盒子三边尺寸(半长,非全尺寸)
Quaternion orientation, // 盒子旋转角度
int layerMask = AllLayers, // 层级掩码(默认所有层)
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:检测范围内的所有碰撞器组成的数组(无碰撞时返回空数组)
例如:
cpp
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
参数相关:
cpp
Collider[] Physics.OverlapSphere(
Vector3 position, // 球心位置(世界坐标)
float radius, // 球体半径
int layerMask = AllLayers, // 层级掩码
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:球体内的所有碰撞器数组
cpp
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
参数相关:
cs
Collider[] Physics.OverlapCapsule(
Vector3 point0, // 胶囊体底部球心
Vector3 point1, // 胶囊体顶部球心
float radius, // 胶囊半径
int layerMask = AllLayers, // 层级掩码
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
例如:
cs
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 版
cpp
int Physics.OverlapBoxNonAlloc(
Vector3 center, // 盒子中心点
Vector3 halfExtents, // 盒子半尺寸(长/宽/高各一半)
Collider[] results, // 结果存储数组(预分配)
Quaternion orientation = Quaternion.identity, // 旋转角度
int layerMask = AllLayers, // 层级掩码
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:实际检测到的碰撞器数量(非数组长度)
5. 球形检测 NonAlloc 版
cpp
int Physics.OverlapSphereNonAlloc(
Vector3 position, // 球心位置
float radius, // 球体半径
Collider[] results, // 结果存储数组
int layerMask = AllLayers, // 层级掩码
QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal
)
返回值:实际检测到的碰撞器数量
6. 胶囊检测 NonAlloc 版
cpp
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
行为:忽略所有触发器
说明:无论项目全局设置如何,本次检测都会跳过所有触发器
推荐场景:
只关心实体碰撞时(如攻击检测、物理碰撞)
避免触发器干扰检测结果时
示例:不同参数的效果
注: :这种写法叫做可选参数。
cpp
// 检测所有碰撞体(包括普通碰撞器和触发器)
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层
可以在这里进行创建层级
通过代码来获取层级:
cpp
// 单层级
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就合并了。利用,位与,位或进行操作。
层级选择:精准定位目标层
基础选择方法
cs
// 选择单个层级
LayerMask enemyMask = 1 << LayerMask.NameToLayer("Enemy");
// 选择多个层级
LayerMask combatMask = (1 << LayerMask.NameToLayer("Enemy")) |
(1 << LayerMask.NameToLayer("Boss"));
高级选择方式
cpp
// 使用 GetMask 更简洁
LayerMask environmentMask = LayerMask.GetMask("Ground", "Water", "Wall");
层级合并:组合检测
基本合并技巧
cpp
// 创建基础掩码
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");
多掩码组合
cpp
// 定义不同类别的掩码
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;
层级排除:精准过滤
cpp
// 所有层
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. 层名拼写错误(静默失败)
cs
// 错误:层名拼错无提示
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. 位运算优先级错误
cs
// 错误:运算优先级问题
LayerMask wrongMask = 1 << 8 | 1 << 9; // 实际是 (1 << 8) | (9)!
// 正确:使用括号明确优先级
LayerMask correctMask = (1 << 8) | (1 << 9);
3. 掩码值为0(不检测任何层)
cpp
// 常见于动态构建掩码失败
LayerMask emptyMask = 0; // 不检测任何层
// 防护:添加默认层
if (mask == 0)
{
mask = 1 << 0; // 至少包含默认层
Debug.LogWarning("Empty layer mask, using default layer");
}
4. 混淆层索引和掩码值 这个最常见
cpp
// 错误:传入层索引而非掩码
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, ...) |
牢记用半长尺寸 |