本函数一旦被调用,将会返回以参数1为原点和参数2为半径的球体内“满足一定条件”的碰撞体集合,此时我们把这个球体称为 3D相交球。
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,我想通过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);
}
【解释】
因为测试,所以笔者只在start的时候调用一次SearchNearUnits()函数而已,如果你需要一直更新SearchNearUnits()函数里面的colliders的话,你应该把它放到Update()等循环调用的函数里面去从而达到更新周围碰撞体集合的作用。
【猜测结果】
函数执行结束后,你觉得输出Cube的名字是4个还是5个呢?
哈哈,是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)与之前有所不同的是,这次在调用OverlapSphere()函数时,多写了一个参数,这个是针对Unity Layer层的参数,我们在准备工作里面已经将Cube分为了2个层,分别是Team1和Team2,在代码里面,我们检查的是Team1层的碰撞体集合。
(2)LayerMask.NameToLayer(string layerName)的作用是将指定层的“名称”字符串转换成
对应的Int 型的LayerMask码。
【猜测结果】
函数执行结束后,这次又会输出几个呢?
(1)正确答案是2个,因为在准备工作里面,我们只设定了Cube1和Cube2在Team1层里面,所以OverlapSphereCube、Cube3、Cube4都不会被检索到。
(2)同理,若代码部分改成 1 << LayerMask.NameToLayer(“Team2”)的话,答案如下图。
技巧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);
}
从上图我们知道,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);
}
这个时候,我们就可以放心的说,索引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()函数。
声明:笔者知识有限,如果有什么不对的地方,还希望大家指正,一起交流学习^_^。
本节到此为止了,谢谢大家!。