遊戲狀態機的設計與實現

前言:
     遊戲編程中對狀態機的理解和應用,是體現程序員是否對遊戲編程入門的重要指標。本篇文章描述狀態機的原理,以及如何實現。並探討狀態機的擴展性和易用性。


什麼是狀態機:
     1、狀態機是通過狀態變量來描述不同狀態
     2、狀態機變量是互斥的
     3、狀態機的分割是狀態機好壞的標準

狀態機的好處:
     1、降低整個系統的複雜性
     2、容易擴展
     3、容易維護

如何實現狀態機:
     1、通過不同的狀態分割邏輯
     2、通過面向對象思想來擴展和分割邏輯

狀態機簡單類型:

1、定義狀態機類型

  1. enum PlayerState{  
  2.     INVALID,  
  3.     STAND,  
  4.     MOVE,  
  5.     ATTACK,  
  6.     DIE  
  7. };  

2、實現更新狀態,在不同的狀態執行不同的邏輯

  1. void Player::Update(float ts){  
  2.     switch(user_state_){  
  3.         case STAND:  
  4.             Stand(ts);  
  5.             break;  
  6.         case MOVE:  
  7.             Move(ts);  
  8.             break;  
  9.         case ATTACK:  
  10.             Attack(ts);  
  11.             break;  
  12.         case DIE:  
  13.             Die();  
  14.             return;  
  15.         default:  
  16.             std::cout<<"error\n";  
  17.   
  18.     }  
  19.   
  20.     if(hp_ <= 0){  
  21.         SetState(DIE);  
  22.     }  
  23. }  
3、切換狀態,在切換狀態的時候做一些事情

  1. void Player::SetState(PlayerState state){  
  2.     if(state == user_state_){  
  3.         return;  
  4.     }  
  5.     switch(state){  
  6.         case STAND:  
  7.             std::cout << "----begin stand--------\n";  
  8.             break;  
  9.         case MOVE:  
  10.             std::cout << "----begin move--------\n";  
  11.             break;  
  12.         case ATTACK:  
  13.             std::cout << "----begin attack--------\n";  
  14.             break;  
  15.         case DIE:  
  16.             std::cout << "----begin die--------\n";  
  17.             break;  
  18.         default:  
  19.             std::cout <<"the state is error";  
  20.             break;  
  21.     }  
  22.     user_state_ = state;  
  23. }  

     這種狀態機小而精悍,如果在一個對象中有很多標誌量來標記實例的狀態,這時候該考慮下通過這種小型的狀態機來實現了。但是這種狀態機如果狀態變量比較多,擴展性並不好。並且複雜性會隨着狀態機的增多,指數型增加。整個編譯單元的代碼量也會很大,對易讀性和維護性都是負面影響。

狀態機面向對象類型:
     面向對象類的狀態機是一種更容易擴展的新型狀態機,通過單間實現方式,使用更少的內存,先看下整個狀態機的uml設計圖。



  首先是通過接口定義通用狀態機接口,然後定義了單間的接口。這種方式統一讓所有的狀態實現三個函數,這三個函數分別對應切入狀態,在狀態中,退出狀態,需要執行的邏輯分別放在這三個函數裏執行,通過這樣的分割,狀態很容易擴展,也不會混亂。具體代碼實現,請看下面說面裏面github的地址。
     在StateManager是專門管理角色狀態的管理類,每個角色對象包含一個狀態機管理類。

總結:
     狀態機的模型是非常簡單,但並不是每個人都能設計好的狀態機。因爲好的狀態機不僅需要對程序的把握要比較到位,同時需要對整個業務的理解比較到位。好的狀態機使程序變的更加簡潔,易擴展,容易查找bug,還非常穩定。壞得狀態分割只會讓程序晦澀難懂。

說明:
1、通過兩個狀態機實現了兩個簡單的猜數打怪獸遊戲。
2、所有完整程序都可以到這個地址查看,下載,修改。
3、整個代碼都是通過C++ 完成的,編譯環境是osx 10.10 + LLVM 6.0 , C++使用 -std=c++1y。程序寫了makefile,所以如果在其他平臺只需要簡單修改下makefile就可以快樂的玩耍了。

什麼是狀態機:
     1、狀態機是通過狀態變量來描述不同狀態
     2、狀態機變量是互斥的
     3、狀態機的分割是狀態機好壞的標準

狀態機的好處:
     1、降低整個系統的複雜性
     2、容易擴展
     3、容易維護

如何實現狀態機:
     1、通過不同的狀態分割邏輯
     2、通過面向對象思想來擴展和分割邏輯

狀態機簡單類型:

1、定義狀態機類型

  1. enum PlayerState{  
  2.     INVALID,  
  3.     STAND,  
  4.     MOVE,  
  5.     ATTACK,  
  6.     DIE  
  7. };  

2、實現更新狀態,在不同的狀態執行不同的邏輯

  1. void Player::Update(float ts){  
  2.     switch(user_state_){  
  3.         case STAND:  
  4.             Stand(ts);  
  5.             break;  
  6.         case MOVE:  
  7.             Move(ts);  
  8.             break;  
  9.         case ATTACK:  
  10.             Attack(ts);  
  11.             break;  
  12.         case DIE:  
  13.             Die();  
  14.             return;  
  15.         default:  
  16.             std::cout<<"error\n";  
  17.   
  18.     }  
  19.   
  20.     if(hp_ <= 0){  
  21.         SetState(DIE);  
  22.     }  
  23. }  
3、切換狀態,在切換狀態的時候做一些事情

  1. void Player::SetState(PlayerState state){  
  2.     if(state == user_state_){  
  3.         return;  
  4.     }  
  5.     switch(state){  
  6.         case STAND:  
  7.             std::cout << "----begin stand--------\n";  
  8.             break;  
  9.         case MOVE:  
  10.             std::cout << "----begin move--------\n";  
  11.             break;  
  12.         case ATTACK:  
  13.             std::cout << "----begin attack--------\n";  
  14.             break;  
  15.         case DIE:  
  16.             std::cout << "----begin die--------\n";  
  17.             break;  
  18.         default:  
  19.             std::cout <<"the state is error";  
  20.             break;  
  21.     }  
  22.     user_state_ = state;  
  23. }  

     這種狀態機小而精悍,如果在一個對象中有很多標誌量來標記實例的狀態,這時候該考慮下通過這種小型的狀態機來實現了。但是這種狀態機如果狀態變量比較多,擴展性並不好。並且複雜性會隨着狀態機的增多,指數型增加。整個編譯單元的代碼量也會很大,對易讀性和維護性都是負面影響。

狀態機面向對象類型:
     面向對象類的狀態機是一種更容易擴展的新型狀態機,通過單間實現方式,使用更少的內存,先看下整個狀態機的uml設計圖。



  首先是通過接口定義通用狀態機接口,然後定義了單間的接口。這種方式統一讓所有的狀態實現三個函數,這三個函數分別對應切入狀態,在狀態中,退出狀態,需要執行的邏輯分別放在這三個函數裏執行,通過這樣的分割,狀態很容易擴展,也不會混亂。具體代碼實現,請看下面說面裏面github的地址。
     在StateManager是專門管理角色狀態的管理類,每個角色對象包含一個狀態機管理類。

總結:
     狀態機的模型是非常簡單,但並不是每個人都能設計好的狀態機。因爲好的狀態機不僅需要對程序的把握要比較到位,同時需要對整個業務的理解比較到位。好的狀態機使程序變的更加簡潔,易擴展,容易查找bug,還非常穩定。壞得狀態分割只會讓程序晦澀難懂。

說明:
1、通過兩個狀態機實現了兩個簡單的猜數打怪獸遊戲。
2、所有完整程序都可以到這個地址查看,下載,修改。
3、整個代碼都是通過C++ 完成的,編譯環境是osx 10.10 + LLVM 6.0 , C++使用 -std=c++1y。程序寫了makefile,所以如果在其他平臺只需要簡單修改下makefile就可以快樂的玩耍了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章