複雑なデータの取得

  • 複雑なものをC++で直接相手にせず、Luaで平らにして取り扱いやすくする

    Lua側で複雑な入り組んだデータを定義し、これをScenarioModで取得する場合、
    複雑なデータのまま、画用紙に書いてしまうと、SceanrioMod側の負担が高まります。

    なぜなら、ScenarioModで簡単に画用紙から取得できるのは、「少数」もしくは「文字列」であり、
    複雑なデータ構造となると、素のLuaの比較的複雑で取り回しにくいAPIを直接扱うことになるからです。

    今回は、以下のようなデータが、個数不明のまま定義されたとしても、問題なくSceanrioModで取得する
    ということを目指します。

  • ファイルの読み込み部

    以下のように対象となるtxtファイルを「天翔記フォルダ」に用意します。

    • BushouData.txt

      local data_list = {
        {['名前']="武田信玄" , ['政治']=100, ['戦闘']= 90, ['智謀']=80},
        {['名前']="上杉謙信" , ['政治']=70,  ['戦闘']=120, ['智謀']=70},
        {['名前']="北条氏康" , ['政治']=105, ['戦闘']= 80, ['智謀']=90},
      }
      
    • カスタム駆動関数.cpp

      void 複雑なパラメータの取得() {
        try {
          // ファイル存在簡易チェック。win32api
          if ( PathFileExists( "BushouData.txt" ) ) {
            LUA::Do_ファイル("BushouData.txt");
          }
        } catch(...) {
          デバッグ出力 << "LUAファイル実行エラー" << endl;
          return; // このレベルでエラーはヤバイ。パラメタ読み取りはしない。
        }
      }
      
      void カスタム::On_起動時() {
         複雑なパラメータの取得()
      }
      
  • Luaで値を返す関数を作成

    先述のBushouData.txtのデータは、「テーブルのなかに、さらにテーブル」が入っており、入り組んでいますので、
    画用紙に書かれているこのままの形だと、C++側で値を取得するのは、とても大変です。

    そこで、Luaで、以下のような関数を作り、
    「Get_グローバル数値」や「Get_グローバル文字列」で値を引き出せるような形で取れる準備をします。

    dprintしてみると、その様子がわかります。

    • BushouData.txt

      local data_list = {
        {['名前']="武田信玄" , ['政治']=100, ['戦闘']= 90, ['智謀']=80},
        {['名前']="上杉謙信" , ['政治']=70,  ['戦闘']=120, ['智謀']=70},
        {['名前']="北条氏康" , ['政治']=105, ['戦闘']= 80, ['智謀']=90},
      }
      
      -- とある ix 番目のデータを、平べったく値だけ並べて返す。
      -- もし ix 番目がデータの終わりなら、'終了' という文字列を返す。
      function GetNextDataList(ix)
          -- ix番目のデータに別名「p」を付ける。(コピーではない、別名)
          local p = data_list[ix]
          if p then
              -- luaは複数の値を並べて返すことが出来る。
              return '続く', p['名前'], p['政治'], p['戦闘'], p['智謀']
          else
              return '終了'
          end
      end
      
      dprint( GetNextDataList(1) )
      dprint( GetNextDataList(2) )
      dprint( GetNextDataList(3) )
      dprint( GetNextDataList(4) )
      dprint( GetNextDataList(5) )
      

      さて、直接ベタ書きした部分は、 具体的なデータ数を指定せずとも、for ループにできるでしょう。

      又、次のように 一端グローバル変数に受け取って画用紙へと書き 画用紙経由で取り扱うことが可能だと気づくでしょうか。

    • forループにする実験

      local data_list = {
        {['名前']="武田信玄" , ['政治']=100, ['戦闘']= 90, ['智謀']=80},
        {['名前']="上杉謙信" , ['政治']=70,  ['戦闘']=120, ['智謀']=70},
        {['名前']="北条氏康" , ['政治']=105, ['戦闘']= 80, ['智謀']=90},
      }
      
      function GetNextDataList(ix)
          local p = data_list[ix]
          if p then
              return '続く', p['名前'], p['政治'], p['戦闘'], p['智謀']
          else
              return '終了'
          end
      end
      
      
      for i=1, 10000 do
          _is_end, _pname, _pgov, _pbat, _pint = GetNextDataList(i)
          if _is_end == '終了' then
              break;
          else
              dprint(_pname, _pgov, _pbat, _pint)
          end
      end
      

      ということは、C++側から、この for ループのような処理をしてやれば、値を受け取れそうだ、ということがわかります。

    • 最終的なBushouData.txt

      local data_list = {
        {['名前']="武田信玄" , ['政治']=100, ['戦闘']= 90, ['智謀']=80},
        {['名前']="上杉謙信" , ['政治']=70,  ['戦闘']=120, ['智謀']=70},
        {['名前']="北条氏康" , ['政治']=105, ['戦闘']= 80, ['智謀']=90},
      }
      
      function GetNextDataList(ix)
          local p = data_list[ix]
          if p then
              return '続く', p['名前'], p['政治'], p['戦闘'], p['智謀']
          else
              return '終了'
          end
      end
      
    • 最終的なカスタム駆動関数.cpp

      struct myパラメタ型 {
        int 番号;
        string 名前;
        int 政治;
        int 戦闘;
        int 智謀;
      };
      
      vector<myパラメタ型> v_data_list;
      
      
      void 複雑なパラメータの取得() {
        try {
          // ファイル存在簡易チェック。win32api
          if ( PathFileExists( "BushouData.txt" ) ) {
            LUA::Do_ファイル("BushouData.txt");
          }
        } catch(...) {
          デバッグ出力 << "LUAファイル実行エラー" << endl;
          return; // このレベルでエラーはヤバイ。パラメタ読み取りはしない。
        }
      
        // luaの配列の添え字は「1」からスタートする。「i」を増やしながら無限ループ
        for (int i=1; true; i++) {
      
          // 1人分読み取って、画用紙へと受け取る。
          try {
            stringstream cmd;
            cmd << "_is_end, _pname, _pgov, _pbat, _pint = GetNextDataList(" << i << ")";
            LUA::Do_コマンド(cmd.str());  // _ret_en, _pname, ... というように、ScenarioMod側で好きな変数を指定して、画用紙へと書く。
          } catch(...) {
            break; // 関数実行のエラーがあれば、読み取り終了。forを抜ける
          }
      
          // もう「データ読み取りが既に終了」というシグナルが返ってきてるようなら、forループを抜ける
          try {
            // リストが終わりというシグナルが返ってきたなら、読み取り終了。forを抜ける
            if ( LUA::Get_グローバル文字列("_is_end") == "終了" ) {
              break; // 読み取り終了
            }
          } catch(...) {
            // _is_endの処理でエラーが起きるようでは、無限ループに陥る可能性がある。
            // パラメタ読み取りを即座に終了する。
            return;
          }
      
          // 画用紙から読み取って、C++内の変数へと追加保存する。
          try {
            // 画用紙からの現在の値の読み取り。
            myパラメタ型 data;
            data.番号 = i;
            data.名前 = LUA::Get_グローバル文字列("_pname");
            data.政治 = (int)LUA::Get_グローバル数値("_pgov");
            data.戦闘 = (int)LUA::Get_グローバル数値("_pbat");
            data.智謀 = (int)LUA::Get_グローバル数値("_pint");
      
            // ScenarioMod側で用意したパラメタリストへと追加する。
            v_data_list.push_back(data);
          } catch(...) {
            // 例外が起きてもとりあえずスルーして次のデータを読み込む。
          }
        }
      }
      
      void カスタム::On_起動時() {
        複雑なパラメータの取得();
      
        for each(myパラメタ型 data in v_data_list) {
          デバッグ出力 << data.番号 << endl;
          デバッグ出力 << data.名前 << endl;
          デバッグ出力 << data.政治 << endl;
          デバッグ出力 << data.戦闘 << endl;
          デバッグ出力 << data.智謀 << endl;
          デバッグ出力 << string(20, '-') << endl;
        }
      }