浅析UnityAPI【Physics.OverlapSphere】及其技巧

1、API介绍

本函数一旦被调用,将会返回以参数1为原点和参数2为半径的球体内“满足一定条件”的碰撞体集合,此时我们把这个球体称为 3D相交球。

2、函数声明

Physics.OverlapSphere(Vector3 position, float radius)
Physics.OverlapSphere(Vector3 position, float radius, int layerMask)

【形参】
//position  3D相交球的球心
//radius    3D相交球的球半径
//layerMask 在某个Layer层上进行碰撞体检索,例如当前选中Player层,则只会返回周围半径内               
//          Layer标示为Player的GameObject的碰撞体集合

【返回值】
Collider[];

3、用法解析

浅析UnityAPI【Physics.OverlapSphere】及其技巧_第1张图片

如图1,我想通过OverlapSphere函数获取到,以最右边【OverlapSphereCube】周围的一定半径内的碰撞体。我应该这样做。

1)、先测试【Physics.OverlapSphere(Vector3 position, float radius) 函数】

准备工作:

(1)5个CUBE:【OverlapSphereCube、Cube1、Cube2、Cube3、Cube4】(均带碰撞体组件)

//代码
public Transform  OverlapSphereCube; 
public float SearchRadius;
//假设 SearchRadius表示的相交球的检测半径值,大到足够覆盖到Cube4

void Start()
{
    SearchNearUnits();
}

public void SearchNearUnits()
{
  Collider[] colliders = Physics.OverlapSphere(OverlapSphereCube.position, SearchRadius);

  if(colliders.Length <= 0return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}

【解释】
因为测试,所以笔者只在start的时候调用一次SearchNearUnits()函数而已,如果你需要一直更新SearchNearUnits()函数里面的colliders的话,你应该把它放到Update()等循环调用的函数里面去从而达到更新周围碰撞体集合的作用。

【猜测结果】
函数执行结束后,你觉得输出Cube的名字是4个还是5个呢?

【实际结果】
浅析UnityAPI【Physics.OverlapSphere】及其技巧_第2张图片

哈哈,是5个,猜4个的去面壁,在开头函数介绍的时候本文就说过了,返回一定半径内“满足一定条件”的碰撞体集合。因为我们这里没有用到LayerMask这个参数(后面会讲),所以默认是返回一定半径内所有的碰撞体集合,当然也包括自身了。所以实际上应该是5个才对。

2)、现在测试【Physics.OverlapSphere(Vector3 position, float radius, int layerMask) 函数】

准备工作:

(1)5个CUBE:【OverlapSphereCube、Cube1、Cube2、Cube3、Cube4】(均带碰撞体组件)

(2)假设Cube1、Cube2的Layer是Team1

(3)假设Cube3、Cube4的Layer是Team2

(4)实在抱歉!对于LayerMask不了解的朋友,请自行百度关键词,后续作者更新Layer文章的话会在这里贴教程连接.

//代码
public Transform  OverlapSphereCube; 
public float SearchRadius;
//假设 SearchRadius表示的相交球的检测半径值,大到足够覆盖到Cube4

void Start()
{
    SearchNearUnits();
}

public void SearchNearUnits()
{
  Collider[] colliders = Physics.OverlapSphere(OverlapSphereCube.position, SearchRadius,1 << LayerMask.NameToLayer("Team1"));

  if(colliders.Length <= 0return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}

【解释】
(1)与之前有所不同的是,这次在调用OverlapSphere()函数时,多写了一个参数,这个是针对Unity Layer层的参数,我们在准备工作里面已经将Cube分为了2个层,分别是Team1Team2,在代码里面,我们检查的是Team1层的碰撞体集合。
(2)LayerMask.NameToLayer(string layerName)的作用是将指定层的“名称”字符串转换成
对应的Int 型的LayerMask码。

【猜测结果】
函数执行结束后,这次又会输出几个呢?

【实际结果】
浅析UnityAPI【Physics.OverlapSphere】及其技巧_第3张图片

(1)正确答案是2个,因为在准备工作里面,我们只设定了Cube1和Cube2在Team1层里面,所以OverlapSphereCube、Cube3、Cube4都不会被检索到。

(2)同理,若代码部分改成 1 << LayerMask.NameToLayer(“Team2”)的话,答案如下图。
浅析UnityAPI【Physics.OverlapSphere】及其技巧_第4张图片

4、技巧

技巧1:获取一定距离内最近的一个敌人单位

提问:如果我想获取一定半径内离“我”最近的单位怎么办?是不是要先用OverlapSphere()获取到一定半径内的碰撞体集合,然后再对它们到“我”的距离做一个排序并且取距离最小的那个单位啊?

[2017.8.11更新]:
感谢评论区的“yifei5917”指正,在笔者测试的DEMO里面技巧1是没有错误的,但是当附近单位多了以后就不会有“自动排序”的效果的。为了安全起见,请大家在函数返回“满足条件”的碰撞体集合后,对这个集合进行一次遍历排序操作。谢谢!

答案:完全不用,OverlapSphere()获取到碰撞体集合后,就已经帮你排序好了(2017.8.11这条技巧有误,多单位情况下这条技巧失效)(该函数返回的碰撞体集合,调用时,索引越大说明这个索引所指向的碰撞体离“我”的距离越远),但是你得知道这里面有几个坑。看下面的图找规律吧。

情景1
使用如下代码

//代码
public Transform  OverlapSphereCube; 
public float SearchRadius;
//假设 SearchRadius表示的相交球的检测半径值,大到足够覆盖到Cube4

void Start()
{
    SearchNearUnits();
}

public void SearchNearUnits()
{
  Collider[] colliders = Physics.OverlapSphere(OverlapSphereCube.position, SearchRadius);

  if(colliders.Length <= 0return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}

浅析UnityAPI【Physics.OverlapSphere】及其技巧_第5张图片
从上图我们知道,Cube1是距离OverlapSphereCube最近的。

从代码我们可知,此时Cube1等价于colliders[1].gameObject,

然后就认为1这个索引永远都是距离我最近的(前提是索引不越界)。

笔者想要表达的是,在上面这种情况下,索引为1永远指向离我们最近的敌人单位是没有错的。

但是如果OverlapSphereCube(假设OverlapSphereCube是玩家的GameObject),下面还有个Cube5并且带碰撞体(离玩家最近),毕竟玩家身上可能有很多个碰撞体。

这个时候我们用这个方法找离我们最近的敌人就有点麻烦,并且还会发生错误,因为此时索引1就不是指向离我们最近的Cube1,而是Cube5。

这个方法的解决方案是,先确定好“玩家”的这个GameObject身上有多少个碰撞体,我假设有X=5个,那么正确的索引应该是5(数组从0开始算)。即colliders[5].gameObject指向最近的单位。这里的X是动态改变的,可以通过定义一个变量来控制。

是不是觉得上面的方法很麻烦,又容易出错?

还有个更简介的方法,那就是使用LayerMask的功能了。

情景2
我们先把Cube1、2、3、4的Layer设置为Enemys

使用如下代码

//代码
public Transform  OverlapSphereCube; 
public float SearchRadius;
//假设 SearchRadius表示的相交球的检测半径值,大到足够覆盖到Cube4

void Start()
{
    SearchNearUnits();
}

public void SearchNearUnits()
{
  Collider[] colliders = Physics.OverlapSphere(OverlapSphereCube.position, SearchRadius,1 << LayerMask.NameToLayer("Enemys"));

  if(colliders.Length <= 0return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}

看下图
浅析UnityAPI【Physics.OverlapSphere】及其技巧_第6张图片

这个时候,我们就可以放心的说,索引0永远指向离我最近的敌人了,因为敌人已经通过LayerMask被标示为Enemys了,所以上面的代码只会在敌人的Layer里面找,我才不管你玩家挂载多少碰撞体呢。。。。当然前提是,你的玩家身上的碰撞体的Layer不能设置成Enemys,不然出错别来找我= =!

技巧2:实现AOE(范围)伤害,例如手雷爆炸的范围伤害

思路:下面的函数应该在手雷爆炸瞬间被调用,然后函数会获取到手雷一定半径内“满足一定条件”的单位进行杀伤。

public void Grenade_AOE_Damage(Transform _grenade, float _AOE_radius, float _damage)
{

    //获取手雷_AOE_radius范围内所有的碰撞体(敌人)
    Collider[] colliders =  Physics.OverlapSphere(_grenade.position, _AOE_radius, 1 << LayerMask.NameToLayer("Enemys"));

    //遍历范围内所有敌人并给予伤害
    for (int i = 0; i < colliders.Length; i++)
    {
        //获取生命脚本组件,调用伤害函数
        colliders[i].gameObject.GetComponent().GetDamage(_damage);
    }
}

//函数执行结束后,就已经对手雷一定范围内所有敌人造成伤害了,如果想让手雷对友军也有用的话,
//请调用带有2个参数的OverlapSphere()函数。

声明:笔者知识有限,如果有什么不对的地方,还希望大家指正,一起交流学习^_^。

本节到此为止了,谢谢大家!。

你可能感兴趣的:(【Unity3D,原创笔记系列】)