Unity 使用物理射线(Physics.Raycast),实现扇形区域碰撞检测三种方法

xiaoxiao2021-02-28  17

转载自: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。


实现代码

// // LookDecision // //放射线检测 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; } //射出射线检测是否有Player 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来获取角度就好了。

// // LookDecision // [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°。
转载请注明原文地址: https://www.6miu.com/read-1600036.html

最新回复(0)