身為一個日常躺平的大學生,寒假入手了無主之地3,開始了刷刷刷之旅,但出于對游戲的興趣,我想討論一下無主之地敵人的AI設計,通常AI設計都會符合遵循一定的規律,行為是由邏輯的可拿代碼實作的,就拿無主之地3中最基礎的那棒子追著玩家打的狂熱者而言,當玩家進入狂熱者攻擊范圍時,他會追上玩家并且揮動棒子攻擊,于是我們可以根據他的行為寫出下面的代碼.
void Update()
{
if(CanAttack())
{
Attack();//在進入可攻擊范圍內進行
}
else
{
Dash();//在小于攻擊范圍內追擊
}
}
這就簡易直觀的還原了狂熱者的行為,用影像展示如下:
這就是不是有狀態機那味了,那我們就可以把這兩個方法Attack,Dash抽象為兩種狀態,我們剛剛的代碼就是讓他在狂熱者的兩個狀態切換,切換僅滿足一定的條件即可,你說 欸?這不就是狀態機嗎?我拿unity連線也是這,我想說這就是狀態機沒錯,狀態機本身就是這么簡單.接下來我們就談談狀態機.
狀態機(Finite State Machine).
狀態機其實跟上面的代碼的作用一樣,當然你用switch,case也可以做出來,但狀態機比較規范方便罷了,后期也方便擴展,這里我介紹一個我寒假新學的一種架構,確實有點蠢,但也是最方便理解的,理解了這個在平時運用啊什么的可以靈活變化一下,進行簡化或者復雜化.
狀態機主要由以下幾部分組成:
我們可以參考Unity的官方檔案,寫一個敵人的狀態機為例子
1.列舉所有狀態:StateType
public enum EnemyStateID
{
NullState,
ChaseState,
AttackState,
IdleState........
}
2.轉換標明:Transition
public enum EnemyTransition
{
NullTansition = 0,
SeeSoldier,
NoSoldier,
LowHealth.....
}
用來標明什么情況下進行狀態轉換,也可以展示當前狀態.
3.狀態機:FSMSystem
public class EnemyFSMSystem
{
private List<IEnemyState> mStates;
private IEnemyState mCurrentState;
public IEnemyState currentState
{
get { return mCurrentState; }
}
public void AddState(params IEnemyState[] states)
{
foreach (IEnemyState s in states)
{
AddState(s);
}
}
public void AddState(IEnemyState state)
{
if (state == null)
{
Debug.LogError("state can not be null");
return;
}
if (mStates.Count == 0)
{
mStates.Add(state);
mCurrentState = state;
mCurrentState.DoBeforeEntering();
return;
}
foreach (IEnemyState s in mStates)
{
if (s.stateID == state.stateID)
{
Debug.LogError("要添加的狀態ID" + s.stateID + "已經添加");
return;
}
}
mStates.Add(state);
}
public void DeleteState(EnemyStateID Id)
{
if (Id == EnemyStateID.NullState)
{
Debug.LogError("要洗掉的狀態ID為空" + Id);
return;
}
foreach (IEnemyState s in mStates)
{
if (s.stateID == Id)
{
mStates.Remove(s);
return;
}
}
Debug.LogError("要洗掉的ID不存在于集合中:" + Id);
}
public void PerfromTransition(EnemyTransition trans)
{
if (trans == EnemyTransition.NullTansition)
{
Debug.LogError("要執行的轉換條件為空:" + trans);
return;
}
EnemyStateID NextstateID = mCurrentState.GetOutPutState(trans);
if (NextstateID == EnemyStateID.NullState)
{
Debug.LogError("在轉換條件" + trans + "下沒有對應的轉換狀態");
return;
}
foreach (IEnemyState s in mStates)
{
if (s.stateID == NextstateID)
{
mCurrentState.DoBeforeLeaving();
mCurrentState = s;
mCurrentState.DoBeforeEntering();
return;
}
}
}
}
FSMSystem是狀態機的心臟,當然你也可以獻上心臟(滑稽),在例子里我們寫了一個儲存狀態的串列,與下面的類組合,再通過呼叫下面的PerfromTransition函式通過條件進行狀態之間的轉化,從而確保能轉換到我想要的狀態,有點狀態模式超級升級版那味了對吧,我感覺狀態機就是狀態模式演化來的(狗頭保命個人觀點). 通過上述操作就可以通過標識轉換條件進行轉換,而不是哪個狀態,實作了一點邏輯和狀態的分離.
4.具體狀態
public abstract class IEnemyState : MonoBehaviour
{
protected ICharacter mCharacter;//一個角色管理類 可以理解為一個游戲物體GameObject Enemy.
protected Dictionary<EnemyTransition, EnemyStateID> mMap = new Dictionary<EnemyTransition, EnemyStateID>();
protected EnemyStateID mStateID;
public EnemyStateID stateID { get { return mStateID; } }
protected EnemyFSMSystem mFSM;
public IEnemyState(EnemyFSMSystem fsm, ICharacter character)//初始化
{
mFSM = fsm;
mCharacter = character;
}
public void AddTransition(EnemyTransition trans, EnemyStateID id)
{
//將trans,id加入到mMap這個字典,用來標明某個transition
//進行轉換和判別
if (trans == EnemyTransition.NullTansition)
{
Debug.LogError("EnemyState Error:Trans can not be NULL");
return;
}
if (id == EnemyStateID.NullState)
{
Debug.LogError("EnemyState Error:StateID can not be NULL");
return;
}
if (mMap.ContainsKey(trans))
{
Debug.LogError("EnemyState Error:" + trans + "Already added");
return;
}
mMap.Add(trans, id);
}
public void DeleteTransition(EnemyTransition trans)
{
if (mMap.ContainsKey(trans) == false)
{
Debug.LogError("洗掉轉換條件的時候,轉換條件不存在:[" + trans + "]不存在");
return;
}
mMap.Remove(trans);
}
public EnemyStateID GetOutPutState(EnemyTransition trans)
{
if (mMap.ContainsKey(trans) == false)
{
return EnemyStateID.NullState;
}
else
{
return mMap[trans];
}
}
public virtual void DoBeforeEntering() { }
public virtual void DoBeforeLeaving() { }
public abstract void Reason(List<ICharacter> targets);
public abstract void Act(List<ICharacter> targets);
}
這個就是最后留給具體狀態繼承的類了,通過字典mMap可以添加對應的鍵值對,來進行狀態的載入,洗掉和展示.
具體應用
我這里留下了幾個虛函式和抽象函式,我們可以在其中比如預載一些狀態配對的動作,玩家坐標,影片等,就不需要再連接每一個的影片模型等東西了.比如行為動作的不同 在具體的某個具體狀態的具體實作來寫就可以.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/430262.html
標籤:AI
