目录
射线在游戏开发中,应用非常广泛,所以掌握射线非常重要。
列如:判断角色是否着地,可以向角色脚下发射射线,判断是否接触地面。
注意:各种射线检测都是以物理系统为基础的。射线需要与碰撞体和触发器配合才能发挥出作用。
射线与视线一样会被障碍物阻挡并且射线不仅可以有长度,还可以有粗细和形状,列如球形射线,盒子射线和胶囊体射线。方法都大同小异。
射线的编程方法
常用的直线射线类型用类型Ray表示,Ray包含了起点origin跟方向direction的定义,起点和方向都用Vector3类型表示,前者是一个坐标,后者是一个表示方向的向量。
Vector3 origin = transform.position;
Vector3 direction = Vector3.down;
Ray ray = new Ray(origin, direction);
在游戏中发射一条射线,最常用的是physics.Raycast()和physics.RaycastAll(),前者只能检测一个碰撞体而被返回,后者可以一起检测多个碰撞体。我们先讲讲Raycast(),常用的重载方法如下
bool raycast =Physics.Raycast(ray.origin,ray.direction,out RaycastHit hitInfo,float maxDistance,int layerMask);
这里有五个参数,第一个是发射起点,第二个是发射方向,第三个是发射距离,第四个是射线碰撞信息,第五个是发射图层,只检测指定图层,而忽略其他图层,最后返回值bool是是否击中某个碰撞体或者触发器。其中,前两个参数是必须,后三个参数根据我们的需要可加可不加。
射线的碰撞信息
我们再重点讲讲第三个参数RaycastHit,若没有这个参数,我们的返回值仅仅只是“是否碰到了物体”,而无法确定碰撞点在哪,也不知道碰撞体是哪一个。射线检测其实有着非常丰富的碰撞信息,如可以获得其碰撞坐标,信息,以及法线。这些丰富的信息都被保存在RaycastHit结构体中。
综合用法演示如下:
//声明变量,用于保存信息
RaycastHit hitInfo;
//发射射线,起点是当前物体位置,方向是世界前方
if(Physics.Raycast(transform.position,Vector3.forward,out hitInfo))
{
//如果碰到物体,运行此处,返回的是bOOl值
//获取碰撞点坐标
Vector3 point = hitInfo.point;
//获取对方碰撞体组件
Collider coll = hitInfo.collider;
//获取对方的Transgorm组件
Transform trans = hitInfo.transform;
//获取对方的物体名称
string name = hitInfo.collider.name;
//获取碰撞点的法线向量
Vector3 normal = hitInfo.normal;
}
注意,2D游戏跟3D游戏获取碰撞体的方式有些不一样,具体对比如下
//声明变量,将碰撞信息直接赋值给变量
RaycastHit2D hitInfo=Physics2D.Raycast(transform.position,Vector3.forward);
//发射射线,起点是当前物体位置,方向是世界前方
if(hitInfo.collider != null)
{
//如果检测的碰撞体不为空,运行此处。
//获取碰撞点坐标
Vector3 point = hitInfo.point;
//获取对方碰撞体组件
Collider coll = hitInfo.collider;
//获取对方的Transgorm组件
Transform trans = hitInfo.transform;
//获取对方的物体名称
string name = hitInfo.collider.name;
//获取碰撞点的法线向量
Vector3 normal = hitInfo.normal;
}
穿过多个物体的射线
前面我们提到了Raycast()跟RaycastAll的区别,,区别是前者只能检测一个碰撞体便返回,后者是能可以获取到射线沿途碰到的所有信息(在指定图层内)。我们以2D为例,差异看图
如图,圆形物体发射的射线被绿色碰撞体阻挡,无法到达蓝色碰撞体,所以此时射线只能检测绿色图层,但不能检测蓝色碰撞体,那我们怎么能即检测绿色碰撞体又能检测蓝色碰撞体。这时就需要我们使用Physics,RaycastAll(),该函数的返回值是RaycastHit数组。重载方式如下:
RaycastHit[] hit = Physics.RaycastAll(Vector3 origin,Vector3 direction,float maxDistance,int LayerMask);
利用此函数,我们便可以实现同时检测绿色,蓝色两个碰撞体,效果如下
例子代码,仅供参考
RaycastHit2D[] hitInfo = Physics2D.RaycastAll(ray.origin,ray.direction,10f,LayerMask.GetMask("ground","war"));
for(i=0;i<hitInfo.Length;i++)
{
Debug.DrawLine( ray.origin,hitInfo[i].point ,Color.red);
Debug.Log(hitInfo[i].collider.name);
}
获取图层的方法
其中介绍一一个小知识,这里获取图层的方法是LayerMask.GetMask(),这里可获得一个图层或者多个图层
例如获得Ground,player,obstacle者三个层,代码如下
int mask = layerMask.GetMask("ground","player","obstacle");
区域覆盖型射线(Overlap)
有时候需要检测一个空间的范围,例如炸弹爆炸时,范围十米之内的物体都会受到波及,那么这里需要的不是一条射线,而是一个半径为10米的球形区域。物理系统也提供了这类函数,它们均已Physics。Overlap开头,这里以球形区域为例:
Collider[] collider = overlapSphere(Vector3 position,float radius,int LayerMask);
第一个参数是位置,第二个参数是范围距离,第三个是图层,且该返回值是collider。
射线的调试方法
利用Debug调试
在游戏开发中,射线的调试方法非常重要,可以将看不见的射线以可视化的形式表现出来,方便我们查看数据是否正确。这个方法是使用Debug.DrawLine()函数和Debug.DrawRay().
效果图可以看上面,都有涉及使用到。需要说明的是在默认的情况下,该辅助线仅在编辑器的场景窗口可见,如果要在game窗口看到,则需要单机Game窗口右上角的Gizmos开关。
利用Gizmos
调用OnDrawGizmos()函数或OnDrawGizmosSelected,它们同样都可以用来绘制辅助图标。区别在于
函数OnDrawGizmos()在程序一运行就执行,之后每帧都在执行,
函数OnDrawGizmosSelected()在鼠标点击到脚本挂载的物体的身上的时候运行,不管有多少父类对象,它都会执行。
这里推荐使用OnDrawGizmosSelected()
范例如下
private void OnDrawGizmosSelected()
{
Gizmos.DrawLine(Vector3 from, Vector3 to);
Gizmos.DrawRay((Vector3 from, Vector3 direction);
}
可在scence窗口查看辅助线,同样想在game窗口可以看到,则需要单机Game窗口右上角的Gizmos开关。