Unity 3D | 敵人控制腳本狀態機(FSM)

前言

本文參考自:html

在閱讀完本文後,請查看上述連接內容。git

本文使用的示例工程:FINCTIVE/lost-in-the-wilderness-nightmare(荒野迷蹤:噩夢)歡迎Star!github

因爲水平有限,文章內容可能有誤,歡迎探討、交流、或直接批評。
┗|`O′|┛設計模式

本文做者: FINCTIVE
聯繫郵箱: finctive@qq.comide

轉載請標明原文連接以及做者名字,謝謝。this

原文發佈於語雀:www.yuque.com/finctive/ga…spa

應用場景

在荒野迷蹤:噩夢中,敵人的行爲以下圖(在線運行demo):
設計

Artboard1.png

文字描述再詳細也不如你親自打開網頁玩一下 :D

看起來能夠用大大的 if{...}else if{...}else{...} 語句實現,但實際上手開發以後我發現……3d

切入正題:一個狀態指AI的一系列行爲,例如本項目中的靜止、追逐、自爆狀態,能夠使用一個類描述。對於一個狀態,應該把處理代碼寫在一個類中。好比,「追逐玩家」狀態相關的代碼,儘可能不要寫到其餘狀態的類裏面。code

解決方案

敵人AI遊戲對象截圖

www.png

如下是狀態的 基類

public abstract class BaseState : MonoBehaviour
{
	// 執行本狀態的相關操做,返回值是下一次遊戲循環的狀態
    public abstract BaseState Tick();
    // 與本狀態有關的初始化代碼
    public virtual void OnStateStart(){}
    // 與本狀態有關的退出代碼
    public virtual void OnStateExit(){}
}
複製代碼

狀態機

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;
    }
}
複製代碼

我把與全部狀態相關的控制腳本寫在了EnemyController組件中,暴露出公共方法讓狀態機腳本調用。這樣能夠複用代碼,而且讓狀態機的邏輯代碼只負責更高一層的控制,而無論細節如何。

如下是追逐狀態的代碼,其餘狀態同理。

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;
        }
        
        // 距離太遠,放棄追逐
        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)歡迎轉載,請附上原文連接以及做者名字,謝謝!

相關文章
相關標籤/搜索