转载自:http://blog.csdn.net/l773575310/article/details/73251093
Unity 使用物理射线(Physics.Raycast),实现扇形(Fan-Shaped)区域碰撞检测。
参考之前的制作简单AI: Unity 有限状态机(Finite State Machine)的理解 与 实现简单的可插拔(Pluggable)AI脚本对象。 源码:GentleTank/PluggableAI/Scripts/Decision/LookDecision.cs
网上已经很多实现扇形检测的方法。大部分都是用MeshCollider实现的。而据说MeshCollider这东西很耗性能(没有亲测),所以就用射线来实现。在之前看官方教程时,作者使用了Physics.SphereCast(射出类似圆柱体)来检测坦克前方是否有Player,但我自己在制作成扇形射线时,发现相比使用Physics.SphereCast,使用多条Physics.RayCast有更好的性能,而且更精确。及射线细而密。
方法一:
实现原理:(lookAngle / 2) / lookAccurte
很简单,就是射多几条角度平均的射线。可以设置角度,精度(射线数量),来调节扇形区域的检测。每条射线夹角是总夹角处于2,再除于精度。
1. 默认是射出一条向前的射线,精度为0。
2. 设置角度为90,精度为1,就会多出两条相对正前方45度的射线。
3.设置精度为2。
实现代码
private bool Look(StateController controller)
{
var defaultStats = controller.defaultStats;
if (LookAround(controller, Quaternion.identity, Color.green))
return true;
float subAngle = (defaultStats.lookAngle /
2) / defaultStats.lookAccurate;
for (
int i =
0; i < defaultStats.lookAccurate; i++)
{
if (LookAround(controller, Quaternion.Euler(
0, -
1 * subAngle * (i +
1),
0), Color.green)
|| LookAround(controller, Quaternion.Euler(
0, subAngle * (i +
1),
0), Color.green))
return true;
}
return false;
}
static public bool LookAround(StateController controller, Quaternion eulerAnger,Color DebugColor)
{
Debug.DrawRay(controller.eyes.position, eulerAnger * controller.eyes.forward.normalized * controller.defaultStats.lookRange, DebugColor);
RaycastHit hit;
if (Physics.Raycast(controller.eyes.position, eulerAnger * controller.eyes.forward,
out hit, controller.defaultStats.lookRange) && hit.collider.CompareTag(
"Player"))
{
controller.chaseTarget = hit.transform;
return true;
}
return false;
}
123456789101112131415161718192021222324252627282930313233343536373839
最终效果
查找角度:90、精度:6,追杀角度15、精度2。
红坦克是查找时敌人发出的绿色射线;绿坦克是追杀时发出红色射线;蓝坦克在第一条默认射线就检测到敌人,所以就不需要在添加额外角度射线。
查找精度:50、追杀精度:10。
- 基本像个扇形了,而且性能没有太大变化。
方法2:
相对方法一,可以说又省代码,又省内存。缺点就是检测扇形区域每一帧只有一个方向。
原理:只用一条射线,每次调用的时候旋转一定角度。如果一秒走30帧,那就是一秒可以变化30次角度。一般来说也够了。实现起来就相当简单了。使用Mathf.Repeat来获取角度就好了。
[Range(
0,
360)]
public float angle =
90f;
[Range(
0,
100)]
public float distance =
25f;
public float rotatePerSecond =
90f;
private bool Look(StateController controller)
{
if (LookAround(controller, Quaternion.Euler(
0, -angle /
2 + Mathf.Repeat(rotatePerSecond * Time.time, angle),
0), distance, debugColor))
return true;
return false;
}
1234567891011121314151617
上图中射线其实是一直在摆动的。
方法3:
就是结合方法1和方法2了,多条线同时旋转检测,算是结合前两者的优点了。
原理就是在方法2基础上,多加一层循环,即同一帧有多条线检测,如下修改代码。
[Range(
1,
50)]
public float accuracy =
1f;
private bool Look(StateController controller)
{
float subAngle = angle / accuracy;
for (
int i =
0; i < accuracy; i++)
if (LookAround(controller, Quaternion.Euler(
0, -angle /
2 + i * subAngle + Mathf.Repeat(rotatePerSecond * Time.time, subAngle),
0), distance, debugColor))
return true;
return false;
}
123456789101112
说明:
蓝色坦克:攻击了红坦克。红坦克就同时放出红色坦克:巡逻时不知道被谁打了,同时放出四条黄色射线旋转检测,每条射线只要旋转90°就可以检测完周围360°。绿色坦克:发现了敌人,每次先直接射出一条正前方的红线(因为攻击时经常只需要这一条第一帧就抓到敌人),另外一条就是旋转的检测射线。黄色坦克:正在巡逻。三条绿色同时旋转,检测角度为90°,所以每条射线只要旋转30°。