山东工艺美术学院网站建设公司,wordpress页面转文章,做网站哪家公司比较好,直播开放平台入驻Unity基础-范围检测
七、范围检测
概述
范围检测#xff08;Overlap Detection#xff09;是Unity物理系统中的一种常用技术#xff0c;用于在不产生实际物理碰撞或不依赖刚体的情况下#xff0c;判断指定区域内是否存在或有哪些碰撞器。它常用于游戏中瞬时的攻击范围判…Unity基础-范围检测
七、范围检测
概述
范围检测Overlap Detection是Unity物理系统中的一种常用技术用于在不产生实际物理碰撞或不依赖刚体的情况下判断指定区域内是否存在或有哪些碰撞器。它常用于游戏中瞬时的攻击范围判断、技能效果判定、区域触发等场景例如
玩家在前方5米处释放一个范围检测此范围内的对象是否受到伤害。玩家攻击在前方1米圆形范围内检测对象是否受到伤害。类似这种并没有实体物体只是想检测在指定某一范围是否让敌方受到伤害时便可以使用。
简而言之在指定位置进行范围判断我们可以得到处于指定范围内的对象目的是对这些对象进行处理比如受伤、减血等。
必备条件与注意点
必备条件想要被范围检测到的对象必须具备碰撞器Collider注意点 范围检测相关API是瞬时的只有当执行该代码时才进行一次范围检测。范围检测相关API并不会真正产生一个碰撞器它仅仅是进行碰撞判断计算。
层级 (LayerMask)
在进行范围检测时我们通常需要检测指定层级Layer上的物体以避免检测到不相关的对象提高效率和精确性。Unity使用位运算来表示层级信息。
通过名字得到层级编号LayerMask.NameToLayer(LayerName)层级编号是0~31刚好32位是一个int数。每一个编号都代表二进制的一位。通过将1左移对应层级编号可以得到该层级的二进制表示。 0 层1 0 → 0000 0000 0000 0000 0000 0000 0000 0001 ( 1)1 层1 1 → 0000 0000 0000 0000 0000 0000 0000 0010 ( 2)2 层1 2 → 0000 0000 0000 0000 0000 0000 0000 0100 ( 4)3 层1 3 → 0000 0000 0000 0000 0000 0000 0000 1000 ( 8)4 层1 4 → 0000 0000 0000 0000 0000 0000 0001 0000 ( 16)… 好处一个int就可以表示所有想要检测的层级信息。我们可以通过位运算来选择想要检测的层级。 检测单层1 LayerMask.NameToLayer(Default)检测多层(1 LayerMask.NameToLayer(Layer1)) | (1 LayerMask.NameToLayer(Layer2))检测所有层默认不传入层级参数或传入-1。
范围检测API
Unity Physics 类提供了一系列范围检测的API。它们通常分为两类
直接返回Collider[]数组的方法有GC开销例如 Physics.OverlapBox每次调用会创建一个新的数组。非分配式方法NonAlloc无GC开销例如 Physics.OverlapBoxNonAlloc需要传入一个预先分配好的Collider[]数组来存储结果推荐在频繁调用时使用。
1. 盒形范围检测 (Box Overlap)
在指定的一个立方体范围内检测所有碰撞体。 Physics.OverlapBox(center, halfExtents, orientation, layerMask) center立方体的中心点世界坐标。halfExtents立方体各边一半的长度Vector3。orientation立方体的旋转Quaternion。layerMask可选参数要检测的层级int。返回值Collider[]数组包含在该范围内的所有碰撞器。 Physics.OverlapBoxNonAlloc(center, halfExtents, results, orientation, layerMask) results预先分配好的Collider[]数组用于存储检测结果。返回值int表示实际检测到的碰撞器数量。
示例代码
// 声明一个Collider数组用于NonAlloc方法
Collider[] boxColliders new Collider[10]; // 使用OverlapBox会产生GC
Collider[] detectedColliders Physics.OverlapBox(transform.position Vector3.forward * 1, Vector3.one / 2, Quaternion.identity, 1 LayerMask.NameToLayer(Default));
if (detectedColliders.Length 0)
{Debug.Log($OverlapBox检测到 {detectedColliders.Length} 个碰撞体。);
}// 使用OverlapBoxNonAlloc无GC
int numColliders Physics.OverlapBoxNonAlloc(transform.position Vector3.forward * 1, Vector3.one / 2, boxColliders, Quaternion.identity, 1 LayerMask.NameToLayer(Default));
if (numColliders 0)
{Debug.Log($OverlapBoxNonAlloc检测到 {numColliders} 个碰撞体。);for (int i 0; i numColliders; i){Debug.Log($ - {boxColliders[i].name});}
}2. 球形范围检测 (Sphere Overlap)
在指定的一个球形范围内检测所有碰撞体。 Physics.OverlapSphere(position, radius, layerMask) position球体的中心点世界坐标。radius球体的半径。layerMask可选参数要检测的层级int。返回值Collider[]数组包含在该范围内的所有碰撞器。 Physics.OverlapSphereNonAlloc(position, radius, results, layerMask) results预先分配好的Collider[]数组。返回值int表示实际检测到的碰撞器数量。
示例代码
// 声明一个Collider数组用于NonAlloc方法
Collider[] sphereColliders new Collider[10];// 使用OverlapSphere会产生GC
Collider[] detectedSphereColliders Physics.OverlapSphere(transform.position Vector3.down * 1, 10f, 1 LayerMask.NameToLayer(Default));
if (detectedSphereColliders.Length 0)
{Debug.Log($OverlapSphere检测到 {detectedSphereColliders.Length} 个碰撞体。);
}// 使用OverlapSphereNonAlloc无GC
int numSphereColliders Physics.OverlapSphereNonAlloc(transform.position Vector3.down * 1, 10f, sphereColliders, 1 LayerMask.NameToLayer(Default));
if (numSphereColliders 0)
{Debug.Log($OverlapSphereNonAlloc检测到 {numSphereColliders} 个碰撞体。);for (int i 0; i numSphereColliders; i){Debug.Log($ - {sphereColliders[i].name});}
}3. 胶囊范围检测 (Capsule Overlap)
在指定的一个胶囊体范围内检测所有碰撞体。 Physics.OverlapCapsule(point1, point2, radius, layerMask) point1胶囊体底部半球的中心点世界坐标。point2胶囊体顶部半球的中心点世界坐标。radius胶囊体的半径。layerMask可选参数要检测的层级int。返回值Collider[]数组包含在该范围内的所有碰撞器。 Physics.OverlapCapsuleNonAlloc(point1, point2, radius, results, layerMask) results预先分配好的Collider[]数组。返回值int表示实际检测到的碰撞器数量。
示例代码
// 声明一个Collider数组用于NonAlloc方法
Collider[] capsuleColliders new Collider[10];// 使用OverlapCapsule会产生GC
Collider[] detectedCapsuleColliders Physics.OverlapCapsule(transform.position, transform.position Vector3.forward * 5, 0.5f, 1 LayerMask.NameToLayer(Default));
if (detectedCapsuleColliders.Length 0)
{Debug.Log($OverlapCapsule检测到 {detectedCapsuleColliders.Length} 个碰撞体。);
}// 使用OverlapCapsuleNonAlloc无GC
int numCapsuleColliders Physics.OverlapCapsuleNonAlloc(transform.position, transform.position Vector3.forward * 5, 0.5f, capsuleColliders, 1 LayerMask.NameToLayer(Default));
if (numCapsuleColliders 0)
{Debug.Log($OverlapCapsuleNonAlloc检测到 {numCapsuleColliders} 个碰撞体。);for (int i 0; i numCapsuleColliders; i){Debug.Log($ - {capsuleColliders[i].name});}
}实际应用示例WASD移动与多类型范围检测
这个示例提供一个PlayController脚本用于控制一个游戏对象的移动并实现三种不同形状的碰撞范围检测盒形、胶囊体和球形。移动通过Transform.Translate实现碰撞检测则利用Unity的Physics.Overlap系列方法。
核心思想
WASD 移动控制通过获取水平和垂直输入使用Transform.Translate方法实现游戏对象的移动。多种碰撞检测 Physics.OverlapBox用于检测一个指定盒子范围内的所有碰撞体。Physics.OverlapCapsule用于检测一个指定胶囊体范围内的所有碰撞体。Physics.OverlapSphere用于检测一个指定球形范围内的所有碰撞体。 LayerMask (层掩码)用于过滤碰撞检测只检测指定层上的对象提高效率和精度。
代码实现 (PlayController.cs) 点击展开/折叠 PlayController.cs 代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayController : MonoBehaviour
{// 用于存储移动向量private Vector3 movement;// 移动速度可在Inspector面板中调整public float moveSpeed 2f; // 用于存储碰撞检测结果的Collider数组预设大小为10个避免频繁GCprivate Collider[] colliders new Collider[10];// Update是MonoBehaviour的生命周期函数每帧调用一次void Update(){// 处理对象的移动逻辑HandleMovement();// 处理对象的碰撞检测逻辑HandleOverLap();}/// summary/// 处理碰撞检测逻辑。/// 根据按键J、K、L执行不同类型的范围检测。/// /summaryvoid HandleOverLap(){// J键按下时执行盒形Box范围检测if(Input.GetKeyDown(KeyCode.J)){// Physics.OverlapBox检测一个立方体范围内的所有碰撞体// transform.position transform.forward * 1检测中心点在物体前方1个单位// Vector3.one检测盒子的尺寸为1x1x1// transform.rotation检测盒子的旋转与物体当前旋转一致// 1 LayerMask.NameToLayer(Default)只检测Default层上的物体int numDetected Physics.OverlapBoxNonAlloc(transform.position transform.forward * 1, Vector3.one / 2, // 半尺寸所以实际尺寸是Vector3.onecolliders, transform.rotation, 1 LayerMask.NameToLayer(Default));// 清空数组中未检测到的部分for (int i numDetected; i colliders.Length; i){colliders[i] null;}if (numDetected 0){Debug.Log($J键盒形检测到 {numDetected} 个碰撞体);}else{Debug.Log(J键盒形检测未发现碰撞体。);}}// K键按下时执行胶囊体Capsule范围检测if(Input.GetKeyDown(KeyCode.K)){// Physics.OverlapCapsule检测一个胶囊体范围内的所有碰撞体// transform.position胶囊体的底部中心点// transform.position transform.forward * 5胶囊体的顶部中心点在物体前方5个单位// 0.5f胶囊体的半径// 1 LayerMask.NameToLayer(Default)只检测Default层上的物体int numDetected Physics.OverlapCapsuleNonAlloc(transform.position, transform.position transform.forward * 5, 0.5f, colliders, 1 LayerMask.NameToLayer(Default));// 清空数组中未检测到的部分for (int i numDetected; i colliders.Length; i){colliders[i] null;}if (numDetected 0){Debug.Log($K键胶囊形检测到 {numDetected} 个碰撞体);}else{Debug.Log(K键胶囊形检测未发现碰撞体。);}}// L键按下时执行球形Sphere范围检测if(Input.GetKeyDown(KeyCode.L)){// Physics.OverlapSphere检测一个球形范围内的所有碰撞体// transform.position Vector3.down * 1球体的中心点在物体下方1个单位// 10f球体的半径为10个单位// 1 LayerMask.NameToLayer(Default)只检测Default层上的物体int numDetected Physics.OverlapSphereNonAlloc(transform.position Vector3.down * 1, 10f, colliders, 1 LayerMask.NameToLayer(Default));// 清空数组中未检测到的部分for (int i numDetected; i colliders.Length; i){colliders[i] null;}if (numDetected 0){Debug.Log($L键球形检测到 {numDetected} 个碰撞体);}else{Debug.Log(L键球形检测未发现碰撞体。);}}// 如果检测到碰撞体则遍历并打印所有检测到的碰撞体名称// 这里使用循环来确保只处理实际检测到的数量for(int i 0; i colliders.Length; i){// 确保当前元素不为空才打印if (colliders[i] ! null) {Debug.Log($检测到碰撞体: {colliders[i].name}); }}}/// summary/// 处理移动逻辑。/// 根据WASD键的输入控制物体的移动和旋转。/// /summaryvoid HandleMovement(){// 获取水平轴A/D键或左右箭头的输入float horizontal Input.GetAxis(Horizontal);// 获取垂直轴W/S键或上下箭头的输入float vertical Input.GetAxis(Vertical);// 基于垂直输入前后移动// Time.deltaTime 用于使移动速度与帧率无关transform.Translate(Vector3.forward * vertical * moveSpeed * Time.deltaTime);// 基于水平输入左右旋转// 旋转速度可以根据moveSpeed调整或者单独设置一个rotateSpeedtransform.Rotate(Vector3.up * horizontal * moveSpeed * 50 * Time.deltaTime); // 乘以一个系数使旋转更明显}
}使用建议
性能优化在频繁进行范围检测的场景优先使用NonAlloc系列方法如OverlapBoxNonAlloc以避免每次调用都产生新的Collider[]数组从而减少GC垃圾回收开销。精确控制善用LayerMask参数来精确控制只检测特定层级的对象提高检测效率和准确性。调试可视化在开发过程中可以使用Debug.DrawBox、Debug.DrawLine等方法来可视化检测范围帮助理解和调试。触发器设置范围检测通常用于获取范围内的对象信息而不是模拟物理碰撞。确保被检测对象上的碰撞器如果设置为Is Trigger则它们不会产生物理交互。