Unity 中的 AI 模擬群組行為
AI(人工智慧)的主旨本質上是讓計算機能夠像生物體一樣,具有思考和決定的能力來執行某些特定操作。
人工智慧在遊戲中的作用是通過提供富有挑戰性的競爭對象來讓遊戲更好玩,在遊戲世界中行動逼真的有趣的非玩家角色(NPC)會讓遊戲更好玩。通過讓這些NPC對遊戲世界中不斷變化的情形,產生對玩家來說足夠合理、有意義的反應,來讓它們看起來更加智能。
群組行為:
模擬鳥群行走或者人群行走的過程稱之為群組行動
Demo:
給鳥加上一些行為:
public class Crow : MonoBehaviour {nnntpublic Vector3 velocity = Vector3.forward;ntpublic float animRandomTime = 2f;ntprivate Animation anim;nntprivate IEnumerator Start () {nttanim = GetComponentInChildren<Animation> ();ntt//每隔0到2秒之間的隨機數等待nttyield return new WaitForSeconds (Random.Range (0, animRandomTime));nttanim.Play ();nt}ntntvoid Update () {ntttransform.Translate (velocity * Time.deltaTime, Space.World);nt}n}n
這樣,鳥會不同的動畫朝著前面移動。
我們可以讓鳥群朝一個方向運動:
public class Crow : MonoBehaviour {nntpublic Transform target;ntpublic float speed = 1;ntpublic float animRandomTime = 2f;ntprivate Animation anim;nntprivate IEnumerator Start () {ntttarget = GameObject.Find ("Target").transform;nttanim = GetComponentInChildren<Animation> ();ntt//每隔0到2秒之間的隨機數等待nttyield return new WaitForSeconds (Random.Range (0, animRandomTime));nttanim.Play ();nt}ntntvoid Update () {ntttransform.LookAt (target.position);ntttransform.Translate (Vector3.forward * Time.deltaTime * speed);nt}n}n
但是這樣,鳥群會隨著時間的推移越來越靠近,不符合大自然的真實場景。
鳥群飛行會有三個特徵:
- 分離
- 隊列
- 聚集
這基本上都是動物的本能,每個生物都會與其他生物分離,有時也會聚集,同時我們也都是隊列的一部分。
第一個圖是判斷在一定範圍內,視為靠近,可施加一個力進行分離。第二個圖是指如果發現個體飛行方向偏離了大部隊的方向,就施加一個力進行調整。第三個圖是個體發現自己離鳥群比較遠的時候,選取就近的幾個點的中心點,進行力的施加,實現聚集。之所以使用力而不是速度是因為如果是直接設置速度的話就無法模擬現實環境,因為萬事萬物都是有慣性的。
如果你想要讓鳥朝向上圖中的紅色箭頭,有兩種方式。一種是直接在這個箭頭的方向施力,另一種是用目標方向減去當前方向施力。分離和聚集使用了第一種,因為鳥群以同樣的速度在飛行,可視為是相對靜止。可認為當前方向上的速度為0,所以直接在目標方向施力。隊列使用第二種是因為方向的偏移,並不能視為相對靜止。
實現分離:
主要思路是每隔一段時間判斷一次是否需要分離。通過Physics.OverlapSphere函數判斷鳥周圍固定數值半徑存在的 Collider,從而獲取到附近的鳥類。通過本身的坐標減去周圍的鳥的坐標,然後加起來。最終根據牛頓第二定律,確定速度。
public class CrowAI : MonoBehaviour {nntpublic Vector3 sumForce = Vector3.zero; //合力,下面三個力的綜合作用nntpublic Vector3 separationForce = Vector3.zero; //分離的力ntpublic Vector3 alignmentForce = Vector3.zero; //隊列的力ntpublic Vector3 cohesionForce = Vector3.zero; //聚集的力nntpublic float separationDistance = 3; //距離這個鳥有三米以內的鳥類,跟其他鳥進行分離ntpublic List<GameObject> separationNeighbors = new List<GameObject>(); //存儲附近的鳥ntpublic float separationWeight = 1; //分離的力所佔的比重ntpublic float m = 1; //鳥的質量ntpublic Vector3 velocity = Vector3.forward; nntpublic float checkInteterval = 0.2f;nntpublic float animRandomTime = 2f;ntprivate Animation anim;nntvoid Start () {nttInvokeRepeating ("CalForce", 0, checkInteterval);nttanim = GetComponentInChildren<Animation> ();ntt//每隔0到2秒之間的隨機數等待nttInvoke ("PlayAnim", Random.Range (0, animRandomTime));nt}nntvoid PlayAnim(){nttanim.Play ();nt}nntvoid CalForce(){nttsumForce = Vector3.zero;nttseparationForce = Vector3.zero; //分離的力nttalignmentForce = Vector3.zero; //隊列的力ntt cohesionForce = Vector3.zero; //聚集的力nnttseparationNeighbors.Clear ();nttCollider[] colliders = Physics.OverlapSphere (transform.position, separationDistance);nttforeach (Collider c in colliders) {ntttif (c != null && c.gameObject != this.gameObject) {nttttseparationNeighbors.Add (c.gameObject);nttt}ntt}ntt//計算分離的力nttforeach(GameObject neighbor in separationNeighbors){nttt//需要遠離的方向ntttVector3 dir = transform.position - neighbor.transform.position;nttt//距離越小,施加的力越大ntttseparationForce += dir.normalized / dir.magnitude;ntt}nttif (separationNeighbors.Count > 0) {ntttseparationForce *= separationWeight;ntttsumForce += separationForce;ntt}nnt}nntvoid Update(){ntt//牛頓第二定律,計算加速度nttVector3 a = sumForce / m;ntt// v=at ,速度計算公式nttvelocity += a * Time.deltaTime;ntttransform.Translate (velocity * Time.deltaTime, Space.World);nt}n}n
實現隊列:
思路差不多,只是用向量的減法實現目標方向的偏移。
//計算隊列的力nttcolliders = Physics.OverlapSphere (transform.position, alignmentDistance);nttforeach (Collider c in colliders) {ntttif (c != null && c.gameObject != this.gameObject) {nttttalignmentNeighbors.Add (c.gameObject);nttt}ntt}nttVector3 avgDir = Vector3.zero;nttforeach (GameObject n in alignmentNeighbors) {ntttavgDir += n.transform.forward;ntt}nttif (alignmentNeighbors.Count > 0) {ntttavgDir /= alignmentNeighbors.Count;ntttalignmentForce = avgDir - transform.forward;ntttalignmentForce *= alignmentWeight; ntttsumForce += alignmentForce;ntt}n
實現聚集:
聚集的判斷範圍和隊列的一樣。通過找到附近的鳥然後求出中心點。根據向量的減法求出當前鳥的目標方向。
ttif (alignmentNeighbors.Count <= 0)ntttreturn;nttVector3 center = Vector3.zero;nttforeach (GameObject n in alignmentNeighbors) {ntttcenter += n.transform.position;ntt}nttcenter /= alignmentNeighbors.Count;nttVector3 dirToCenter = center - transform.position;nttcohesionForce += dirToCenter;nttcohesionForce *= cohesionWeight; nttsumForce += alignmentForce;n
這樣的話後面的鳥的速度會顯得很慢,需要添加一個恆定的力來保證每隻鳥的速度。
//保持恆定飛行的力nttVector3 engineForce = startVelocity - velocity;nttsumForce += engineForce * 0.3f;n
在正常情況下,鳥群都會有一個首領帶著飛。那麼可以設計一個點,讓鳥群進行跟隨。
Vector3 targetDir = target.position - transform.position;nsumForce += (targetDir.normalized - transform.forward) * speed;n
然後對鳥群的行為進行一個優化,主要是對各個數值的調整。
學習資料:
Unity遊戲開發人工智慧編程
Boids Background and Update
推薦閱讀:
※前方高萌!這款會扒著你手指賣萌撒嬌的智能機器人,你確定你拒絕得了?
※高科技范兒的智能音箱,這個很洋氣的東西為什麼火不起來呢?
※大疆,大疆,路在何方(一)
※用機器人「考古」是怎樣的體驗?
※人臉識別有多火?從谷歌到山寨小廠都下手了