Unity 有限狀態機(Finite State Machine)的理解 與 實現簡單的可插拔(Pluggable)AI指令碼物件。
阿新 • • 發佈:2019-01-27
#Unity 有限狀態機(Finite State Machine)的理解 與 實現簡單的可插拔AI指令碼物件。
一般的遊戲AI都是使用狀態機的設計模式來實現的。發現官方有教程,就跟了一遍,這裡就總結一下。
先簡單說一下狀態模式。就是根據當前狀態和對應條件,選則需要轉換的下一個狀態。例如本文的例子,坦克預設狀態是巡邏(狀態),在發現有敵人時(條件),轉換成追殺(狀態),在敵人逃脫或死掉了之後(條件),又變成巡邏(狀態)。
遊戲場景:綠色標籤為巡邏點。藍色標籤為玩家出生點。紅色為AI出生點。(如圖玩家被兩個AI追殺。)
AI物件結構
每個AI物件都有一個狀態控制器(StateController)指令碼元件,包含一個當前狀態(State),且狀態包含需要執行動作(Action),還有狀態轉換的條件(Transition)。
Unity中檔案分類層級如下。其中DefaultEnemyStats是預設AI的一些配置。包括攻擊距離,移動速度,旋轉速度等等。
State (狀態)
using UnityEngine;
[CreateAssetMenu (menuName = "PluggableAI/State")]
public class State : ScriptableObject
{
public Action[] actions; //動作
public Transition[] transitions; //通過決定,選擇下一種動作決定
public Color sceneGizmoColor = Color.gray; //拿來渲染eyes的Gizmos顏色
//每一幀更新狀態,在StateController的OnUpdate中呼叫。
public void UpdateState(StateController controller)
{
DoActions(controller); //執行動作
CheckTransition(controller); //檢測轉換狀態
}
//順序執行動作列表的動作。
private void DoActions(StateController controller)
{
for (int i = 0; i < actions.Length; i++)
actions[i].Act(controller);
}
//檢查所有轉換狀態,並改變狀態。
private void CheckTransition(StateController controller)
{
for (int i = 0; i < transitions.Length; i++)
{
//這裡條件轉換隻有兩個,所以直接用Bool型別來判斷。當然也可以有多種條件轉換。
bool decisionSucceeded = transitions[i].decision.Decide(controller);
if (decisionSucceeded)
controller.TransitionToState(transitions[i].trueState);
else
controller.TransitionToState(transitions[i].falseState);
}
}
}
狀態名 | 說明 | 配置 |
---|---|---|
Remain State | 保持原來狀態。不包含任何屬性。 | 略。 |
PatrolChaser | 巡邏者狀態。在巡邏點之間巡邏,如果檢測到敵人,轉為追殺狀態。 | |
ChaseChaser | 追殺者狀態。導航到目標一定距離並持續攻擊,直到目標死掉,轉回巡邏者模式。 | |
PatrolScanner | 同Patrol Chaser。巡邏到敵人,轉為追殺。 | |
ChaseChaser | 同Chase Chaser 。追殺敵人,但到目標死掉,轉換成警覺模式。 | |
AlertScanner | 警覺模式。其實就是旋轉掃描敵人,直到掃描到目標或超過了一定時間。 |
通過上表其實可以發現有兩個非常相似的動作,只是為了演示Alert Scanner 的。其實是可以優化的,就是修改Transition的結構,不再是隻有兩種狀態選擇,而是多種,這樣就可以更好的重用狀態。
Action(動作)
using UnityEngine;
public abstract class Action : ScriptableObject
{
//State直接呼叫這個方法來執行動作。
public abstract void Act(StateController controller);
}
動作名 | 說明 | 原理 |
---|---|---|
PatrolAction | 巡邏動作。從StateController搞來巡邏點列表。在這些巡邏點之間巡邏移動。 | 設定巡邏點為導航目標點,到達後設置下一個巡邏點為導航點。 |
ChaseAction | 追隨動作。最短距離到達目標。 | 設定目標為導航目標點,導航過去就是了。 |
AttackAction | 攻擊動作。攻擊目標。 | 射出檢測射線(紅色的),檢測到目標就發炮攻擊。 |
Decision(決定)
using UnityEngine;
public abstract class Decision : ScriptableObject
{
//通過這個方法的返回值來判斷決定的選擇。
public abstract bool Decide(StateController controller);
}
決定名 | 說明 | 原理 |
---|---|---|
LookDecision | 檢測決定。檢測到目標就設定為追蹤目標,並且返回True。 | 射出檢測球體射線(綠色的)來檢測。 |
ActiveStateDecision | 活動狀態決定。就是判斷追蹤的目標是否active(啟用狀態)。 | 直接返回追蹤目標的active的bool值。 |
ScanDecision | 掃描決定。說是掃描其實並沒有掃描,只是在轉圈而已。一直轉到外部條件改變狀態,或者超過了掃描限定的時間。 | 停止導航。配合Time.deltaTime旋轉自己。返回是否超過掃描限定時間。 |
Transitions(狀態轉換)
//狀態轉換。通過決定的返回值,選則兩種狀態中其中一個
[System.Serializable]
public class Transition
{
public Decision decision;
public State trueState;
public State falseState;
}