unity: Physics.OverlapSphere

浅析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[];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、用法解析

图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 <= 0) return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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

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

【实际结果】 
图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 <= 0) return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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

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

【实际结果】 
图3

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

(2)同理,若代码部分改成 1 << LayerMask.NameToLayer(“Team2”)的话,答案如下图。 
图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 <= 0) return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

图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 <= 0) return ;

  for (int i = 0; i < colliders.Length; i++)
      print(colliders[i].gameObject.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

看下图 
这里写图片描述

这个时候,我们就可以放心的说,索引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<Health>().GetDamage(_damage);
    }
}

//函数执行结束后,就已经对手雷一定范围内所有敌人造成伤害了,如果想让手雷对友军也有用的话,
//请调用带有2个参数的OverlapSphere()函数。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

 

unity3d 关于 Physics2D.OverlapCircleNonAllocTest()

09-14
http://blog.csdn.net/microsoftMSDNNET/article/details/48442745 测试工程代码附件。 unit3d 4.6版本。 Update()函数里面有

  • qq826364410
    Mr_传奇:文章中if语句中文括号,还有Physics.OverlapSphere方法,倒数第二个逗号也是中文逗号。。。1年前
    1
  • u013826918
    款冬:百度了一下Physics.OverlapSphere 发现只有你的csdn有写到,真的挺受用的,感谢!3年前
    1
    • u013700908
      雨落風輕回复:文章里面的技巧1 在多单位情况下可能没有用。最好附加一个排序算法在末尾。3年前
  • qq_34110694
    爱吃肉的狐狸:
    1.  
      Collider[] Mcollider = Physics.OverlapSphere(transform.position, SearchMyrun,
    2.  
      1 &lt;< LayerMask xss=removed>Vector3.Distance(transform.position,Mcollider[i].transform.position))
    3.  
      {
    4.  
      Target = Mcollider[i].transform.position;
    5.  
      Distance = Vector3.Distance(transform.position, Mcollider[i].transform.position);
    6.  
      }
    7.  
      }
    2年前
  • qq_31807711
    qq_31807711:哥 是返回在球体内的所有物体的碰撞器吧。。3年前
    • u013700908
      雨落風輕回复:Collider【】 你想这么理解都行 哈哈。我习惯喊成碰撞体了。3年前
  • yifei5917
    yifei5917:我测试了几遍,好像并没有按距离进行顺序的返回.4年前
    • u013700908
      雨落風輕回复:感谢你提出的问题,经测试,多单位情况下 这个技巧确实不准确,但是博客里面的结果是没有做过任何处理的。。3年前
    • u013700908
      雨落風輕回复:按照上面的例子来说的话 却是是顺序的,我晚点测试下复杂情况。4年前
  • qimuak47
    七目:好,LayerMask一开始直接设置数字,结果不生效,居然要1<<才行……4年前
    • qq_37055562
      Rainbow丶Six回复雨落風輕:哦哦,懂了 谢谢大佬2年前
    • u013700908
      雨落風輕回复:恩,是的,1<<可以理解为启用的意思,0<<就是禁用了。就这么理解吧。比较方便4年前

posted on 2020-08-26 19:37  ZhYQ_note  阅读(884)  评论(0)    收藏  举报

导航