1. 程式人生 > >基於委託與事件的有限狀態機設計與實現(Unity3d)

基於委託與事件的有限狀態機設計與實現(Unity3d)

有限狀態機設計與實現

前言

最近看到一句話,就是優秀是一種習慣,所以突然就總想寫點什麼,當作是遊戲開發這條路上的學習筆記吧,另外也時刻提醒自己需要不斷努力。

什麼是狀態機?

先貼百度百科的概念內容吧: 有限狀態機,(英語:Finite-state machine, FSM),又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。 狀態儲存關於過去的資訊,就是說:它反映從系統開始到現在時刻的輸入變化。轉移指示狀態變更,並且用必須滿足來確使轉移發生的條件來描述它。動作是在給定時刻要進行的活動的描述。有多種型別的動作: 進入動作:在進入狀態時進行 退出動作:在退出狀態時進行 輸入動作

:依賴於當前狀態和輸入條件進行 轉移動作:在進行特定轉移時進行

我自己對狀態機的理解就是,狀態機負責狀態的切換以及當前狀態的動作。 那麼這樣的話就可以設計一個狀態機負責角色的狀態切換以及執行狀態內的方法,多個狀態類需要有狀態的進入動作Enter,退出動作Exit以及執行的動作Action(轉移動作以後再說),還有一個角色類,通過角色類的動作來觸發狀態機的狀態切換來達到狀態的切換。

設計狀態機UML圖

有限狀態機的設計思路 大概是這樣的一個思路,這個圖有點舊就湊合著先用著了。

狀態機的設計思路

我是想遊戲的AI與玩家都能共用一套狀態機,這樣會不會比較方便一些,所以就給Player類跟AIEnemy類都設計了一個他們的基類AllCharacter

類,接著是狀態機FiniteStateMachine類以及他們的狀態基類BaseState類。

AllCharacter.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AllCharacter : MonoBehaviour
{
    [HideInInspector]
    public BaseState CurBaseState;

    public enum StateType
    {
        //AI狀態
        STATE_PATROL,
STATE_CHASE, //玩家狀態 STATE_STAND, STATE_MOVE, //共同狀態 STATE_ATTACK, STATE_DEAD, } public StateType CharaterState; }

Player.cs

Player類中定義了一個委託PlayerStateChange和一個PlayerStateChange的事件StateChange,在Awake中註冊屬於Player的狀態
以及註冊狀態切換事件的方法ChangeState(在FiniteStateMachine中)
public class Player : AllCharacter
{
    public delegate void PlayerStateChange(AllCharacter.StateType stateType);
    public event PlayerStateChange StateChange;

    [HideInInspector]
    public FiniteStateMachine FsMachine;

    // Use this for initialization
    void Awake ()
    {
        FsMachine = new FiniteStateMachine(this);
        //註冊角色的狀態
        FsMachine.RegisterState(new AttackState(this));
        FsMachine.RegisterState(new StandState(this));
        FsMachine.RegisterState(new DeadState(this));
        FsMachine.RegisterState(new MoveState(this));
        //註冊事件
        StateChange+=new PlayerStateChange(FsMachine.ChangeState);
    }

    void Start()
    {
        //設定角色的初始狀態
        EnterStand();
    }

    void Update () {
        FsMachine.OnUpdate();
	}

    public void StickMove()
    {
        Debug.Log("Moving the JoyStick");
        StateChange(AllCharacter.StateType.STATE_MOVE);
    }

    public void DownAttack()
    {
        Debug.Log("Down the Attack");
        StateChange(AllCharacter.StateType.STATE_ATTACK);
    }

    public void EnterStand()
    {
        StateChange(AllCharacter.StateType.STATE_STAND);
    }
    }

Player中的方法StickMove()、DownAttack()是通過EasyTouch操作呼叫的,通過這兩個方法的呼叫可以控制角色狀態的切換

BaseState.cs

public class BaseState
{
    //獲取角色
    public AllCharacter Character;
    
    //得到當前狀態
    public virtual AllCharacter.StateType GetStateType()
    {
        return Character.CharaterState;
    }
    //進入狀態
    public virtual void EnterState(FiniteStateMachine fsMachine, BaseState preState)
    {

    }
    //狀態更新
    public virtual void UpdateState()
    {

    }
    //狀態結束 退出狀態
    public virtual void ExitState(BaseState preState)
    {

    }
    //輸入控制
    public virtual void InputHandle()
    {

    }

}

MoveState.cs

這是其中一個狀態類,負責角色移動時的動作

public class MoveState : BaseState
{
    private readonly Player _player;
    public MoveState(Player player)
    {
        this._player = player;
    }
    public override AllCharacter.StateType GetStateType()
    {
        return AllCharacter.StateType.STATE_MOVE;
    }
    public override void EnterState(FiniteStateMachine fsMachine, BaseState preState)
    {
        if (preState != null)
        {
            Debug.Log("Enter the MoveState the preState is:" + preState.GetStateType());
        }
        else
        {
            Debug.Log("Enter the MoveState");
        }
        //Debug.Log(_player.CurBaseState);
    }

    public override void UpdateState()
    {
        Debug.Log("I'm Moving.");
    }

    public override void ExitState(BaseState preState)
    {
        Debug.Log("Exit Move State. " + preState.GetStateType());
    }

    public override void InputHandle()
    {

    }
}

總結

貼了一堆程式碼,我覺得還不如自己說兩句,其實有限狀態機蠻簡單的,也很基礎的東西,就是Player在把在當前的狀態類幹啥了告訴狀態機,接著狀態機根據這個幹啥了幫Player切換Player的狀態,並執行該狀態類的方法。我不懂的還是很多的,要是以上有說得不妥的地方,希望幫忙指正一下,謝謝。