unity射线碰撞检测及layermask中位运算符使用讲解

一、射线碰撞检测

今天探讨一下unity中射线用法的种类,经过我的查阅发现有好多呀,还有一些和射线类似功能的东东。

1、摄像机创建射线Ray

首先我们创建一个摄像机射线射线,也是常用的简单射线

 //创建射线,从摄像机位置发射射线到鼠标点击位置
 Ray m_Ray = Camera.main.ScreenPointToRay(Input.mousePosition);

然后就是判断射线是否被collider遮挡(这里的collider就是collider组件,不区分该组件是否勾选trigger),首先利用常见的Raycast创建讲解

①、Raycast和RaycastAll方法:

两个方法基本一样,先看Raycast方法:

RaycastHit m_RayHit;//声明存储射线碰撞到的对象信息结构体

if (Physics.Raycast(m_Ray, out m_RayHit, Mathf.Infinity, 1 << 
      LayerMask.NameToLayer("Cube")))
  {
      Debug.Log("Cube射线检测到:" + m_RayHit.collider.gameObject.name);
      Debug.DrawLine(m_Ray.origin, m_RayHit.point);
  }

 上面代码中Physics.Raycast(m_Ray, out m_RayHit, Mathf.Infinity, 1 << LayerMask.NameToLayer("Cube"))返回值类型为bool,就是判断是否碰撞到collider,如果碰撞到返回为真,并且将碰撞到的第一个collider信息存到RaycastHit类型的m_RayHit中,最后一个参数就是本篇文章的第二话题,我们稍后一起讲,先简单理解此处他表示只检测Layer名字为Cube的层。

第二个RaycastAll方法:

RaycastHit[] RayHits = Physics.RaycastAll(m_Ray, Mathf.Infinity, 1 << LayerMask.NameToLayer("Cube"));

if (RayHits.Length > 0)
{
     for (int i = 0; i < RayHits.Length; i++)
     {
         Debug.Log("Cubes检测到:" + RayHits[i].collider.gameObject.name);
         Debug.DrawLine(m_Ray.origin, RayHits[i].point);
     }
}

可以发现它返回的不是bool类型了,而是一个数组,是因为它检测的是这条射线碰撞到的所有collider信息,这就是两者最大的区别。

②、SphereCast方法

其实这里它可以归为一类发射射线方法(例如BoxCast()、CapsuleCast()等等),就是这个的射线不再是一条一维的线了,他可以理解为是一个方向,然后向这个方法投各种形状物体,依照SphereCast就是向某个投掷球形物体,但是有一个不好的就是他不能检测起点到半径之内的物体,也就是说发出射线的时候就已经包含在球半径内的话是不能被检测到的,下面代码样例:

if (Physics.SphereCast(m_Ray, 3,
    out m_RayHit, Mathf.Infinity, 1 << LayerMask.NameToLayer("Cube")))
{
    Debug.Log("Cube球形射线检测到:" + m_RayHit.collider.gameObject.name);
    Debug.DrawLine(m_Ray.origin, m_RayHit.point, Color.red, 3.0f);
}

这里的3就是球形半径,其实这个方法我还没想出来如果不知道原理该如何打印出来这条射线,但是如果想验证它是否真的有“粗”度了,可以利用第一个方法对比就会一目了然了?。

③、OverlapSphere方法

这个方法就完美的填补了上一个SphereCast方法中无法检测半径内的collider物体的遗憾了,它主要检测和球形物体相交或在内部的collider。不过我认为他不算是射线了,有些类似触发器,但是他检测的物体并没有触发器事件那么多的前提条件,他也是只要检测物体有collider组件即可,而且它可不仅仅只有Sphere一个形状哟,还有Box,Capsule等等吧,下面代码样例:

Collider[] HitSpheres = Physics.OverlapSphere(transform.position, 3,
                1 << LayerMask.NameToLayer("Cube"));
if (HitSpheres.Length > 0)
{
   for (int i = 0; i < HitSpheres.Length; i++)
   {
       Debug.Log("Cubes检测到:" + HitSpheres[i].gameObject.name);
   }
}

/// <summary>
/// unity事件
///绘制物体事件
/// </summary>
private void OnDrawGizmos()
{
   Gizmos.color = Color.red;
   //线框绘制
   Gizmos.DrawWireSphere(transform.position, 3);
}

 这个方法是直接返回collider数组,3同样表示球形半径,下面的OnDrawGizmos函数是unity自带的一个绘制物体函数,主要为了便于观察。

二、利用其他物体发射射线

此处和前面类似不多解释,先看代码:

根据物体创建射线。
Vector3 fwd = transform.TransformDirection(Vector3.forward);//方向
if (Physics.Raycast(transform.position, fwd, out m_RayHit))
{
     Debug.Log("物体自身发射射线:" + m_RayHit.collider.gameObject.name);
     Debug.DrawLine(transform.position, m_RayHit.point);
}

 直接利用Raycast方法,设定起点和方向即可,省去了创建Ray射线步骤。

二、layermask中位运算符

本节参考链接:https://www.cnblogs.com/leeplogs/p/9133824.html

首先说一下位运算符

按位运算符:与(&)、非(~)、或(|)、异或(^)、<<(左移)、>>(右移)。位运算符主要用来对二进制位进行操作。

与运算符:只有两个位都是1,结果才是1;

或运算符:只要两个位有一个是1,结果就是1;

非运算符:如果位为0,结果是1,如果位为1,结果是0;

异或运算符:两个操作数的位中,相同则结果为0,不同则结果为1;

左移运算符<<:左移表示乘以2,左移多少位表示乘以2的几次幂;

右移运算符>>:移动多少位表示除以2的几次幂。

 

Unity是用 int32来表示32个Layer层,int32用二进制来表示一共有32位。

0000 0000 0000 0000 0000 0000 0000 0000

31                    0

eg:

(1)Physics.Raycast(m_Ray, out m_RayHit, Mathf.Infinity, 1 << 8) ----开启Layer8

其中 <<左边的 1表示有[开启],0表示没有该layer[忽略] 。右边的2表示左移2位即是 layer2层的位置。

(2)Physics.RaycastAll(m_Ray, Mathf.Infinity, 1 << 9|1<<10) ----开启layer 9和layer 10

(3)Physics.RaycastAll(m_Ray, Mathf.Infinity, 1 << 9|0<<10) ----开启Layer9 并关闭 Layer10

(4)Physics.RaycastAll(m_Ray, Mathf.Infinity, ~(1 << 9)) ----关闭Layer9层,变0 按位取反 ~
上面列举了我在unity经常使用的一些位运算符,其他的含义已经为大家列举出来了,可以时情况而应用

<think>我们正在讨论Unity中的射线碰撞检测,特别是如何获取碰撞到的物体的图层(Layer)。根据引用内容,我们可以总结出以下关键点: 1. 使用射线检测(如Physics.Raycast)时,会返回一个RaycastHit结构体(2D中为RaycastHit2D),其中包含碰撞的详细信息。 2.RaycastHit结构体中,有一个属性是collider,通过collider我们可以获取到碰撞到的游戏物体(GameObject),然后通过游戏物体获取其图层。 3. 图层的索引(int)或名称(string)都可以获取。 具体步骤: - 进行射线检测,如果返回true,则从RaycastHit结构中获取碰撞体的游戏对象。 - 然后通过gameObject.layer获取图层的索引(整数),或者通过LayerMask.LayerToName(gameObject.layer)将索引转换为图层名称(字符串)。 根据引用[3]和[5]中的代码示例,我们可以看到在射线检测后,通过hit.collider.gameObject来访问碰撞到的游戏对象。 因此,获取图层的方法如下: 1. 执行射线检测,例如:Physics.Raycast(ray, out hit, distance, layerMask) 2. 如果射线碰撞到了物体,则可以通过hit.collider.gameObject.layer获取图层的索引(整数)。 3. 如果需要图层的名称,可以使用LayerMask.LayerToName(hit.collider.gameObject.layer)。 注意:在2D中,使用Physics2D.Raycast,返回的是RaycastHit2D结构,同样包含collider属性,因此方法相同。 示例代码(3D): ```csharp RaycastHit hit; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // 假设我们使用所有层,或者指定一个层掩码layerMask int layerMask = ~0; // 所有层 if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask)) { // 获取碰撞物体的图层索引 int layerIndex = hit.collider.gameObject.layer; Debug.Log("碰撞到的图层索引: " + layerIndex); // 获取图层名称 string layerName = LayerMask.LayerToName(layerIndex); Debug.Log("碰撞到的图层名称: " + layerName); } ``` 示例代码(2D): ```csharp RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.right, distance, layerMask); if (hit.collider != null) { int layerIndex = hit.collider.gameObject.layer; string layerName = LayerMask.LayerToName(layerIndex); Debug.Log("碰撞到的图层名称: " + layerName); } ``` 引用[4]中提到图层(Layer)用于射线检测,通过LayerMask来指定交互的图层。但这里我们关注的是如何从碰撞结果中获取图层。 因此,回答用户的问题:在Unity中获取射线碰撞检测所碰撞到的图层,可以通过碰撞信息中的collider.gameObject.layer获取图层索引,然后可转换为图层名称。 §§相关问题§§ 1. 如何设置Unity中物体的图层? 2. 如何在射线检测中排除特定图层? 3. Unity射线检测的常见应用有哪些? </think>在Unity中获取射线碰撞检测碰撞到的图层(Layer)可以通过以下步骤实现,关键方法是访问`RaycastHit.collider.gameObject.layer`属性: ### 核心方法 1. **执行射线检测**: ```csharp Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, 100f, layerMask)) { // 检测到碰撞 } ``` 2. **获取碰撞物体的图层**: ```csharp int layerIndex = hit.collider.gameObject.layer; // 获取图层索引 string layerName = LayerMask.LayerToName(layerIndex); // 索引转图层名 Debug.Log($"碰撞图层索引: {layerIndex}, 名称: {layerName}"); ``` --- ### 完整示例代码 ```csharp void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; int layerMask = 1 << LayerMask.NameToLayer("Enemy"); // 仅检测"Enemy"层 if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask)) { // 获取图层信息 int collidedLayer = hit.collider.gameObject.layer; string layerName = LayerMask.LayerToName(collidedLayer); Debug.Log($"碰撞对象图层: {layerName}(ID:{collidedLayer})"); Debug.Log($"碰撞对象名称: {hit.collider.gameObject.name}"); } } ``` --- ### 关键技术点说明 1. **图层索引与名称转换**: - `gameObject.layer` 返回整数型图层索引(0~31) - `LayerMask.LayerToName(int)` 将索引转为可读名称 - `LayerMask.NameToLayer(string)` 反向转换(如引用[3][^3]) 2. **图层掩码(LayerMask)应用**: - 通过位运算筛选特定图层(如引用[5][^5]): ```csharp int layerMask = 1 << 9; // 仅检测第9层 int layerMask = ~(1 << 3); // 排除第3层 ``` 3. **2D/3D通用性**: - 方法同样适用于`Physics2D.Raycast`(引用[1][^1]) - `RaycastHit2D` 结构体包含等效的`collider`属性 > 提示:图层系统是Unity碰撞检测的核心过滤机制,合理使用层掩码可大幅提升检测效率[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值