【高級(jí)AI】用Unity實(shí)現(xiàn)一群鳥

項(xiàng)目難度:★★★★ (文章結(jié)尾有工程地址)
大多數(shù)人都喜歡欣賞漂亮的動(dòng)物,貓、狗、鳥、魚、蟲等等很多。其中最特別的要數(shù)鳥和魚了,因?yàn)樗鼈儾粌H可以單獨(dú)欣賞,還可以觀賞鳥群飛翔、魚群集體游動(dòng)的壯觀景象。


游戲世界里,模擬鳥群也是開發(fā)者們夢(mèng)寐以求的效果。我們希望能模擬出一個(gè)栩栩如生的鳥群,同時(shí)還能讓它們按照我們的需要向著目標(biāo)點(diǎn)移動(dòng)、或者在某個(gè)地方盤旋。這篇文章將演示如何制作一個(gè)漂亮的鳥群。

一、理論基礎(chǔ):
鳥群本質(zhì)是由一只一只的鳥組成的,每個(gè)鳥是獨(dú)立行動(dòng)的,只能通過視覺、聽覺與外界進(jìn)行有限的交互,然后做出合理的行為。有個(gè)專有名詞“自主主體(Autonomous Agent)”專門描述這種自主行為的個(gè)體。
雖然個(gè)體是自發(fā)行動(dòng)的,但是通過遵守某些簡單的規(guī)則,就可以滿足群體的集體目標(biāo),比如:不發(fā)生碰撞、順利到達(dá)目的地,或是一起在附近周旋覓食。
順著這種思路,我們就知道了要模擬鳥群,要從每一只具體的鳥入手。讓每一只鳥遵循同樣的規(guī)則來實(shí)現(xiàn)群體效果。那么,這種規(guī)則的設(shè)計(jì)就成為了關(guān)鍵問題。首先,集群的模擬分為三個(gè)層次:
行為選擇 Action Selection:?最高層。是移動(dòng)還是盤旋還是躲避敵人呢?我們要確定整體目標(biāo)以及個(gè)體目標(biāo)。整體目標(biāo)和個(gè)體目標(biāo)可以是不完全一致的,比如整體目標(biāo)是移動(dòng),個(gè)體在發(fā)現(xiàn)敵人時(shí)要優(yōu)先進(jìn)行規(guī)避。
引導(dǎo) Steering:?中間層。群體中個(gè)體的移動(dòng)和單獨(dú)個(gè)體的移動(dòng)當(dāng)然不一樣。群體中的個(gè)體要考慮的問題多很多,他總是在判斷離周圍的人是否太近容易碰到?是否太遠(yuǎn)脫離了隊(duì)伍?是否和大家的方向一致?是否要同時(shí)躲避敵人的攻擊?每個(gè)時(shí)刻,所有的因素互相影響作用,最終得出一個(gè)引導(dǎo)的方向和大小。
行動(dòng) Locomotion:?最底層。把引導(dǎo)化為實(shí)際行動(dòng)。在本例中,我們是利用剛體+力這種基本的物理方法來模擬的,引導(dǎo)是一種力,調(diào)整行動(dòng)的速度。
二、搭建Unity工程
1. 新建Unity 3D工程,將Assets Store下載的《Animals Full Pack -- Birds Pack》導(dǎo)入到工程中以便使用。
2. 在鳥的資源的Prefab里面找一種你喜歡的鳥,我選的是海鷗。將它拷貝到Assets目錄里準(zhǔn)備使用。動(dòng)畫默認(rèn)是用Animation組件實(shí)現(xiàn)的,將它的默認(rèn)動(dòng)作調(diào)整為fly,還需要添加Rigidbody剛體組件才能使用力Force,參數(shù)默認(rèn)即可:


3. 實(shí)現(xiàn)Bird腳本,是本文的主要內(nèi)容,下面會(huì)詳解。
三、寫代碼實(shí)現(xiàn)Bird腳本,控制每一只鳥的行為
本文對(duì)初學(xué)者來說比較有難度。建議從基本行為開始慢慢嘗試,我自己寫的時(shí)候也嘗試了很多基本的飛行方式,最終解決了多重問題,最終才得到不錯(cuò)的結(jié)果。飛行方式有以下幾種:

要實(shí)現(xiàn)集群效果,Seek、Flocking這兩種行為是最必要的,其它行為可以參考工程代碼和其他資料?;緮?shù)據(jù)和初始化如下:

a. Seek方式
Seek的方式是最基本的,它返回一個(gè)力(向量),引導(dǎo)鳥飛向目標(biāo):

向量運(yùn)算自行畫圖分析,我就不啰嗦了。這個(gè)力如何使用呢?只需要在Update的每一楨調(diào)用Seek,每一楨算一次Steering引導(dǎo)力即可。

然后讓引導(dǎo)力生效即可:

第一步很簡單吧?接下來看終點(diǎn)Flocking方式。
b. Flocking
Flocking的原理不像Seek那么直觀,必須參考一些資料??偟膩碚f,集群中的鳥:需要考慮三個(gè)基本問題,分散、統(tǒng)一方向、聚合:

上圖摘自游戲AI的經(jīng)典書籍《Programming Game AI By Example》。如圖,簡單來說,只看Separation分散和Cohesion聚合,你會(huì)發(fā)現(xiàn)其實(shí)它倆就像彈簧一樣,聚合負(fù)責(zé)壓緊彈簧,分散負(fù)責(zé)彈開彈簧。當(dāng)離得太近,彈力就比壓力大了,當(dāng)離得太遠(yuǎn),壓力又比彈力大。只要合理調(diào)整這兩個(gè)力的大小,就能實(shí)現(xiàn)一個(gè)大小合適、密度合適的群體。
而Alignment保證大家盡可能朝向一個(gè)方向。
Flocking函數(shù)全貌如下圖,可以看出每一只鳥在決定自己行為之前,要先觀察所有周圍的鳥,這是通過把周圍的鳥加入臨時(shí)容器實(shí)現(xiàn)的。后面的Separation、Alignment、Cohesion要用到這個(gè)容器。

分散力,思路是遠(yuǎn)離所有周圍的人:

聚合力,思路是盡量飛向群體的“質(zhì)心”:

統(tǒng)一方向力,思路是只看別人的速度方向,不看大小。然后向該方向移動(dòng):

Seek和Flocking原理完成啦!只要同時(shí)應(yīng)用Flocking和Seek兩種力,就能實(shí)現(xiàn)基本的集群效果。

四、關(guān)鍵性調(diào)整
如果只做上面的步驟,然后簡單限制一下速度,你會(huì)發(fā)現(xiàn)效果是這樣的:

動(dòng)畫太一致不算什么問題,關(guān)鍵是鳥在原地懸停這個(gè)不能忍,又不是飛碟……所以還要做一些關(guān)鍵內(nèi)容:
1、最關(guān)鍵的是,鳥是不會(huì)倒車的,鳥和飛機(jī)是靠轉(zhuǎn)彎再轉(zhuǎn)彎來實(shí)現(xiàn)掉頭。所以,我們將引導(dǎo)力的向后的分量完全取消:

2、引導(dǎo)力有時(shí)會(huì)劇烈變化方向,但鳥指向的角度不要迅速變化,那樣會(huì)非常假,平滑處理一下:

3、限制最大速度:

4、根據(jù)引導(dǎo)力的大小來控制扇動(dòng)翅膀的速度,非??茖W(xué):

以上用到的參數(shù)都是猜想+試驗(yàn)得出的,其原理值得大家思考。再看看最終效果:

五、還未結(jié)束
以上修改,已經(jīng)達(dá)到了本文演示的目的,而且效果個(gè)人覺得還挺好看的,體現(xiàn)了鳥類集群飛行的美感。如果你的游戲中需要用到鳥群作為裝飾,稍微優(yōu)化一下算法也基本夠用了。
但是深究的話,余下的問題還有很多,值得思考:
1、當(dāng)引導(dǎo)力指向后方,我們直接去掉引導(dǎo)力的向后分量,會(huì)導(dǎo)致出現(xiàn)很大的轉(zhuǎn)彎半徑,作為演示來說挺炫酷,但是不符合實(shí)際,思考飛機(jī)轉(zhuǎn)彎的例子,如果能模擬鳥類急轉(zhuǎn)彎,就會(huì)更好看了。

2、動(dòng)畫方面,鳥類會(huì)綜合使用撲翼、滑翔、爬升等動(dòng)作,應(yīng)當(dāng)根據(jù)實(shí)際調(diào)整。我們目前只實(shí)現(xiàn)了調(diào)整動(dòng)畫速度,很不完美。
相信解決了以上兩個(gè)問題,鳥群的模擬效果還能再上一層樓,也更符合實(shí)際。小問題總是需要花大力氣解決,魔鬼總在細(xì)節(jié)之中~~
GitHub工程地址:https://github.com/mayao11/GameAIAdvanced/tree/master/Flocking
參考書籍:
Programming Game AI By Example, by Mat Buckland. 譯名:《游戲人工智能編程案例精粹》
想系統(tǒng)學(xué)習(xí)游戲開發(fā)的童鞋,歡迎訪問?http://levelpp.com/? ? ? ? ? ? ? ??
游戲開發(fā)攪基QQ群:869551769? ? ? ? ? ? ? ???
微信公眾號(hào):皮皮關(guān)