Unity AI 感知侦探
游戏中AI的感知用的最多的是看到或者听到,也就是:
1.视觉感知
2.听觉感知
视觉感知:
视觉感知一般会有一个视野范围,这个范围与角色的朝向有关,只有在视觉范围内角色才有可能感知得到,这个范围与一个扇形接近,可以直接用半径和角度来控制。
潜在目标除了需要在视视觉范围内之外,探索者的视线还不能被其他障碍物遮挡,这里可以用射线来检测——发出一条从探索者到潜在目标的射线,如果目标是这条射线撞到的第一个单位,则该单位可以被看到,否则被忽略。
1 using System.Collections.Generic; 2 using UnityEngine; 3 4 public class AIViewPerception : MonoBehaviour 5 { 6 //视野半径 7 public float radius=10f; 8 //视野的角度范围 9 public float angel=120f; 10 11 //侦察的结果 12 public List<GameObject> PerceptionResult { get; private set; } 13 14 /// <summary> 15 /// 进行一次侦察 16 /// </summary> 17 /// <param name="explorer">探索者</param> 18 /// <param name="targets">潜在目标集</param> 19 public void Check(GameObject explorer,List<GameObject> targets) 20 { 21 PerceptionResult.Clear(); 22 23 foreach(var item in targets) 24 { 25 //距离判断,这里用平方减少系统开根号的计算量 26 Vector3 offset = item.transform.position - explorer.transform.position; 27 if (offset.sqrMagnitude > radius*radius) 28 continue; 29 30 //角度判断,是否在视线范围内 31 float angel = Vector3.Angle(explorer.transform.forward, offset.normalized); 32 if (angel > this.angel * 1.0f / 2) 33 continue; 34 35 //射线判断,是否被其它目标或障碍物阻挡 36 Ray ray = new Ray(explorer.transform.position, offset); 37 RaycastHit info; 38 if (Physics.Raycast(ray,out info,radius)) 39 { 40 if (info.collider.gameObject == item) 41 { 42 PerceptionResult.Add(item); 43 } 44 } 45 } 46 } 47 48 void Start() 49 { 50 //测试 51 PerceptionResult = new List<GameObject>(); 52 var targets =new List<GameObject>(GameObject.FindGameObjectsWithTag("Target")); 53 Check(gameObject, targets); 54 foreach(var result in PerceptionResult) 55 { 56 Debug.Log(result.name); 57 } 58 }
听觉感知:
听觉感知不像视觉感知那样跟朝向有关,也没有一个角度范围,但听觉感知一般与声源的距离有关;距离越远声音越小,在距离相对较远的地方由于接收到的音量逐渐变小,更好的处理方式是根据距离的远近有感知丢失的可能性。
这里采用两个听力半径作为区分,一个是安全听力半径,一个是最大听力半径,在安全听力半径内一定能感知到声源,但超过安全半径后,直到最大听力范围,感知到声源的概率呈现线性降低,直到最大听力范围处为0。
1 using System.Collections.Generic; 2 using UnityEngine; 3 4 public class AIListenPerception : MonoBehaviour 5 { 6 //最大听力半径 7 public float maxRadius = 10f; 8 //安全听力半径 9 public float safeRadius = 5f; 10 11 //侦听结果 12 public Dictionary<AudioSource,float> PerceptionResult { get; private set; } 13 14 /// <summary> 15 /// 进行一次侦听 16 /// </summary> 17 /// <param name="explorer">探索者</param> 18 /// <param name="targets">声源目标集</param> 19 public void Check(GameObject explorer, List<AudioSource> targets) 20 { 21 PerceptionResult.Clear(); 22 23 foreach(var item in targets) 24 { 25 //最大听力范围判断 26 Vector3 offset = item.transform.position - explorer.transform.position; 27 if (offset.sqrMagnitude > maxRadius * maxRadius) 28 continue; 29 30 //侦听丢失的概率判断:计算出一个声源距离比例权重,距离越远,权重越大,侦听丢失的概率越大 31 float distance = offset.magnitude; 32 float weight = (distance - safeRadius) * 1.0f / (maxRadius - safeRadius); 33 if (Random.value < weight) 34 continue; 35 36 //计算真实的侦听到音量,与距离成反比 37 float volume = item.volume *(1- distance / maxRadius); 38 PerceptionResult.Add(item, volume); 39 } 40 } 41 42 private void Start() 43 { 44 //测试 45 PerceptionResult = new Dictionary<AudioSource, float>(); 46 var targets = new List<GameObject>(GameObject.FindGameObjectsWithTag("Target")); 47 List<AudioSource> audioSources = new List<AudioSource>(); 48 foreach(var item in targets) 49 { 50 audioSources.Add(item.GetComponent<AudioSource>()); 51 } 52 Check(gameObject, audioSources); 53 foreach (var result in PerceptionResult) 54 { 55 Debug.Log("audioSource : " + result.Key.gameObject.name); 56 Debug.Log("volume : "+ result.Value); 57 } 58 }