一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

U3D 簡(jiǎn)單的FSM狀態(tài)機(jī)

 Ko03 2015-11-04

http://coder./archives/592

在之前的文章里介紹了一個(gè)基礎(chǔ)U3D狀態(tài)機(jī)框架(Unity3D游戲開發(fā)之狀態(tài)流框架)即大Switch的枚舉狀態(tài)控制。這種方法雖然容易理解,編程方法也相對(duì)簡(jiǎn)單,但是弊端是當(dāng)狀態(tài)變得復(fù)雜之后,或需要添加一種新的狀態(tài)時(shí),會(huì)顯得非?;靵y并且難以下手。故我們需要引進(jìn)一種更高級(jí)的狀態(tài)機(jī)技術(shù)來避免這些問題。網(wǎng)上有一些講述U3D-FSM狀態(tài)機(jī)的文章,但都不針對(duì)基礎(chǔ)講解,而且大多帶有冗余的與狀態(tài)機(jī)不相關(guān)的代碼,基礎(chǔ)不好的讀者容易看不清FSM狀態(tài)機(jī)的核心所在。這里針對(duì)網(wǎng)上的一些文章和代碼做了一個(gè)整理,意圖使之簡(jiǎn)單易懂。

這里關(guān)于FSM有限狀態(tài)機(jī)這類名詞的解釋這里就不再說明了,感興趣的朋友可以自己去百度下(度娘鏈接),本文只說重點(diǎn)。

首先是狀態(tài)機(jī)基類State.cs

復(fù)制代碼
/**
  * 狀態(tài)基類 
 */
public class State[entity_type>
{
     public entity_type Target;
     //Enter state  
     public virtual void Enter (entity_type entityType)
     {
         
     }
     //Execute state
     public virtual void Execute (entity_type entityType)
     {
         
     }
     //Exit state
     public virtual void Exit (entity_type entityType)
     {
         
     }

}
復(fù)制代碼

基類之所以設(shè)計(jì)成含有3個(gè)小的狀態(tài)方法是因?yàn)椋ǔT谟螒蛑杏行┬袨槎贾皇窃谶M(jìn)入或退出某個(gè)狀態(tài)時(shí)出現(xiàn)的,并不會(huì)發(fā)生在通常的更新步驟中。這樣設(shè)計(jì)就可以有效的將持續(xù)性調(diào)用語句和一次性調(diào)用語句有效的區(qū)分開來。(舉例:發(fā)送技能時(shí)的特效,有些是持續(xù)性而有些又是一次性的)

接下來我們編寫狀態(tài)機(jī)代碼,來使直接的這個(gè)基類的各個(gè)方法運(yùn)作起來:

復(fù)制代碼
using UnityEngine;
using System.Collections;

public class StateMachine[entity_type>
{
     private entity_type m_pOwner;

     private State[entity_type> m_pCurrentState;//當(dāng)前狀態(tài)
    private State[entity_type> m_pPreviousState;//上一個(gè)狀態(tài)
    private State[entity_type> m_pGlobalState;//全局狀態(tài)

    /*狀態(tài)機(jī)構(gòu)造函數(shù)*/
     public StateMachine (entity_type owner)
     {
         m_pOwner = owner;
         m_pCurrentState = null;
         m_pPreviousState = null;
         m_pGlobalState = null;
     }
     
     /*進(jìn)入全局狀態(tài)*/
     public void GlobalStateEnter()
     {
         m_pGlobalState.Enter(m_pOwner);
     }
     
     /*設(shè)置全局狀態(tài)*/
     public void SetGlobalStateState(State[entity_type> GlobalState)
     {
         m_pGlobalState = GlobalState;
         m_pGlobalState.Target = m_pOwner;
         m_pGlobalState.Enter(m_pOwner);
     }
     
     /*設(shè)置當(dāng)前狀態(tài)*/
     public void SetCurrentState(State[entity_type> CurrentState)
     {
         m_pCurrentState = CurrentState;
         m_pCurrentState.Target = m_pOwner;
         m_pCurrentState.Enter(m_pOwner);
     }

     /*Update*/
     public void SMUpdate ()
     {

         if (m_pGlobalState != null)
             m_pGlobalState.Execute (m_pOwner);
         
         if (m_pCurrentState != null)
             m_pCurrentState.Execute (m_pOwner);
     }

     /*狀態(tài)改變*/
     public void ChangeState (State[entity_type> pNewState)
     {
         if (pNewState == null) {
             Debug.LogError ("can't find this state");
         }
         
                 //觸發(fā)退出狀態(tài)調(diào)用Exit方法
        m_pCurrentState.Exit(m_pOwner);
         //保存上一個(gè)狀態(tài) 
        m_pPreviousState = m_pCurrentState;
         //設(shè)置新狀態(tài)為當(dāng)前狀態(tài)
        m_pCurrentState = pNewState;
         m_pCurrentState.Target = m_pOwner;
         //進(jìn)入當(dāng)前狀態(tài)調(diào)用Enter方法
        m_pCurrentState.Enter (m_pOwner);
     }

     public void RevertToPreviousState ()
     {
         //切換到前一個(gè)狀態(tài)
        ChangeState (m_pPreviousState);
         
     }

     public State[entity_type> CurrentState ()
     {
         //返回當(dāng)前狀態(tài)
        return m_pCurrentState;
     }
     public State[entity_type> GlobalState ()
     {
         //返回全局狀態(tài)
        return m_pGlobalState;
     }
     public State[entity_type> PreviousState ()
     {
         //返回前一個(gè)狀態(tài)
        return m_pPreviousState;
     }

}
復(fù)制代碼

這個(gè)狀態(tài)機(jī)其實(shí)還不是最簡(jiǎn)的,全局和上一個(gè)狀態(tài)的相關(guān)部分都可以去掉,但同時(shí)功能上就會(huì)被削減,故這里將其保留。

現(xiàn)在狀態(tài)基類和狀態(tài)機(jī)類都有了,我們可以開始編寫游戲?qū)ο蟮莫?dú)立狀態(tài)類,先編寫游戲的總流程狀態(tài)類,這里命名為MainState.cs

復(fù)制代碼
/**
  * 全局狀態(tài)
 */
public class MainState : State[Main>
{

   
     public static MainState instance;

     /*構(gòu)造函數(shù)單例化*/
     public static MainState Instance()
     {
         if (instance == null)
             instance = new MainState();

         return instance;
     }


     public override void Enter(Main Entity)
     {
         //這里添加進(jìn)入此狀態(tài)時(shí)執(zhí)行的代碼
    }

     public override void Execute(Main Entity)
     {
         //這里添加持續(xù)此狀態(tài)刷新代碼
    
     }

     public override void Exit(Main Entity)
     {
         //這里添加離開此狀態(tài)時(shí)執(zhí)行代碼
    }

}



/**
  * Ready狀態(tài)
 */
public class MainState_Ready : State[Main>
{

     public static MainState_Ready instance;

     /*構(gòu)造函數(shù)單例化*/
     public static MainState_Ready Instance()
     {
         if (instance == null)
             instance = new MainState_Ready();

         return instance;
     }


     public override void Enter(Main Entity)
     {
         //這里添加進(jìn)入此狀態(tài)時(shí)執(zhí)行的代碼
    }

     public override void Execute(Main Entity)
     {
         //這里添加持續(xù)此狀態(tài)刷新代碼
        //這里是重點(diǎn) 當(dāng)滿足某條件后 我們可以進(jìn)行狀態(tài)切換 執(zhí)行如下代碼 切換到 Run狀態(tài)
        Entity.GetFSM().ChangeState(MainState_Run.Instance());  
     }
     public override void Exit(Main Entity)
     {
         //這里添加離開此狀態(tài)時(shí)執(zhí)行代碼
    }
}


/**
  * Run狀態(tài)
 */
public class MainState_Run : State[Main>
{
     public static MainState_Run instance;
     /*構(gòu)造函數(shù)單例化*/
     public static MainState_Run Instance()
     {
         if (instance == null)
             instance = new MainState_Run();
         return instance;
     }

     public override void Enter(Main Entity)
     {
         //這里添加進(jìn)入此狀態(tài)時(shí)執(zhí)行的代碼
    }

     public override void Execute(Main Entity)
     {
         //這里添加持續(xù)此狀態(tài)刷新代碼
        //當(dāng)滿足某條件后 我們可以繼續(xù)進(jìn)行狀態(tài)切換 執(zhí)行如下代碼 切換到 Over狀態(tài)
        Entity.GetFSM().ChangeState(MainState_Over.Instance()); 
     }

     public override void Exit(Main Entity)
     {
         //這里添加離開此狀態(tài)時(shí)執(zhí)行代碼
    }
}

/**
  * Over狀態(tài)
 */
public class MainState_Over : State[Main>
{
     public static MainState_Over instance;
     /*構(gòu)造函數(shù)單例化*/
     public static MainState_Over Instance()
     {
         if (instance == null)
             instance = new MainState_Over();
         return instance;
     }

     public override void Enter(Main Entity)
     {
         //這里添加進(jìn)入此狀態(tài)時(shí)執(zhí)行的代碼
    }

     public override void Execute(Main Entity)
     {
        //這里添加持續(xù)此狀態(tài)刷新代碼
       //如之前兩個(gè)狀態(tài)類一樣 同理 當(dāng)滿足一定狀態(tài)后 可以切換回Ready狀態(tài)
       Entity.GetFSM().ChangeState(MainState_Ready.Instance()); 
     }

     public override void Exit(Main Entity)
     {
         //這里添加離開此狀態(tài)時(shí)執(zhí)行代碼
    }
}
復(fù)制代碼

代碼有點(diǎn)長(zhǎng),主要是為了讓大家能夠看清楚如何進(jìn)行一個(gè)狀態(tài)的編寫,其實(shí)基類都是一樣的,都是重復(fù)內(nèi)容。 這里我們看到,除了定義一個(gè)全局的狀態(tài)類之外,我們還添加了Ready、Run、Over三個(gè)狀態(tài)。重點(diǎn)注意一下Execute函數(shù),這里是狀態(tài)切換的關(guān)鍵,當(dāng)帶此狀態(tài)綁定的對(duì)象Update時(shí)就在不停的執(zhí)行Execute里的代碼段,當(dāng)滿足一定條件后,即達(dá)成狀態(tài)的切換。

這里我們看一下之前的狀態(tài)機(jī)代碼里的ChangeState方法,就知道整個(gè)狀態(tài)切換是如何工作的了:

復(fù)制代碼
/*狀態(tài)改變*/
     public void ChangeState (State[entity_type> pNewState)
     {
         if (pNewState == null) {
             Debug.LogError ("can't find this state");
         }
         
         //觸發(fā)退出狀態(tài)調(diào)用Exit方法
        m_pCurrentState.Exit(m_pOwner);
         //保存上一個(gè)狀態(tài) 
        m_pPreviousState = m_pCurrentState;
         //設(shè)置新狀態(tài)為當(dāng)前狀態(tài)
        m_pCurrentState = pNewState;
         m_pCurrentState.Target = m_pOwner;
         //進(jìn)入當(dāng)前狀態(tài)調(diào)用Enter方法
        m_pCurrentState.Enter (m_pOwner);
     }
復(fù)制代碼

可以看到當(dāng)狀態(tài)切換時(shí),會(huì)自動(dòng)觸發(fā)當(dāng)前狀態(tài)的Exit方法和目標(biāo)狀態(tài)的Enter方法。這樣就完成了一整個(gè)狀態(tài)的切換過程。

到這里整個(gè)有限狀態(tài)機(jī)體系基本就算完工了,剩下的是如何在Main里進(jìn)行MainState類的創(chuàng)建及使用,Main.cs代碼如下:

復(fù)制代碼
using UnityEngine;
using System.Collections;

public class Main : MonoBehaviour{
     
     StateMachine[Main> m_pStateMachine;//定義一個(gè)狀態(tài)機(jī)

    void Start () {
             
         m_pStateMachine = new StateMachine[Main>(this);//初始化狀態(tài)機(jī)
        m_pStateMachine.SetCurrentState(MainState_Ready.Instance()); //設(shè)置一個(gè)當(dāng)前狀態(tài)
        m_pStateMachine.SetGlobalStateState(MainState.Instance());//設(shè)置全局狀態(tài)
    }
     
     void Update ()
     {   
         m_pStateMachine.SMUpdate();
     }

         /*返回狀態(tài)機(jī)*/
     public StateMachine[Main> GetFSM ()
     {
         return m_pStateMachine;
     }
     
}
復(fù)制代碼

寫到這里我們整個(gè)狀態(tài)機(jī)的框架及使用流程就基本結(jié)束了,這里要注意幾個(gè)問題: ①不要在SetCurrentState()方法調(diào)用前,調(diào)用ChangeState()方法,否則會(huì)出現(xiàn)null對(duì)象錯(cuò)誤,具體原因很簡(jiǎn)單,看一下ChangeState()里的代碼調(diào)用了哪些變量就知道了。 ②狀態(tài)間的通信,這個(gè)狀態(tài)機(jī)其實(shí)還是有未完善的地方的,目前狀態(tài)間的通知是通過直接調(diào)用其他狀態(tài)機(jī)的ChangeState()方法實(shí)現(xiàn)的,這樣勢(shì)必要先獲取該對(duì)象的腳本,這個(gè)功能待完善吧。 ③在U3D里每個(gè)游戲?qū)ο蟪跏蓟⒄{(diào)用Start()方法的時(shí)機(jī)是不一樣的,所以要注意,開始游戲時(shí)不要直接進(jìn)入開始狀態(tài),而是要有一個(gè)等待態(tài)來讓所有的游戲?qū)ο笸瓿蒘tart()方法后再調(diào)用這些對(duì)象的狀態(tài)機(jī)。

另外,多個(gè)狀態(tài)機(jī)間的通信,就像上文②中所述那樣,僅僅是通過調(diào)用ChangeState()方法來實(shí)現(xiàn),并不是非常完善,所以暫時(shí)不做講解,以免誤導(dǎo)大家,待日后有較好解決方案再另行開篇。 此FSM狀態(tài)機(jī)僅為一個(gè)雛形,還有很多功能及優(yōu)化要做,但對(duì)于入門FSM有限狀態(tài)機(jī)來說,已經(jīng)實(shí)現(xiàn)了其最主要的功能。不足之處歡迎大家提出討論,并幫助加以完善。

謝謝關(guān)注。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产精品制服丝袜美腿丝袜| 内射精品欧美一区二区三区久久久| 五月综合激情婷婷丁香| 欧美精品亚洲精品日韩专区| 日本99精品在线观看| 黄色国产精品一区二区三区| 五月婷婷综合缴情六月| 国产欧美日韩精品自拍 | 久久天堂夜夜一本婷婷| 精品人妻一区二区三区免费| 久久精视频免费视频观看| 精品人妻一区二区三区四区久久| 亚洲精品一区三区三区| 亚洲av首页免费在线观看| 国内精品偷拍视频久久| 中国少妇精品偷拍视频| 天堂网中文字幕在线视频| 69久久精品亚洲一区二区| 中文字幕人妻一区二区免费 | 久久这里只有精品中文字幕| 亚洲精品一区二区三区日韩| 大香蕉再在线大香蕉再在线| 国产一级内射麻豆91| 日本少妇三级三级三级| 五月激情五月天综合网| 亚洲中文字幕在线视频频道| 日韩av欧美中文字幕| 国产91麻豆精品成人区| 丰满的人妻一区二区三区| 国产成人精品在线一区二区三区 | 久久亚洲国产视频三级黄| 色一情一乱一区二区三区码| 午夜免费精品视频在线看| 亚洲国产91精品视频| 人妻少妇av中文字幕乱码高清| 亚洲一区二区三区一区| 中国美女偷拍福利视频| 亚洲淫片一区二区三区| 欧美日韩综合综合久久久| 精品国产成人av一区二区三区| 91久久精品中文内射|