【Unity】Terrainを使ってMandelbroを動かしてみた♬

ちょっと遊んでいたら、MandelbroをUnityで動かしているひとがいた。そして、Terrainを動かしている人がいる。
これはOculusGoで動くMandelbroが見られるかなと思いやってみました。

今回は以下の三つの参考の通りです。
【参考】
①[【Unity】terrainを繰り返して、無限に続く地面を作る](https://qiita.com/takahi5/items/14967dad3494eb9edee7)
②[UnityのTerrainでマンデルブロしてみた](https://qiita.com/Ijoru/items/976935225beff0a5540f)
③[Pythonでカオス・フラクタルを見よう！](https://qiita.com/jabberwocky0139/items/33add5b3725204ad377f)

今回のコードは以下に置いた
[MuAuan/Unity ](https://github.com/MuAuan/Unity/tree/master)
###やったこと
（１）Mandelbro集合
（２）Unityで見る
（３）Terrainを動かす
###（１）Mandelbro集合
pythonだと参考③から以下のとおり

```py
def mandel(X, Y):
    a, b = [0] * 2
    for i in range(N):
        a, b = a**2 - b**2 + X, 2 * a * b + Y
    return a**2 + b**2 < 4
x, y = [np.linspace(-2, 2, M)] * 2
X, Y = np.meshgrid(x, y)
plt.pcolor(X, Y, mandel(X, Y))
plt.show()
```
[Wikipediaによれば](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%B3%E3%83%87%E3%83%AB%E3%83%96%E3%83%AD%E9%9B%86%E5%90%88)
次の漸化式

```math
 {\begin{cases}z_{{n+1}}=z_{n}^{2}+c\\z_{0}=0\end{cases}}
```
で定義される複素数列 {zn}n∈N∪{0} が n → ∞ の極限で無限大に発散しないという条件を満たす複素数 c 全体が作る集合がマンデルブロ集合である。
マンデルブロ集合を複素数を使わずに書き直すには、zn を点 (xn, yn) に、c を点 (a, b) にそれぞれ置き代えて、

```math
{\begin{cases}x_{{n+1}}=x_{n}^{2}-y_{n}^{2}+a\\y_{{n+1}}=2x_{n}y_{n}+b\end{cases}}
```
とすればよい。ということで、上記のPythonコードでよいことが分かる。上記コードはReturnが秀逸だ。以下蛇足。。
「上式で z0 を 0 以外の複素数にした場合、マンデルブロ集合の周が変形し、ジュリア集合に似たフラクタル状の曲線が現れる。」
確かに絵は変わるけど。。。オリジナルの方が綺麗。因みにコードは

```py
N, M = 50, 500
a0=-0.5
b0=-0.7
def mandel(X, Y):
    a, b = a0,b0 
    for i in range(N):
        #a,b = a*a- b*b+ X,2 * a * b + Y
        b1=b
        b = 2 * a * b + Y
        a = a*a- b1*b1+ X
    return a*a + b*b < 4
x, y = [np.linspace(-2, 2, M)] * 2
X, Y = np.meshgrid(x, y)
plt.pcolor(X, Y, mandel(X, Y))
plt.pause(3)
plt.savefig('plot_mandela{}b{}.png'.format(a0,b0), dpi=60)
plt.close() 
```
###（２）Unityで見る
これはまずは参考②のまんまです。コードも公開されているのでそのままで動くと思います。しかし、ウワンが独自にやってみた感じだと、Terrainの性質が少し異なってしまいました。
![mandelebro.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/ed7f619e-0e35-efb3-312a-a546eaf49330.jpeg)
上の絵のように、二つのマンデルブロが見えますが、一つは公開されているassetsを使っています。何が異なるかというと、ウワンのは表示されているマンデルブロの高さを調整していますが、公開版は無調整でこの高さになっています。
一応、以下にウワンのコードを公開しておきます。
※元のコードは参考②を見てください

```cs
    IEnumerator UpdateTerrainCoroutine()
    {
        var heights = new float[terrainData.heightmapWidth, terrainData.heightmapHeight];
        while (true)
        {
            renderArea *= 0.98;
            var startX = baseX - renderArea / 2.0;
            var startY = baseY - renderArea / 2.0;
            var renderAreaPerWidth = renderArea / terrainData.heightmapWidth;
            var renderAreaPerHeight = renderArea / terrainData.heightmapHeight;
            for (int x = 0; x < terrainData.heightmapWidth; x++)
            {
                for (int y = 0; y < terrainData.heightmapHeight; y++)
                {
                    heights[x, y] = MandelbrotCalculator.Calculate(
                        startX + x * renderAreaPerWidth,
                        startY + y * renderAreaPerHeight);
                    heights[x, y] = heights[x, y] / 10;//ここ追加
                }
            }
            terrainData.SetHeights(0, 0, heights);
            yield return null;
        }
    }
```
ちなみにマンデルブロの部分は元コードのままで以下の通りです。

```cs
public class MandelbrotCalculator
{
    const int CALCULATE_LIMIT = 50;
    public static float Calculate(double baseX, double baseY)
    {
        double x = 0.0, y = 0.0, cacheX = 0.0, cacheY = 0.0;
        for (int i = 0; i < CALCULATE_LIMIT; i++)
        {
            cacheX = x * x - y * y + baseX;
            cacheY = x * y * 2.0 + baseY;
            if (cacheX * cacheX + cacheY * cacheY > 4.0)
            {
                return (float)i / CALCULATE_LIMIT;
            }
            x = cacheX;
            y = cacheY;
        }
        return 1f;
    }
}
```
このコードでは発散するというか４を超えたときの次数の最大回数との比を返しており、超えない場合１を返しているので、場所により高さが異なります。
ちなみにこれを見るのはその大きさが最初大きすぎて何を見ているのかわからずカメラの位置などに苦労しました。
Terainは初めて使いましたが、hierarchyの３Dの下にあり、hierarchyに張り付けました。あとは用意したスクリプトMandelbrotTerrainを貼り付けます。しかしここで、terraindataにMandelbroTerrainを指定するとTerrainColliderのところでエラーが出てしまいます。まあ、ここはNewTerrainのまま動きました。もう一方のMandelbrotCalculatorは前者から呼ばれているだけで特に何もしません。
以下、３連結した絵です。
![mandelbroTerrain.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/4c2f06a6-4a3f-6e79-7580-65fc314a7b06.jpeg)

###（３）Terrainを動かす
こうしてほぼまんまでマンデルブロがUnityの中で動きます。
しかし、もっとダイナミックな絵を見たいということで、Terrainを動かすというのを追加してみました。
こちらは、以下のコードをUpdateに追加するだけ。。のはずでした。


```cs
public class MoveTerrain : MonoBehaviour {
    public float speed;
    float size = 2000;  //NOTE!
    // Update is called once per frame
    void Update () {
        transform.Translate (0, 0, speed);
        if (this.transform.position.z + size < 0) {
            Debug.Log("out of display");
            this.transform.Translate(0, 0, size * 2);
        }
    }
}
```
しかし、float size = 2000;  //NOTE!がTerrainの大きさなんだけど、これウワンのは５００でした。ということで、ここ変更しました。
それでもなんとなく動かしている方向が異なるらしく、結局以下のコードで連続する大地というか連続するマンデルブロができました。

```cs
    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed);
        if (this.transform.position.z >500)
        {
            Debug.Log("out of display");
            this.transform.Translate(0, 0, -1000 );
        }
    }
```
ということで、最初の５００の符号と不等号が異なっています。

さて、OculusGoで見てみると。。。あれ？？なぜか見えません。
ということで、動画は無しです。
残念。。。
![man.gif](https://qiita-image-store.s3.amazonaws.com/0/233744/dd983ba1-4c73-9831-73d0-335a4b549489.gif)
上記は二連結した場合のシーンです。三連結は迫力さらにアップしました。
【追記】認証で失敗していたようです。OculusGoで見えました！
[【OculusGo】Mandelbroの大地が動く♪](https://youtu.be/VuWxCtxZGZ8)
<a href="http://www.youtube.com/watch?feature=player_embedded&v=VuWxCtxZGZ8" target="_blank"><img src="http://img.youtube.com/vi/VuWxCtxZGZ8/0.jpg" 
 </a>
※画像をクリックするとYouTube動画につながります
###まとめ
・Terrainにマンデルブロを貼り付けて動かしてみた。
・さらにTerrainを動かして複数マンデルブロが続々出てくる無限ループを作成した

・~~OculusGoではマンデルブロ（Terrain）が見えない~~
・同じように動くがパラメータ（TerrainColliderのTerrainData）が参考サイトのものと異なるが設定しようとするとTerrainにエラーが出る
・Gameの中に入って（その視線で）Terrainを散歩したい