感知=触发器+感知器+事件管理器
1、视觉感知:
视锥体是模拟视觉地基本方法,以眼睛为中心,一定锥角范围内为检测区域
除了判断物体是否在视锥体范围内外,还要进行视线测试(LOS),才能确定最终结果
原则上以玩家为中心,限定AI角色的智能。
对于AI角色看到需要做出响应的物体,添加Trigger派生类SightTrigger作为触发器
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SightTrigger : Trigger { public override void Try(Sensor sensor) { //如果感知器能感觉到这个触发器,那么向感知器发出通知,感知体做出相应的决策或行动 if (isTouchingTrigger(sensor)) { sensor.Notify(this); } } //判断感知器是否能感知到这个触发器 protected 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 () { //调用基类的Start()函数 base.Start(); //像管理器注册这个触发器,管理器会把它加入到当前触发器列表中 manager.RegisterTrigger(this); } // Update is called once per frame void Update () { } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SightSensor : Sensor { //定义这个AI角色的视域范围 public float fieldOfView = 45; //定义这个AI角色最远能看到的距离 public float viewDistance = 100.0f; // Use this for initialization void Start () { sensorType = SensorType.sight; manager.RegisterSensor(this); } // Update is called once per frame void Update () { } public override void Notify(Trigger t) { //当感知器能够真正感觉到某个触发器的信息时被调用,产生相应的行为或做出某些决策 //此处行为:打印相关信息,感知体和触发器之间画一条红色连线 print("I see a " + t.gameObject.name + "!"); Debug.DrawLine(transform.position, t.transform.position, Color.red); } private void OnDrawGizmos() { Vector3 frontRaypoint = transform.position + (transform.forward * viewDistance); 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))); // } }