Unity 3D | 敵人AI控制腳本狀態(tài)機(jī)(FSM)

前言

原文鏈接 https://www.yuque.com/finctive/game-dev/fsm-ai

本文參考自:

在閱讀完本文后,請查看上述鏈接內(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):

Artboard1.png
Artboard1.png

文字描述再詳細(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

www.png
www.png

以下是狀態(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)載,請附上原文鏈接以及作者名字,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容