1. 程式人生 > >【Unity】【C#】《U3d人工智慧程式設計精粹》學習心得--------AI角色的感知方式-視覺感知實現解讀

【Unity】【C#】《U3d人工智慧程式設計精粹》學習心得--------AI角色的感知方式-視覺感知實現解讀

視覺感知:

sightSensor 與 sightTrigger 實現

視覺感知前提:

  • 需要判斷是否有物體的視覺觸發器處於其他物體的視覺感知器的範圍內

感知視覺觸發器的感知器-------sightSensor類

  1. 可視角度,
  2. 最遠可視距離
  3. 繪製可視距離
  4. 繪製與視覺目標的連線

判斷是否被視覺感知器觸發的觸發器--------sightTrigger類

  1. 判斷感知器是否為視覺感知器
  2. 通過獲取感知器上的資訊與當前位置,計算出當前物體是否處於視覺感知器的範圍內
  3. 更新觸發器位置(觸發器位置跟隨著物體改變)

sightSensor類:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SightSensor : Sensor {
    //定義這個AI角色的視域範圍
    public float fieldOfView = 45;
    //定義這個AI角色最遠能看到的距離
    public float viewDistance = 100.0f;
    //private AIController controller;
    
	// Use this for initialization
	void Start () {
        //controller = GetComponent<AIController>();
        //設定感知器型別
        sensorType = SensorType.sight;
        //向管理器註冊
        manager.RegisterSensor(this);
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    public override void Notify(Trigger trigger)
    {
        //當感知器能夠真正感覺到某個觸發器的資訊時被呼叫產生相應的行為決策
        print("i see a " + this.gameObject.name + "!");
        //畫出與觸發目標的連線線
        Debug.DrawLine(transform.position, trigger.transform.position, Color.red);
        //controller.MoveToTarget(trigger.transform.position);
    }

    private void OnDrawGizmos()
    {
        Vector3 frontRayPoint = transform.position + (transform.forward * viewDistance);
        //把45度轉換成 Π/4
        float fieldOfViewinRadians = fieldOfView * 3.14f / 180.0f;

        Vector3 leftRayPoint = transform.TransformPoint(new Vector3(viewDistance * Mathf.Sin(fieldOfViewinRadians), 0, viewDistance * Mathf.Cos(fieldOfViewinRadians)));

        Vector3 rightRayPoint = transform.TransformPoint(new Vector3(-viewDistance * Mathf.Sin(fieldOfViewinRadians), 0, viewDistance * Mathf.Cos(fieldOfViewinRadians)));

        Debug.DrawLine(transform.position + new Vector3(0, 1, 0), frontRayPoint+ new Vector3(0, 1, 0), Color.green);

        Debug.DrawLine(transform.position + new Vector3(0, 1, 0), leftRayPoint + new Vector3(0, 1, 0), Color.green);

        Debug.DrawLine(transform.position + new Vector3(0, 1, 0), rightRayPoint + new Vector3(0, 1, 0), Color.green);

    }
}

sightTrigger類:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SightTrigger : Trigger {
    public override void Try(Sensor sensor)
    {
        base.Try(sensor);
    }

    //判斷感知器是否能感知到這個觸發器
    public override bool isTouchingTrigger(Sensor sensor)
    {
        GameObject g = sensor.gameObject;
        //如果這個感知器能夠感知視覺資訊
        if (sensor.sensorType == Sensor.SensorType.sight)
        {
            RaycastHit hit;
            Vector3 rayDirection = transform.position - g.transform.position;
            rayDirection.y = 0;
            //判斷感知體的前方向於物體所在的方向的夾角,是否在實現的範圍內
            if((Vector3.Angle(rayDirection,g.transform.forward))<(sensor as SightSensor).fieldOfView)
            {
                //在實現距離內是否存在其他障礙物遮擋,如果沒有障礙物,返回true;
                if(Physics.Raycast(g.transform.position+new Vector3(0,1,0),rayDirection,out hit,(sensor as SightSensor).viewDistance))
                {
                    if (hit.collider.gameObject == this.gameObject)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    //更新觸發器內部訊息,由於帶有視覺觸發器的AI角色可能是運動的,因此需要不停地更新這個觸發器的位置
    public override void Updateme()
    {
        position = transform.position;
    }

    // Use this for initialization
    void Start () {
        base.Start();
        manager.RegisterTrigger(this);
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

繪製可視範圍(解讀):

涉及公式:

a為夾角度數 

b為a的度數近似值     

度數近似值 = 度數 * 3.14 / 180b= a* 3.14 / 180

red為紅色線長度,redx為紅色虛線長度

sin(b)=red/redx

由圖右上文字可知,

x=redx,y=redx

求出綠色座標點的座標

通過TransformPoint()將該本地座標轉換成世界座標。

參考書籍:《unity3d人工智慧程式設計精粹》 王洪源等著