• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一.引言言

            有 限狀態(tài)機是一種用來進行對象行為建模的工具,其作用主要是描述對象在它的生命周期內(nèi)所經(jīng)歷的狀態(tài)序列,以及如何響應來自外界的各種事件。在面向?qū)ο蟮能浖? 系統(tǒng)中,一個對象無論多么簡單或者多么復雜,都必然會經(jīng)歷一個從開始創(chuàng)建到最終消亡的完整過程,這通常被稱為對象的生命周期。一般說來,對象在其生命期內(nèi) 是不可能完全孤立的,它必須通過發(fā)送消息來影響其它對象,或者通過接受消息來改變自身。在大多數(shù)情況下,這些消息都只不過是些簡單的、同步的方法調(diào)用而 已。例如,在銀行客戶管理系統(tǒng)中,客戶類(Customer)的實例在需要的時候,可能會調(diào)用帳戶(Account)類中定義的getBalance()方法。在這種簡單的情況下,類Customer并不需要一個有限狀態(tài)機來描述自己的行為,主要原因在于它當前的行為并不依賴于過去的某個狀態(tài)。[1]

            遺憾的是并不是所有情況都會如此簡單,事實上許多實用的軟件系統(tǒng)都必須維護一兩個非常關鍵的對象,它們通常具有非常復雜的狀態(tài)轉(zhuǎn)換關系,而且需要對來自外部的各種異步事件進行響應。例如,在VoIP電話系統(tǒng)中,電話類(Telephone)的實例必須能夠響應來自對方的隨機呼叫,來自用戶的按鍵事件,以及來自網(wǎng)絡的信令等。在處理這些消息時,類Telephone所要采取的行為完全依賴于它當前所處的狀態(tài),因而此時使用狀態(tài)機就將是一個不錯的選擇。[1]

            游戲引擎是有限狀態(tài)機最為成功的應用領域之一,由于設計良好的狀態(tài)機能夠被用來取代部分的人工智能算法,因此游戲中的每個角色或者器件都有可能內(nèi)嵌一個狀態(tài)機。考慮RPG游戲中城門這樣一個簡單的對象,它具有打開(Opened)、關閉(Closed)、上鎖(Locked)、解鎖(Unlocked)四種狀態(tài),如圖1所示。當玩家到達一個處于狀態(tài)Locked的門時,如果此時他已經(jīng)找到了用來開門的鑰匙,那么他就可以利用它將門的當前狀態(tài)轉(zhuǎn)變?yōu)?/span>Unlocked,進一步還可以通過旋轉(zhuǎn)門上的把手將其狀態(tài)轉(zhuǎn)變?yōu)?/span>Opened,從而成功地進入城內(nèi)。[1]

            1 控制城門的狀態(tài)機

            在描述有限狀態(tài)機時,狀態(tài)、事件、轉(zhuǎn)換和動作是經(jīng)常會碰到的幾個基本概念。

            狀態(tài)(State指的是對象在其生命周期中的一種狀況,處于某個特定狀態(tài)中的對象必然會滿足某些條件、執(zhí)行某些動作或者是等待某些事件。

            事件(Event指的是在時間和空間上占有一定位置,并且對狀態(tài)機來講是有意義的那些事情。事件通常會引起狀態(tài)的變遷,促使狀態(tài)機從一種狀態(tài)切換到另一種狀態(tài)。

            轉(zhuǎn)換(Transition指的是兩個狀態(tài)之間的一種關系,表明對象將在第一個狀態(tài)中執(zhí)行一定的動作,并將在某個事件發(fā)生同時某個特定條件滿足時進入第二個狀態(tài)。

               動作(Action指的是狀態(tài)機中可以執(zhí)行的那些原子操作,所謂原子操作指的是它們在運行的過程中不能被其他消息所中斷,必須一直執(zhí)行下去。

            二、基于傳統(tǒng)C語言的FSM實現(xiàn)技術(shù)

            2.1、基于switch(狀態(tài))的實現(xiàn)

            在實現(xiàn)有限狀態(tài)機時,使用switch語句是最簡單也是最直接的一種方式,其基本思路是為狀態(tài)機中的每一種狀態(tài)都設置一個case分支,專門用于對該狀態(tài)進行控制。下面的代碼示范了如何運用switch語句,來實現(xiàn)圖1中所示的狀態(tài)機:

             

             
            switch (state)  {
              // 處理狀態(tài)Opened的分支
              case (Opened): {
                // 執(zhí)行動作Open
                open();
                // 檢查是否有CloseDoor事件
                if (closeDoor()) { 
                  // 當前狀態(tài)轉(zhuǎn)換為Closed
                  changeState(Closed)
                }
                break;
              } 
              // 處理狀態(tài)Closed的分支
              case (Closed): {
                // 執(zhí)行動作Close
                close();
                // 檢查是否有OpenDoor事件
                if (openDoor()) {
                  // 當前狀態(tài)轉(zhuǎn)換為Opened
                  changeState(Opened);
                }
                // 檢查是否有LockDoor事件
                if (lockDoor()) {
                  // 當前狀態(tài)轉(zhuǎn)換為Locked
                  changeState(Locked);
                }
                break;
              }
             
              // 處理狀態(tài)Locked的分支
              case (Locked): {
                // 執(zhí)行動作Lock
                lock();
                // 檢查是否有UnlockDoor事件
                if (unlockDoor()) {
                  // 當前狀態(tài)轉(zhuǎn)換為Unlocked
                  changeState(Unlocked);
                }
                break;
              }
             
              // 處理狀態(tài)Unlocked的分支
              case (Unlocked): {
                // 執(zhí)行動作Unlock
                unlock();
                // 檢查是否有LockDoor事件
                if (lockDoor()) {
                  // 當前狀態(tài)轉(zhuǎn)換為Locked    
                  changeState(Locked)
                }
                // 檢查是否有OpenDoor事件    
                if (openDoor()) {
                  // 當前狀態(tài)轉(zhuǎn)換為Opened
                  changeSate(Opened);
                }
                break;
              } 
            }

            使用switch語句實現(xiàn)的有限狀態(tài)機的確能夠很好地工作,但代碼的可讀性并不十分理想,主要原因是在實現(xiàn)狀態(tài)之間的轉(zhuǎn)換時,檢查轉(zhuǎn)換條件和進行狀態(tài)轉(zhuǎn)換都是混雜在當前狀態(tài)中來完成的。例如,當城門處于Opened狀態(tài)時,需要在相應的case中調(diào)用closeDoor()函數(shù)來檢查是否有必要進行狀態(tài)轉(zhuǎn)換,如果是的話則還需要調(diào)用changeState()函數(shù)將當前狀態(tài)切換到Closed。顯然,如果在每種狀態(tài)下都需要分別檢查多個不同的轉(zhuǎn)換條件,并且需要根據(jù)檢查結(jié)果讓狀態(tài)機切換到不同的狀態(tài),那么這樣的代碼將是枯燥而難懂的。從代碼重構(gòu)的角度來講,此時更好的做法是引入checkStateChange()performStateChange()兩個函數(shù),專門用來對轉(zhuǎn)換條件進行檢查,以及激活轉(zhuǎn)換時所需要執(zhí)行的各種動作。這樣一來,程序結(jié)構(gòu)將變得更加清晰:

             

             
            switch (state)  {
             
              // 處理狀態(tài)Opened的分支
              case (Opened): {
                // 執(zhí)行動作Open
                open();
                // 檢查是否有激發(fā)狀態(tài)轉(zhuǎn)換的事件產(chǎn)生
                if (checkStateChange()) {
                  // 對狀態(tài)機的狀態(tài)進行轉(zhuǎn)換
                  performStateChange();
                }
                break;
              } 
              // 處理狀態(tài)Closed的分支
              case (Closed): {
                // 執(zhí)行動作Close
                close();
                // 檢查是否有激發(fā)狀態(tài)轉(zhuǎn)換的事件產(chǎn)生
                if (checkStateChange()) {
                  // 對狀態(tài)機的狀態(tài)進行轉(zhuǎn)換
                  performStateChange();
                }
                break;
              }
             
              // 處理狀態(tài)Locked的分支
              case (Locked): {
                // 執(zhí)行動作Lock
                lock();
                // 檢查是否有激發(fā)狀態(tài)轉(zhuǎn)換的事件產(chǎn)生
                if (checkStateChange()) {
                  // 對狀態(tài)機的狀態(tài)進行轉(zhuǎn)換
                  performStateChange();
                }
                break;
              }
             
              // 處理狀態(tài)Unlocked的分支
              case (Unlocked): {
                // 執(zhí)行動作Lock
                unlock();
                // 檢查是否有激發(fā)狀態(tài)轉(zhuǎn)換的事件產(chǎn)生
                if (checkStateChange()) {
                  // 對狀態(tài)機的狀態(tài)進行轉(zhuǎn)換
                  performStateChange();
                }
                break;
              } 
            }

            checkStateChange()performStateChange()這兩個函數(shù)本身依然會在面對很復雜的狀態(tài)機時,內(nèi)部邏輯變得異常臃腫,甚至可能是難以實現(xiàn)。

            在很長一段時期內(nèi),使用switch語 句一直是實現(xiàn)有限狀態(tài)機的唯一方法,甚至像編譯器這樣復雜的軟件系統(tǒng),大部分也都直接采用這種實現(xiàn)方式。但之后隨著狀態(tài)機應用的逐漸深入,構(gòu)造出來的狀態(tài) 機越來越復雜,這種方法也開始面臨各種嚴峻的考驗,其中最令人頭痛的是如果狀態(tài)機中的狀態(tài)非常多,或者狀態(tài)之間的轉(zhuǎn)換關系異常復雜,那么簡單地使用switch語句構(gòu)造出來的狀態(tài)機將是不可維護的。

            三、基于面向?qū)ο蟮?/span>FSM實現(xiàn)技術(shù)

            3.1、用一個類實現(xiàn)FSM

             

            class DoorFSM {

            private:

               States __Y;

               std::queue<Event> __events;

               void __processEvent( Event e );

             

            protected:

               virtual void enterOpened() = 0;

               virtual void enterLocked() = 0;

               virtual void enterUnlocked() = 0;

               virtual void enterClosed() = 0;

            public:

            /* States */

               enum States { Closed, Unlocked, Locked, Opened   }; // states

            /*Events*/

               enum Event { Lock, Unlock, Open, Close   };

               /// Constructor

               DoorFSM() { __Y = Opened; }

               /// Destructor

               virtual ~DoorFSM() {}

             

               /** Get current FSM state

                   @returns current FSM state

                */

               States currentState() { return __Y; }

             

               /** Send event to FSM

                   Use this function to send event to DoorFSM After you call it given event will be handled, and, if some of transition conditions match, appropriate transition will be triggered, and currentState() will be changed. If this function is called during existing event handling process, given event will be added to pending event queue, and will be handled after current transition. See examples for details.

               */

               void A( Event e );

            };

            void DoorFSM::__processEvent( Event e )

            {

               States yOld = __Y;

               bool pass = false;

               switch( __Y ) { //transitions

               case Closed:

                  if( e == Open ) {

                     //outcome actions

                     __Y = Opened;

                     pass = true;

                  }

                  else if( e == Lock ) {

                     //outcome actions

                     __Y = Locked;

                     pass = true;

                  }

                  break;

               case Unlocked:

                  if( e == Lock ) {

                     //outcome actions

                     __Y = Locked;

                     pass = true;

                  }

                  else if( e == Open ) {

                     //outcome actions

                     __Y = Opened;

                     pass = true;

                  }

                  break;

               case Locked:

                  if( e == Unlock ) {

                     //outcome actions

                     __Y = Unlocked;

                     pass = true;

                  }

                  break;

               case Opened:

                  if( e == Close ) {

                     //outcome actions

                     __Y = Closed;

                     pass = true;

                  }

                  break;

               }

             

               if( yOld == __Y && !pass ) { return; }

             

               switch( __Y ) { // income actions

               case Closed:

                     enterClosed();

                  break;

               case Unlocked:

                     enterUnlocked();

                  break;

               case Locked:

                     enterLocked();

                  break;

               case Opened:

                     enterOpened();

                  break;

               }

            }

             

            void DoorFSM::A( Event e )

            {

               bool __empty = __events.empty();

               __events.push( e );

               if( __empty ) {

                  while( !__events.empty() ) {

                     __processEvent( __events.front() );

                     __events.pop();

                  }

               }

            }

             

             

            class DoorFSMLogic : public DoorFSM

            {

             protected:

              virtual void enterOpened(){std::cout << "Enter Opened state." << std::endl;}

              virtual void enterLocked() {std::cout << "Enter Closed state." << std::endl;}

              virtual void enterUnlocked() {std::cout << "Enter Locked state." << std::endl;}

              virtual void enterClosed() {std::cout << "Enter Unlocked state." << std::endl;}

            };

            測試程序

            int main()

            {

              DoorFSMLogic door;

              door.A(DoorFSM::Close);

              door.A(DoorFSM::Lock);

              door.A(DoorFSM::Unlock);

              door.A(DoorFSM::Open);

            }

              
            -關于FSM

            posts - 94, comments - 138, trackbacks - 0, articles - 94

            Copyright © RichardHe

            72种姿势欧美久久久久大黄蕉| 91精品国产综合久久久久久| 久久播电影网| 2020久久精品亚洲热综合一本| 久久精品国产精品亚洲精品| 精品蜜臀久久久久99网站| 狠狠色丁香婷婷久久综合不卡| 一本色道久久88加勒比—综合| 久久精品成人欧美大片| 久久夜色精品国产亚洲| 精品久久久久久无码中文字幕一区| 精品欧美一区二区三区久久久 | 久久精品免费一区二区| 久久久久久久亚洲Av无码| 亚洲国产二区三区久久| 777午夜精品久久av蜜臀| 国产精品美女久久久网AV| 国产成人久久精品一区二区三区| 国产精品99久久精品| 久久大香萑太香蕉av| 精品无码久久久久久国产| 国产成人综合久久综合 | 久久成人国产精品| 亚洲综合久久久| 久久男人中文字幕资源站| 国产精品久久久久9999高清| 蜜桃麻豆WWW久久囤产精品| 久久e热在这里只有国产中文精品99 | 天天久久狠狠色综合| 久久精品蜜芽亚洲国产AV| 久久婷婷色综合一区二区| 少妇久久久久久被弄到高潮| 久久国产综合精品五月天| 2020最新久久久视精品爱| 99久久综合狠狠综合久久| 国产精品久久久久久福利漫画| 亚洲色大成网站www久久九| 亚洲成色WWW久久网站| AV无码久久久久不卡蜜桃| 狠狠色噜噜色狠狠狠综合久久| 久久国产AVJUST麻豆|