複雑なデータの取得

複雑なものを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) )
    

    PICTURE

    さて、直接ベタ書きした部分は、 具体的なデータ数を指定せずとも、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
    

    PICTURE

    ということは、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;
      }
    }