Box2D + WinRTでゲームを作る (15) – 状態管理をBoost.MSMで実装する

公開:2013-06-16 05:08
更新:2020-02-15 04:37
カテゴリ:boost,シューティングゲーム,c++,windows,ゲーム,box2d + winrtでゲームを作る,ゲーム製作

今週は水曜日あたりまでBoost.MSM関係のドキュメントをチェックして、木曜日からおそるおそるコードを書き始めた。チェックしたドキュメントは下記。MSMで実装しようとするとほぼ必ず読み返している。

Boost.MSMは状態とイベントを型定義で表現でする。これはメタ・プログラミングでは常道のやり方。値イコール型というやつだ。そしてその遷移をboost::mpl::vectorで遷移テーブルとして書いていく。大まかにはこれだけである。実際に書きかけのコードは以下。


    struct Menu : msmf::state<> {};

      struct Play_ : msmf::state_machine_def<Play_> 
      {
        struct Init : msmf::state<> {};
        struct Waiting : msmf::state<> {};
        struct Active : msmf::state<>{};
        struct LevelComplete : msmf::state<>{};
        struct GameComplete : msmf::state<>{};
        struct GameOver : msmf::state<>{};
        struct Pause : msmf::state<>{};
        struct Restart : msmf::state<>{};
        struct CheckRestart : msmf::state<>{};
    // 状態遷移テーブル
    struct transition_table : boost::mpl::vector
      //           現在状態      ,イベント           , 次の状態    , アクション   , ガード 
      < 
        msmf::Row <Init           ,ev::Complete       ,Waiting      ,msmf::none,msmf::none>, 
        msmf::Row <Waiting        ,ev::StartGame      ,Active       ,msmf::none, msmf::none > ,
        msmf::Row <Active         ,ev::Escape         ,Pause        ,msmf::none, msmf::none > ,
        msmf::Row <Active         ,ev::PlayerIsGone   ,CheckRestart      ,msmf::none, msmf::none > ,
 //       msmf::Row <CheckRestart   ,ev::st::any    ,Restart       ,msmf::none, msmf::none > ,
        msmf::Row <CheckRestart   ,ev::PlayerLeftZero ,GameOver     ,msmf::none, msmf::none > ,
        msmf::Row <Pause          ,ev::Exit           ,Waiting      ,msmf::none, msmf::none > ,
        msmf::Row <Pause          ,ev::Escape         ,Active       ,msmf::none, msmf::none > ,
        msmf::Row <GameOver       ,ev::Escape         ,Waiting      ,msmf::none, msmf::none > ,
        msmf::Row <GameOver       ,ev::TimeOver       ,Waiting      ,msmf::none, msmf::none >
      >
    {};
        typedef Init initial_state;
      };

      typedef boost::msm::back::state_machine< Play_ > Play;

      struct Edit : msmf::state<> {};

      struct game_main_ : msmf::state_machine_def<game_main_>
      {
      public:
        game_main_(){};
        virtual ~game_main_(){};
        // 状態遷移テーブル
        struct transition_table : boost::mpl::vector
          //            現在状態   ,イベント           ,次の状態   , アクション , ガード 
          <
           msmf::Row    <Menu      ,ev::SelectEdit     ,Edit        ,msmf::none  ,msmf::none  > ,
           msmf::Row    <Menu      ,ev::SelectPlay     ,Play        ,msmf::none  ,msmf::none  > 
          >
        {};
        typedef Menu initial_state;

      };
      typedef boost::msm::back::state_machine< game_main_ > game_main;

コードを見ればわかると思うけれども、いくつか実装のルールがある。例えば

ほかにもいろいろあるが、それはドキュメントをみればわかる。上記の例を見るとわかるように状態管理クラスは入れ子にできる。こんな感じで実装していくのである。

問題となるのはコンパイル速度である。もうすでにその影響は出ている。普通この手のものはヘッダーに定義するとのちのち痛い目にあう。コンパイルが遅いのでちょっと作ってはコンパイルしてみて試すということがきつくなってくるのだ。なので今のうちにpimpl化してヘッダーには状態管理をラップするクラスと、イベントクラスの定義のみ含むようにした。これで実装を続けていく。