1. 程式人生 > >Unity 使用物理射線(Physics.Raycast),實現扇形區域碰撞檢測三種方法

Unity 使用物理射線(Physics.Raycast),實現扇形區域碰撞檢測三種方法

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°。