【Unity(C#)】ハノイの塔の斬新？な実装法

##ハノイの塔
ルールとか もろもろこちらのサイトでまるわかりです→[ハノイの塔](https://hanoi.aimary.com/)

プログラミング界隈では再帰処理の勉強方法として定番の題材のようです。


##作り方は？
みんなどうやって実装しているのだろう？と思って調べましたが、
`最小手数での解き方`ばかりで"サクッと遊べるハノイの塔作った！"というのは見つけられませんでした。

##Unityでの実装
会社で**サクッと簡単にハノイの塔をVRで実装する**というミニハッカソンみたいなものをやった際に
私以外のエンジニアの方の実装方法が、(私にとって)斬新でおもしろかったのでまとめます。

ちなみに私は、**それぞれのブロックに大きさ順でIndexを与える**というやり方で実装に取り掛かり、
2時間後、真っ白なプロジェクトがそこにありました。


上司と私はマシンガンと水鉄砲くらいの性能差があるので
コード自体には目をつぶって考え方だけを真剣に見ていただけると幸いです。

##RayとLayerとColliderSize
**Ray**、**Layer**、**ColliderSizeの比較**をうまいこと使います。

**それぞれのBlock(土台も含む)が自身の上空から、自身に向かってRayを飛ばします。**
もしそのRayを妨げる障害物がなければ
**LayerはTopに変更されます。**

![HanoiExplanation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/85546f92-6ede-9995-866b-bc78825fb6a0.png)

次にLayerを**Top同士しか衝突しない**設定にします。
![HanoiExplanation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/bd8f0cb1-6da1-cb7c-5c37-839d12804775.png)

これにより、**一番上のオブジェクト(Top)のみ衝突するので比較対象を限定**することができます。

そして、最後に**一番上のオブジェクト(Top)同士のColliderの大きさを比較**します。
**自分より小さいColliderをもつオブジェクトの上に置けない実装**にすれば完了です。

![HanoiMove.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/337a3cc1-c39d-5303-899a-6f9484423a89.gif)

![Hanoi.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/86a1c059-3e3b-2138-d70c-422011fde6ad.gif)
(モデルは会社のパイセンが作ってくださいましたありがとうございます。)

次はコードです。
今回の説明に関係ないクラス(つかむ、もとの位置に戻る等)が気になる方は
GitHubでプロジェクトごと落としてきて確認してください。→[GitHub](https://github.com/ForJobOk/HanoiTower)

##自身の真上から自身に向かってRayを飛ばす

```C#:Blockにアタッチされたクラスのコード
    //自身の真上から自身に向けてレイを出す
    void RaySelfAbove()
    {
        Vector3 thisPos = this.gameObject.transform.position; 

        Ray ray = new Ray(new Vector3(thisPos.x,thisPos.y+1,thisPos.z), Vector3.down);
        RaycastHit hit = new RaycastHit();
        
        if (Physics.Raycast(ray.origin, ray.direction, out hit, Mathf.Infinity) )
        {
            if(hit.collider.gameObject.name == this.gameObject.name)
            {
                hit.collider.gameObject.layer = LayerMask.NameToLayer("Top");
            }
            
            if(hit.collider.gameObject.name != this.gameObject.name && movaChildBlock.isMove == false)
            {
                this.gameObject.layer = LayerMask.NameToLayer("Default");
            }
        }
    }
```
いろいろと端折っているので
要点だけ かい摘んで説明しますと、
ここ↓で自身の真上から自身に向かってRayが出てます。

```C#
Ray ray = new Ray(new Vector3(thisPos.x,thisPos.y+1,thisPos.z), Vector3.down);
```
そして、ここ↓で自身が一番上のオブジェクトかどうかを
Rayが衝突したオブジェクトの名前を見てあげることで判断し、Layerを変更しています。

```C#
 if(hit.collider.gameObject.name == this.gameObject.name)
 {
   hit.collider.gameObject.layer = LayerMask.NameToLayer("Top");
 }
```

##ColliderSizeの比較

```C#:BlockをつかむとBlockにAddComponentされるクラスのコード
 //ブロック同士の衝突時にルール通りの移動かどうか判定する
    void OnTriggerEnter(Collider other)
    {
        BoxCollider thisCollider = this.gameObject.GetComponent<BoxCollider>();

        GameObject otherObj = other.gameObject;

        BoxCollider otherObjBoxCollider = otherObj.GetComponent<BoxCollider>();

        //コライダーの大きさを比較して衝突先の方が大きければ移動
        if (otherObjBoxCollider.size.x > thisCollider.size.x)
        {
            Vector3 otherObjPos = otherObj.transform.position;
            moveChildBlock.beforeMovePosition = new Vector3(otherObjPos.x, otherObjPos.y+ thisCollider.size.y*3, otherObjPos.z);
            moveChildBlock.isMove = false;
        }
    }
```

`〇〇Collider.size.軸`で指定した軸のColliderSizeを取得できます。


##Unityはチート
今回の記事の題材となった"ハノイの塔ミニハッカソン"では
Unityのおもしろさを再認識しました。

汎用性とか、処理速度とか何も考えずに作成した場合に限られるかもしれませんが、
**そんなのありかよ！　というチートみたいな方法**が
Unityを使うと実現可能です。

まだまだ知らない機能ばかりですし、どんどん便利になっているようなので
これからの開発も楽しみです。

あ、ハノイの塔の実装でさらなるチートをご存知の方は
コメントでも対抗記事でもなんでもお気軽に書いちゃってください喜びます。