Unity 使用物理射線(Physics.Raycast),實現扇形區域碰撞檢測三種方法
阿新 • • 發佈:2019-02-02
Unity 使用物理射線(Physics.Raycast),實現扇形(Fan-Shaped)區域碰撞檢測。
網上已經很多實現扇形檢測的方法。大部分都是用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;
}
最終效果
查詢角度: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;
}
上圖中射線其實是一直在擺動的。
方法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;
}
說明:
- 藍色坦克:攻擊了紅坦克。紅坦克就同時放出
- 紅色坦克:巡邏時不知道被誰打了,同時放出四條黃色射線旋轉檢測,每條射線只要旋轉90°就可以檢測完周圍360°。
- 綠色坦克:發現了敵人,每次先直接射出一條正前方的紅線(因為攻擊時經常只需要這一條第一幀就抓到敵人),另外一條就是旋轉的檢測射線。
- 黃色坦克:正在巡邏。三條綠色同時旋轉,檢測角度為90°,所以每條射線只要旋轉30°。