UdonSharp日記～道の動的生成3～

モチベーション維持のためにLGTMください。

#やったこと
今回は道の動的生成をバージョンアップさせました。
具体的には下記の2点を行いました。
1.障害物を設置することで迷路のような作りにすること
2.レシピを作成し、それを入力として生成するつくりにすること
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">VケットにもいかずUdonをこねこねしています。<br>ダンジョンの動的生成の続きです。<br>十字路をふさぐ障害物を作って迷路上にしています。<br>しかもダンジョンのレシピも出力！<br>レシピを同期すれば、そこから他のプレイヤーとも同期できるはず。<a href="https://twitter.com/hashtag/UdonSharp?src=hash&amp;ref_src=twsrc%5Etfw">#UdonSharp</a> <a href="https://t.co/mmahSg2WGo">pic.twitter.com/mmahSg2WGo</a></p>&mdash; はんちょ (@sadimensions) <a href="https://twitter.com/sadimensions/status/1255854092477272064?ref_src=twsrc%5Etfw">April 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

1.の意図
　あらかじめプレハブで道のバリエーションを持っておくことも考えたのですが、逐次生成している都合、道の形状のつじつまが合わせられないケースがあると考え、障害物を設置することで迷路を作る構成としました。
　おいおい障害物を取り除くような処理を組むかもしれませんので、このやり方がよいかなと。
2.の意図
　現在、道のインスタンス化にVRCInstantiateを使用していますが、この関数は非同期です。同期を考えた場合、すべてのユーザーで同じようにVRCInstantiateを実行する必要があります。
　つまり、迷路のレシピを同期させておけば間接的に同期できるはずです。

#やりかた

今回はUdonSharpの使い方というよりも、ゲームプログラミングとか設計とかそういう感じですね。

今回の大切なところは、迷路のレシピをどのように作成するかという点です。
道の形状はソースコード中0～6の数字が割り当たった7パターンと、その回転4パターンです。
どの形、どれくらいの回転、座標　この三つの情報がスタックされていれば、
途中から入ってきたプレイヤーも迷路を再現できるようになります。
（今回は同期についての実装はありません）

なおレシピのフォーマットは次の通りです。
nrx,y,z
n:道の形状
r:90度の回転回数
x,y,z(リスポーン地点を0,0,0とした際のマス目)

```c#:
    //どのフロアを作成するか選定する
    //入力はフロアを作成する座標 と 北0, 東1, 南2, 西3どちらに進んでいるか0～3
    //出力はフロアのレシピ
    //フロアレシピ定義
    //フロア形状n(0～6)　y軸回転r(0～3) 座標
    //n定義           r定義
    //0 :┼          0 : 0度
    //1 :│          1 : 90
    //2 :├          2 : 180
    //3 :┌          3 : 270
    //4 :・(行止り)
    //5 :↑上り階段
    //6 :↓下り階段
    private string FM_choiceGenerateFloor(Vector3 createPos, int orient)
    {
        int[] n_kouho;
        if ((int)createPos.y == 0)
        {
            //高さが0の時はフルセット
            n_kouho = new int[] { 0, 1, 2, 3, 4, 5, 6 };
        }else if ((int)createPos.y == (int)FM_FLOOR_HIGHT)
        {
            //高さが2階のとき
            n_kouho = new int[] { 0, 1, 2, 3, 4, 6 };//上りを除く
        }
        else
        {
            n_kouho = new int[] { 0, 1, 2, 3, 4, 5};//下りを除く
        }
        //候補からランダムに選択する
        int n = n_kouho[(int)Random.Range(0, n_kouho.Length)];

        int[] r_kouho;
        //道の形状によって許される回転回数が決まっている。(入り口には障害物を置いてはいけない)
        switch (n)
        {
            //01456は回転しない
            case 0:
            case 1:
            case 4:
            case 5:
            case 6:
                r_kouho = new int[] {0};
                break;
            case 2:
                //2は0,90,180の三択
                r_kouho = new int[] {0, 1, 2};
                break;
            case 3:
                //3は0,90の二択
                r_kouho = new int[] {0, 1};
                break;
            default:
                r_kouho = new int[] {0};
                break;
        }
        //回転もランダムに選択し、そこに、プレイヤーがどちらの方角から入ってきたかを加味する
        int r = (r_kouho[(int)Random.Range(0, r_kouho.Length)] + orient) %4;
        //座標そのままだとレシピが長いため、マス単位にする
        int x = (int)(createPos.x / (FM_FLOOR_SIZE_HALF * 2));
        int z = (int)(createPos.z / (FM_FLOOR_SIZE_HALF * 2));
        int y = (int)(createPos.y /  FM_FLOOR_HIGHT );
        return string.Format("{0}{1}{2},{3},{4}",n.ToString() , r.ToString() ,x,y,z);
    }
```

#課題
まだこの方法で同期が実現できるかわかっていません。
近いうちに同期のテストを実施します。

#同期テスト結果
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">World再突入時に再生成されるパターンもあり。<br>多分目の前のロボットが何らかの権限を持った状態なのでは？なんて考えています。<a href="https://twitter.com/hashtag/UdonSharp?src=hash&amp;ref_src=twsrc%5Etfw">#UdonSharp</a> <a href="https://t.co/oiBKhJ2Q0u">pic.twitter.com/oiBKhJ2Q0u</a></p>&mdash; はんちょ (@sadimensions) <a href="https://twitter.com/sadimensions/status/1256390760963821568?ref_src=twsrc%5Etfw">May 2, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

同じワールドにいるならば同期できそうです。
後から入ってきた人もレシピをもとに同期をとるはずが、取れる場合と取れない場合がありました。

原因を探さねば