轉自:http://www.gameres.com/467913.htmlhtml
人工智能遵循着:感知->思考->行動
決策方法:有限狀態機(Finite-State Machines),分層狀態機(Hierarchical Finite-State Machines),行爲樹(Behavior Trees),效用系統(Utility Systems),目標導向型行動計劃(Goal-Oriented Action Planners),分層任務網絡(Hierarchical Task Networks)
有限狀態機
有限狀態機是目前遊戲AI中最多見的行爲模型。狀態機的代碼簡單又快速,使用上強大、靈活、計算開銷小。
狀態機的一個好處是能夠可視化,以下圖所示:算法
圖中有四個狀態:巡邏(patrol),查看(investigate),攻擊(attack),逃走(flee),咱們把實心圓當作初始狀態。
簡要過程:假設NPC士兵正在保衛他的陣地,當前狀態爲巡邏,當他聽到什麼動靜時就會轉到查看狀態,跑到聲音源去查看,若是看到敵人就轉到攻擊狀態,若是沒看到過一段時間又會回到巡邏狀態。在攻擊狀態中若是血值低下就會進入逃跑狀態。若是擊敗了敵人,又會回到巡邏狀態。
狀態機狀態類的一個主要結構以下,onEnter函數就至關於unity中的Start()函數,在類開始時調用,做爲對舊狀態的過分和新狀態產生的開始,好比當從巡邏轉向攻擊狀態時,能夠在攻擊狀態的開始讓NPC大喊「發現敵人!進攻!」等等。onUpdate()就至關於unity中的Update(),你可讓它每幀都執行,或者幾秒鐘執行一次,是循環執行的,每次執行時間間隔由你來決定。onExit()就是在退出一個狀態以前要執行的,好比,殺死敵人以後由攻擊狀態轉向巡邏狀態以前,讓NPC作一個歡呼手勢並大叫勝利了。FSMTransition列表爲將要轉到的全部可能的狀態。安全
class FSMState { virtual void onEnter(); virtual void onUpdate(); virtual void onExit(); list<FSMTransition> transitions; };
每一個狀態還存儲着FSMTransition的類,表明能從當前狀態能夠轉到的狀態網絡
class FSMTransition { virtual bool isValid(); virtual FSMState* getNextState(); virtual void onTransition(); }
當轉換條件知足時isValid()返回true,好比當發現敵人NPC就從巡邏狀態轉到攻擊,getNextState()返回將要轉到的狀態,onTransition()是狀態之間轉換的過渡,和上面說的onEnter()差很少。
最後是有限狀態機類FiniteStateMachineide
class FiniteStateMachine { void update(); list<FSMState> states; FSMState* initialState; FSMState* activeState; }
有限狀態機類包含一個包含全部狀態的列表states,initialState爲初始狀態,activeState爲當前狀態。
僞代碼以下:函數
在 activeState.transtitions中循環調用isValid(),檢測是否符合達到下一狀態的條件
若是符合轉換條件
調用activeState.on Exit(),退出當前狀態
設置activeState 爲 validTransition.getNextState(),把當前狀態賦值爲下一狀態
調用activeState.onEnter(),下一狀態的開始
若是不符合轉換條件,調用activeState.onUpdate(),讓NPC執行當前狀態須要作的事
在編寫有限狀態機的代碼只前最好畫一個上面的草圖,這樣既能夠明確轉換關係,又能夠不漏掉該有的狀態。
分層有限狀態機
有限狀態機雖然好,可是它有很大的缺點,當狀態少的時候能夠運用自如,當狀態多的時候10個以上就已經結構很是複雜,並且容易出錯。
若是咱們讓NPC巡邏兩個地方,好比安全的室內,和門口。人工智能
若是咱們想在一個狀態上附加一些情況,例如當NPC在巡邏時,讓他接一個電話而後再恢復巡邏,此時若是使用有限狀態機的話咱們必需要新建一個打電話的狀態來作過渡,可是此時的巡邏有兩個,因此能接到電話的狀態也有兩個,而後加了兩個相同的狀態,不少這樣的重複的小狀態使得狀態機愈來愈複雜。以下圖:spa
這時,咱們能夠用分層有限狀態機來解決這個問題,把多個狀態機歸爲一層,以下圖,把巡邏安全處和門口歸爲看守建築,這樣咱們只須要有一個打電話狀態就能夠了。設計
分層有限狀態機增長了一個滯後,在有限狀態機中並無,在一個普通的有限狀態機中,是從初始狀態開始的,在分層有限狀態機中是一個嵌套的狀態。注意上圖有H的圈,表明歷史狀態(history state),當咱們第一次進入嵌套狀態->看守建築時,歷史狀態H表示爲初始狀態,以後歷史狀態H表示爲最近處在的一個狀態。
在咱們的例子中:初始狀態就是看守建築,,而後進入看到手機按住這個嵌套,巡邏安全處是初始狀態。當從巡邏安全處轉換到巡邏門口這個狀態時,H歷史狀態就轉變爲巡邏門口狀態,此時來電話了,轉換到接電話狀態,接電話結束,咱們回到嵌套狀態中的歷史狀態,此時爲巡邏門口,可見H歷史狀態就是一個臨時的,便於嵌套外的狀態返回到以前的嵌套內的小狀態,以不至於出錯,或者換回了別的狀態,若是接完電話回到巡邏安全處,那就出大錯了。
分層有限狀態機,就這樣避免了重複狀態,能夠實現更大的更復雜的狀態。
實例:
Halo2使用了這一技術,以下圖:code
可見:把使用手雷、掩蔽、防護歸爲自衛,交戰部分使用了多層嵌套,可是原理是同樣的,向屍體設計和搜查屍體歸爲戰後處理。在撤退和閒置部分只有一個行爲被嵌套,可是往後能夠繼續添加行爲,可擴展性良好。
至於如何在嵌套的層裏對行爲進行選擇,能夠就按這個順序執行,也能夠加上權重優先級,或者你想讓他執行哪一個經過代碼來控制。
行爲樹
行爲樹是樹型結構的,每一個節點都表明了一個行爲,每一個行爲均可以有子行爲。
全部行爲都有一個先決條件,就是產生的這些行爲的條件。
整個算法先從樹的根部開始,而後開始檢查每個先決條件。樹的每一層只能夠執行一個行爲,因此當一個行爲正在執行,它的兄弟節點都不會被檢查,可是它們的子節點仍是要檢查的。相反若是一個行爲的先決條件當前並不知足,則跳過判斷它的子節點,繼續判斷它的兄弟節點。一個樹所有檢查完畢以後,決定執行優先級最大的,而後再依次執行每一個動做。
僞代碼:
使根節點爲當前節點
當存在當前節點
判斷當前節點的先決條件
若是先決條件返回true
把節點加到執行清單
使子節點爲當前節點
不然
使兄弟節點爲當前節點
執行執行清單上的全部行爲
不一樣於狀態機,行爲樹是無狀態的,不須要記下以前執行的行爲,只是判斷行爲該不應執行。
行爲樹的節點之間是不相關的,刪除或增長節點,對其餘節點都無影響。因此,可擴展性也是行爲樹的一個優點。另外還能夠爲決策樹添加靈活性與隨機性,父節點能夠隨機決定是否檢查子節點。
缺點:決策樹作的選擇並不必定是最優的,結果也不必定是咱們想要的。並且決策每次都要從根部往下判斷選擇行爲節點,比狀態機要耗費時間。每次決策都要通過大量的條件判斷語句,會變得很是慢。
另外還有一個問題,例如:一個農民要收割做物,敵人出現了,農民逃跑,逃出了距離敵人的必定範圍以後,又回去收割做物,走到敵人的範圍又逃出,這樣來回往復,是一個弊端,,能夠根據狀況來寫代碼避免,不然會被玩家***的。
效用系統
人工智能的邏輯->電腦的邏輯,是基於簡單的bool問題,好比:「我能看到敵人嗎?」,「我有彈藥嗎」,是簡單的是或者不是的問題,因此作出的行爲一般是極端化的,一個單一的行動。好比:
if (CanSeeEnemy()) { AttackEnemy(); } if (OutOfAmmo()) { Reload(); } 即時有多條件的行爲,bool判斷帶來的也是一個單一的行動。 if (OutOfAmmo() && CanSeeEnemy()) { Hide(); }
因此有些狀況,只是作這些布爾判斷是不合適的,會遺漏不少狀況,判斷也不穩當。好比:咱們可能須要同時考慮與敵人的距離、有多少彈藥、飢餓程度、HP值,等等。這些判斷條件能映射出許多動做,比咱們單一的判斷作不作這個動做要好不少。utility-based system,基於效用的系統,會根據權重、比率、隊列和許多須要考慮的事項來作出最優選擇,使AI比普通的行爲樹更有頭腦。根據上面的例子,使用效用系統咱們的AI能夠作出咱們想要的動做,並根據當前狀況作出不一樣強度的動做,使AI真實、更具可能性,也再也不是隻有一個正確的選擇了。決策樹就是對AI說,「只是你將要作的一個行爲」,效用系統就是對AI說:「這些是你可能要作的行爲」
Sims模擬人生的人工智能就是使用的效用系統(sims的人工智能讓我膜拜至今),在sims中,小人結合當前環境和自身的狀態,來作出行動的選擇。例如:小人「很是餓」結合環境「沒有食物」會比只有「有一點餓」更加吸引人的眼球。若是「有一點餓」小人會以接近「美食」爲第一執行行爲。注意,這裏的「美食(的美味程度)」、「食物不多(食物儲備程度)」、「一點餓(餓的程度)」,都是一個有範圍的數值(經常使用的是0-1的浮點值)。
當須要選擇新的行爲時,咱們經過分數(上面說的各類程度)來選擇相對最優的選擇,或者加上一個隨機值再選擇,使得接近優選的幾個選擇都有必定概率(概率可根據所加隨機值決定)被選中。
目標導向型行動計劃
GOAP來源於STRIPS方法,這兩種都是讓AI創造他們本身的方法去解決問題,咱們提供給它一系列可能的動做做爲對這個世界的描述,和每一個動做使用的先決條件,和行動帶來的影響。AI擁有一個初始狀態和他須要達到的目標。有一組目標,AI能夠經過優先級或當前狀態選擇一個。計劃系統決定一個動做序列來知足當前目標,計劃出一個像路徑同樣的能最簡單達到目標狀態的動做序列。
GOAP是一個反向連接搜索,從要實現的目標開始,找到什麼動做能實現目標,在尋找剛纔動做的先決條件,一直往前推,知道達到你的當前(初始)狀態。這種反向連接搜索替代了啓發式的前向連接搜索。
僞代碼:
把目標加到未解決事件列表 對於每一個爲解決事件(for) 移除這個爲解決事件 找到達成事件的動做 若是動做的先決條件已經知足 增長動做到計劃中 往回推須要達到先決條件的動做到計劃中 不然 添加該先決條件到未解決時間中
例如:咱們創建一個NPC士兵,把它的目標設爲殺死其餘敵人,咱們設置它的目標爲Target.Dead。爲了讓目標去死,NPC必需要有一個武器用來射擊,這是一個先決條件,可是如今NPC並無正在裝備的武器,NPC就須要執行找到武器這個動做,若是NPC有武器庫,他就會從武器庫中拿一個,若是病沒有武器庫,就須要尋路去找一個武器裝備了。獲得武器裝備以後就要找到敵人,實現方式多種多樣,徒步尋找、或者NPC周圍有車也能夠開着車去尋找。我麼發現,咱們給NPC大量的動做選擇,讓NPC本身決定該作什麼,於是產生動態不可預知又有趣的行爲,並且表現得很天然,比開發者建立行爲好多了。
分層任務網絡
HTN也是尋找一個計劃來讓AI執行,不一樣之處在於怎樣找出這個計劃。開始擁有一個初始狀態和一個跟任務表明咱們須要解決的問題。原理是最高級的任務分解成更小的任務再繼續分解直到咱們解決問題。每一個高級任務都有不少方式被完成,當前世界狀態決定高級任務要分解成哪組小任務。HTN與GOAP相反,HTN是前向連接搜索,是從當前狀態一直推到目標狀態,向前推直到問題解決。世界狀態分散成幾種屬性,它的HP、精力,敵人的HP、相距距離,計劃根據這些來制定。
咱們有兩種任務:原始任務和複合任務。原始任務是能夠只解決問題的任務,也就是能夠直接達到目標的任務。在遊戲中,它能夠爲開火、裝填子彈、移動到掩蔽物。這些人物能夠影響世界狀態,開火這個任務須要先有子彈,並執行裝填子彈這個任務。複合任務是高級別的任務,能夠看做方法。一個方法是一組任務能夠完成複合任務,這一組任務是由先決條件決定的。複合任務讓HTN推斷出世界而且決定該作什麼動做。
使用複合任務,咱們就能構建一個HTN域,這個域是一大層任務,表明咱們解決問題的方法。
僞代碼:
增長根複合任務到分解列表中 對於每一個在咱們分解列表中的任務(for) 移除任務 若是任務是複合任務 找到知足當前條件狀態而且能處理該複合任務的方法 若是該方法找到了,增長方法的任務到分解列表中 若是沒找到,恢復到以前分解任務的狀態中 若是任務是原始任務 在當前狀態下執行任務 增長任務到最終計劃列表
HTN就是從最高級的根任務分解更小的任務再分解成更更小,分解是須要判斷當前狀態和條件的。當咱們終於分解爲原始任務,咱們把原始任務加到最終計劃中,每個原始任務都是一個可操做步驟,咱們能夠直接執行它。