601.CSVからのデータの読み込み

 ゲーム制作前半であれば、配置されるオブジェクトの初期位置やオブジェクトのタイプはソースに即値(いわゆるマジックナンバー)を記述して作成してもいいのですが、制作後半にはマジックナンバーはできるだけ排除しなければいけません。
 また複数のマップだけが違うステージを作成する場合も、マップごとにステージを定義していたのでは拡張性が失われます。
 ですので、何らかの形でステージをデータ化することで、各ステージはそのデータを読み込んでステージを構築するようにします。
 ステージをデータ化の方法はいくつかありますが、まず、一番単純でわかりやすいCSVデータを読み込む方法を説明します。
 CSVデータはエクセルなどの表計算ソフトでも作成することが可能で、テキストファイルなので扱いやすいです。

CsvFileクラス

 FullSample601を実行してみましょう。以下のような画面が出ます。

 

図0601a

 

 このステージはCSVファイルから読み込んで構成しています(プレイヤー以外)。
 CSVファイルCsvFileクラスを使って読み込みます。データを読み込む方法は大きく2つの方法があります。

Csvをマップで読み込む

 まず、直感的な方法がこの方法です。ステージのマップと同じイメージのマス目のようなCSVファイルを作成し、それを読み込む方法です。以下のようなCSVファイルを作成します。
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,2,0,0,0,0,1,0,0,0,0,0,0,0,0,0,A,0,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

 0が並んでいますが、ところどころに1とか23とかがあります。このCSVは、全体がステージのマップを表し、1は敵1、2は敵2、3は敵3を表します。Aはプレイヤーを表し、このサンプルではプレイヤーはデータからは読み込みません。
 上記のCSVファイルはmediaディレクトリにGameStageA.csvがありますので見てみましょう。

 さて、このようにマップ化されたCSVを読み込むのは、まず、CsvFileクラスを準備します。
 GameStageのヘッダ部に以下の記述があります。
class GameStage : public Stage {
    //CSVファイルそのA
    CsvFile m_GameStageCsvA;
    //CSVファイルそのB
    CsvFile m_GameStageCsvB;
    //以下略
};
 CsvFileクラスが2つあります。これらの初期化をOnCreate()関数で行います。
void GameStage::OnCreate() {
    try {
        wstring DataDir;
        App::GetApp()->GetDataDirectory(DataDir);
        ////CSVファイルそのAの読み込み
        m_GameStageCsvA.SetFileName(DataDir + L"GameStageA.csv");
        m_GameStageCsvA.ReadCsv();
        ////CSVファイルそのBの読み込み
        m_GameStageCsvB.SetFileName(DataDir + L"GameStageB.csv");
        m_GameStageCsvB.ReadCsv();
        //中略
    }
    catch (...) {
        throw;
    }
}
 これで、m_GameStageCsvAGameStageA.csvの内容がセットされました。m_GameStageCsvBについてはのちほど説明します。
 このようにして読み込んだCSVはEnemyの展開用のものです。ですからGameStage::CreateEnemy()で反映させます。
//敵の作成
void GameStage::CreateEnemy() {
    //オブジェクトのグループを作成する
    auto Group = CreateSharedObjectGroup(L"EnemyGroup");
    //CSVの全体の配列
    //CSVからすべての行を抜き出す
    auto& LineVec = m_GameStageCsvA.GetCsvVec();
    for (size_t i = 0; i < LineVec.size();i++) {
        //トークン(カラム)の配列
        vector<wstring> Tokens;
        //トークン(カラム)単位で文字列を抽出(L','をデリミタとして区分け)
        Util::WStrToTokenVector(Tokens, LineVec[i], L',');
        for (size_t j = 0; j < Tokens.size(); j++) {
            //XとZの位置を計算
            float XPos = (float)((int)j - 19);
            float ZPos = (float)(19 - (int)i);
            if (Tokens[j] == L"1") {
                AddGameObject<Enemy1>(Vec3(XPos,0.25f,ZPos));
            }
            else if (Tokens[j] == L"2") {
                AddGameObject<Enemy2>(Vec3(XPos, 0.25f, ZPos));

            }
            else if (Tokens[j] == L"3") {
                AddGameObject<Enemy3>(Vec3(XPos, 0.25f, ZPos));

            }
        }
    }
}
 CsvFileクラスはデータを行単位で扱います。このことはすごく重要なので覚えておきましょう。
 上記の
    //CSVの全体の配列
    //CSVからすべての行を抜き出す
    auto& LineVec = m_GameStageCsvA.GetCsvVec();
 はコメントのようにデータをすべて読む関数です。この操作によりLineVecには行の配列が代入されます。
 続く
    for (size_t i = 0; i < LineVec.size();i++) {
 では、それを各行に分けます。そして
        //トークン(カラム)単位で文字列を抽出(L','をデリミタとして区分け)
        Util::WStrToTokenVector(Tokens, LineVec[i], L',');
 で各行をに分けます。Util::WStrToTokenVector()関数は、文字列を指定したデリミタ(区切り文字)によってわけその配列を作成します。
 各列の配列はTokensにセットされますので、下の階層のループで
        for (size_t j = 0; j < Tokens.size(); j++) {
 とスキャンします。その中には0123もしくはAが入ってます。
 Aはプレイヤーの位置ですから無視するとして、123の場合は、その位置に敵を配置します。
            //XとZの位置を計算
            float XPos = (float)((int)j - 19);
            float ZPos = (float)(19 - (int)i);
 はマップ上の位置を計算(調整)しているところで、その後以下のように
            if (Tokens[j] == L"1") {
                AddGameObject<Enemy1>(Vec3(XPos,0.25f,ZPos));
            }
 と、数字によってエネミーのタイプを選んで構築します。

Csvを行の抽出で読み込む

 さて、マップ上のセルに収まるオブジェクトの場合は、これでいいのですがセルをまたぐオブジェクトの場合は、このデータ形式では厄介です。例えば以下のようなCSVの場合です
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4
...以下省略
 ここで4は、壁を表します。しかしこのデータ構造では40個の壁が横に並んでしまいます。
 これではコストが膨大になります。
 もちろん同じ数字が続いたら同じオブジェクトが続くとするという形で読み込むことは可能です。その場合読み込みながら最適化するというのが必要になり、それもまた厄介です。
 そこで登場するのが行の抽出での読み込みです。
 CsvFileクラスには行を抽出する関数であるGetSelect()関数及びGetSelect2()関数があります。
 まず、GameStageB.csvは以下のようなCSV構造になってます。
Name,Scale,,,Rot,,,Pos,,
TilingFixedBox,40,1,1,0,0,0,0,0.5,19.5
TilingFixedBox,40,1,1,0,0,0,0,0.5,-19.5
TilingFixedBox,40,1,1,0,XM_PIDIV2,0,19.5,0.5,0
TilingFixedBox,40,1,1,0,XM_PIDIV2,0,-19.5,0.5,0
TilingPlate,40,40,1,XM_PIDIV2,0,0,0,0,0
 ここには1行で1つのオブジェクトの定義が記載されています。
 TilingFixedBoxです。TilingPlateプレートです。CsvFileクラスはこれらを別々に抽出することが可能になってます。
 まずTilingFixedBoxから説明します。GameStage::CreateFixedBox()関数を見てください。
//ボックスの作成
void GameStage::CreateFixedBox() {
    //CSVの行単位の配列
    vector<wstring> LineVec;
    //0番目のカラムがL"TilingFixedBox"である行を抜き出す
    m_GameStageCsvB.GetSelect(LineVec, 0, L"TilingFixedBox");
    for (auto& v : LineVec) {
        //トークン(カラム)の配列
        vector<wstring> Tokens;
        //トークン(カラム)単位で文字列を抽出(L','をデリミタとして区分け)
        Util::WStrToTokenVector(Tokens, v, L',');
        //各トークン(カラム)をスケール、回転、位置に読み込む
        Vec3 Scale(
            (float)_wtof(Tokens[1].c_str()), 
            (float)_wtof(Tokens[2].c_str()), 
            (float)_wtof(Tokens[3].c_str())
        );
        Vec3 Rot;
        //回転は「XM_PIDIV2」の文字列になっている場合がある
        Rot.x = (Tokens[4] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[4].c_str());
        Rot.y = (Tokens[5] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[5].c_str());
        Rot.z = (Tokens[6] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[6].c_str());
        Vec3 Pos(
            (float)_wtof(Tokens[7].c_str()),
            (float)_wtof(Tokens[8].c_str()),
            (float)_wtof(Tokens[9].c_str())
        );
        //各値がそろったのでオブジェクト作成
        AddGameObject<TilingFixedBox>(Scale, Rot, Pos, 1.0f, 1.0f);
    }
}
 ここでまずポイントとなるのは
    //CSVの行単位の配列
    vector<wstring> LineVec;
    //0番目のカラムがL"TilingFixedBox"である行を抜き出す
    m_GameStageCsvB.GetSelect(LineVec, 0, L"TilingFixedBox");
 です。m_GameStageCsvBCsvFileクラスのインスタンスです。あらかじめGameStageB.csvの内容が読み込まれています。これをGetSelect()関数行の抽出を行います。最初の引数のLineVecは抽出された行の配列が返される文字列の配列です。2番目の00番目のカラム(列)という意味です。3番目の引数 L"TilingFixedBox"は指定したカラムがこの文字列だったらとおいう意味です。
 ですから上記の抽出条件は0番目のカラムがL"TilingFixedBox"である行を抽出しなさいという意味です。
 こうすると、LineVecには
TilingFixedBox,40,1,1,0,0,0,0,0.5,19.5
TilingFixedBox,40,1,1,0,0,0,0,0.5,-19.5
TilingFixedBox,40,1,1,0,XM_PIDIV2,0,19.5,0.5,0
TilingFixedBox,40,1,1,0,XM_PIDIV2,0,-19.5,0.5,0
 だけが抽出されます。
 それぞれの行には名前のあとにスケーリング、回転、位置が3つのカラムを使用してセットされています。回転に関しては、2分のパイを表すXM_PIDIV2が入っている可能性があります。これらを加味して、各値を読み込みます。
        //トークン(カラム)の配列
        vector<wstring> Tokens;
        //トークン(カラム)単位で文字列を抽出(L','をデリミタとして区分け)
        Util::WStrToTokenVector(Tokens, v, L',');
        //各トークン(カラム)をスケール、回転、位置に読み込む
        Vec3 Scale(
            (float)_wtof(Tokens[1].c_str()), 
            (float)_wtof(Tokens[2].c_str()), 
            (float)_wtof(Tokens[3].c_str())
        );
        Vec3 Rot;
        //回転は「XM_PIDIV2」の文字列になっている場合がある
        Rot.x = (Tokens[4] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[4].c_str());
        Rot.y = (Tokens[5] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[5].c_str());
        Rot.z = (Tokens[6] == L"XM_PIDIV2") ? XM_PIDIV2 : (float)_wtof(Tokens[6].c_str());
        Vec3 Pos(
            (float)_wtof(Tokens[7].c_str()),
            (float)_wtof(Tokens[8].c_str()),
            (float)_wtof(Tokens[9].c_str())
        );
        //各値がそろったのでオブジェクト作成
        AddGameObject<TilingFixedBox>(Scale, Rot, Pos, 1.0f, 1.0f);
 で、TilingFixedBoxに渡す変数をセットして、オブジェクトを配置します。

 このようにCsvFile::GetSelect()関数を使うことで複数のタイプのオブジェクトが定義されているCSVファイルから特定の行を抽出することが可能です。
 またCsvFile::GetSelect()関数には多重定義されたもう一つの関数があり、こちらは関数へのポインタコールバック関数として渡します。これはX番目のカラムが○〇〇である行を抽出しなさいという抽出方法では特定できない場合にプログラマが抽出方法を定義できます。渡す関数へのポインタは
    void GetSelect(vector< wstring >& RetVec,bool (Func)(const wstring&));
 となってるようにwstringを受け取ってbool値を返す関数です。staticな関数を別に定義してその関数を渡します。
 そしてもう一つCsvFile::GetSelect2()テンプレート関数があります。こちらはラムダ式を渡せる関数ですので、関数を別に記述しなくても抽出ロジックを記述できます。

 さて、このようにしてTilingFixedBoxを抽出しましたが、おなじCSVに入っているTilingPlateの同様の方法で抽出します。
 GameStage::CreatePlate()関数に記述がありますので各自読んでみましょう。

 このように、CsvFileクラスセル化されたマップの読み込みの他にもいろんな使い方ができます。
 この手法はデータベース操作を経験した人には、なんとなくSQL文のSELECTに似ていると思うかもしれません。SQL文のSELECTほど多機能ではありませんが、その縮小版と考えていいと思います。
 またCSVの保存も可能ですので、たとえばプレイヤーが到達した場所とかレベル、最高得点なども保存できます。

タイリングするオブジェクト

 さて、このサンプルではTilingPlateTilingFixedBoxが実装されています。
 CSVとは直接関係ありませんが、少し説明します。
 タイリング処理(テクスチャをタイル状に並べる処理)は、L"DEFAULT_CUBE"のような、リソース化されたメッシュは使えません。頂点のUV値を変更する必要があるからですが、そのため上記クラスは独自のメッシュを作成して使用しています。以下、TilingFixedBoxOnCreate()関数ですが、
//初期化
void TilingFixedBox::OnCreate() {
    auto PtrTrans = GetComponent<Transform>();
    PtrTrans->SetScale(m_Scale);
    PtrTrans->SetRotation(m_Rotation);
    PtrTrans->SetPosition(m_Position);
    auto Coll = AddComponent<CollisionObb>();
    Coll->SetFixed(true);
    vector<VertexPositionNormalTexture> vertices;
    vector<uint16_t> indices;
    MeshUtill::CreateCube(1.0f, vertices, indices);
    float UCount = m_Scale.x / m_UPic;
    float VCount = m_Scale.y / m_VPic;
    for (size_t i = 0; i < vertices.size(); i++) {
        if (vertices[i].textureCoordinate.x >= 1.0f) {
            vertices[i].textureCoordinate.x = UCount;
        }
        if (vertices[i].textureCoordinate.y >= 1.0f) {
            vertices[i].textureCoordinate.y = VCount;
        }
    }
    //描画コンポーネントの追加
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    //描画コンポーネントに形状(メッシュ)を設定
    PtrDraw->CreateOriginalMesh(vertices, indices);
    PtrDraw->SetOriginalMeshUse(true);
    PtrDraw->SetFogEnabled(true);
    //自分に影が映りこむようにする
    PtrDraw->SetOwnShadowActive(true);
    //描画コンポーネントテクスチャの設定
    PtrDraw->SetTextureResource(L"WALL_TX");
    //タイリング設定
    PtrDraw->SetSamplerState(SamplerState::LinearWrap);
}
 上記の中でまずしなければいけないのはオリジナルメッシュの作成準備です。
    vector<VertexPositionNormalTexture> vertices;
    vector<uint16_t> indices;
    MeshUtill::CreateCube(1.0f, vertices, indices);
    float UCount = m_Scale.x / m_UPic;
    float VCount = m_Scale.y / m_VPic;
    for (size_t i = 0; i < vertices.size(); i++) {
        if (vertices[i].textureCoordinate.x >= 1.0f) {
            vertices[i].textureCoordinate.x = UCount;
        }
        if (vertices[i].textureCoordinate.y >= 1.0f) {
            vertices[i].textureCoordinate.y = VCount;
        }
    }
 のようにCubeを作成したあと、タイリングのための変数(コンストラクタであらかじめ渡されている値)にUV値を書き換えます。
 その後、DrawコンポーネントのCreateOriginalMesh()関数でオリジナルメッシュを作成し、オリジナルメッシュを使うフラグをtrueにします。
    //描画コンポーネントの追加
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    //描画コンポーネントに形状(メッシュ)を設定
    PtrDraw->CreateOriginalMesh(vertices, indices);
    PtrDraw->SetOriginalMeshUse(true);
 これでタイリングされたオブジェクトがメッシュになります。また、
    //タイリング設定
    PtrDraw->SetSamplerState(SamplerState::LinearWrap);
 でサンプラーの設定をSamplerState::LinearWrapにするのを忘れないで記述しましょう。