前言
原文鏈接 https://www.yuque.com/finctive/game-dev/fsm-ai
本文參考自:
- Unity3d College - Unity3D AI with State Machine (FSM), Drones, and Lasers!(視頻教程)
- 游戲設(shè)計(jì)模式-狀態(tài)模式 (譯文)
- Game Programming Patterns - State (英語原文)
在閱讀完本文后,請查看上述鏈接內(nèi)容。
本文使用的示例工程:FINCTIVE/lost-in-the-wilderness-nightmare(荒野迷蹤:噩夢)歡迎Star!
由于水平有限,文章內(nèi)容可能有誤,歡迎探討、交流、或直接批評。
┗|`O′|┛
本文作者: FINCTIVE
聯(lián)系郵箱: finctive@qq.com
轉(zhuǎn)載請標(biāo)明原文鏈接以及作者名字,謝謝。
應(yīng)用場景
在荒野迷蹤:噩夢中,敵人的行為如下圖(在線運(yùn)行demo):

文字描述再詳細(xì)也不如你親自打開網(wǎng)頁玩一下 :D
看起來可以用大大的 if{...}else if{...}else{...} 語句實(shí)現(xiàn),但實(shí)際上手開發(fā)之后我發(fā)現(xiàn)……
切入正題:一個(gè)狀態(tài)指AI的一系列行為,例如本項(xiàng)目中的靜止、追逐、自爆狀態(tài),可以使用一個(gè)類描述。對于一個(gè)狀態(tài),應(yīng)該把處理代碼寫在一個(gè)類中。比如,“追逐玩家”狀態(tài)相關(guān)的代碼,盡量不要寫到其他狀態(tài)的類里面。
解決方案
敵人AI游戲?qū)ο蠼貓D

以下是狀態(tài)的基類
public abstract class BaseState : MonoBehaviour
{
// 執(zhí)行本狀態(tài)的相關(guān)操作,返回值是下一次游戲循環(huán)的狀態(tài)
public abstract BaseState Tick();
// 與本狀態(tài)有關(guān)的初始化代碼
public virtual void OnStateStart(){}
// 與本狀態(tài)有關(guān)的退出代碼
public virtual void OnStateExit(){}
}
狀態(tài)機(jī)
public class StateMachine : MonoBehaviour
{
public BaseState defaultState;
[HideInInspector]public BaseState currentState;
private void Awake()
{
currentState = defaultState;
}
void FixedUpdate()
{
BaseState nextStateType = currentState.Tick();
if (nextStateType != currentState)
{
nextStateType.OnStateStart();
currentState.OnStateExit();
}
currentState = nextStateType;
}
}
我把與所有狀態(tài)相關(guān)的控制腳本寫在了EnemyController組件中,暴露出公共方法讓狀態(tài)機(jī)腳本調(diào)用。這樣可以復(fù)用代碼,并且讓狀態(tài)機(jī)的邏輯代碼只負(fù)責(zé)更高一層的控制,而不管細(xì)節(jié)如何。
以下是追逐狀態(tài)的代碼,其他狀態(tài)同理。
public class EnemyChasingState : BaseState
{
public EnemyAttackingState enemyAttackingState;
public EnemyIdlingState enemyIdlingState;
private EnemyController _enemyController;
private void Awake()
{
_enemyController = GetComponent<EnemyController>();
}
private static readonly int AnimMove = Animator.StringToHash("move");
public override BaseState Tick()
{
Vector3 targetPos = PlayerController.playerTransform.position;
_enemyController.MoveToTarget(targetPos);
float distanceSqrMag = (targetPos - transform.position).sqrMagnitude;
// 距離足夠近,開始攻擊(自爆)
if(distanceSqrMag < _enemyController.enemyInfo.startAttackingDistance*_enemyController.enemyInfo.startAttackingDistance)
{
return enemyAttackingState;
}
// 距離太遠(yuǎn),放棄追逐
if(distanceSqrMag > _enemyController.enemyInfo.stopChasingDistance*_enemyController.enemyInfo.stopChasingDistance)
{
return enemyIdlingState;
}
return this;
}
public override void OnStateExit()
{
_enemyController.modelAnimator.SetFloat(AnimMove, 0f);
}
}
作者:FINCTIVE(finctive@qq.com)
歡迎轉(zhuǎn)載,請附上原文鏈接以及作者名字,謝謝!