【Unity(C#)】シンプルな入れ替えパズルの入れ替えロジック

##入れ替えパズルとは

こんな感じのやつです。
![15-puzzle.png](https://qiita-image-store.s3.amazonaws.com/0/244071/eccd91c7-e1a8-6996-adf8-659b5ca1aa06.png)
[wiki](https://ja.wikipedia.org/wiki/15%E3%83%91%E3%82%BA%E3%83%AB)

物理的に移動可能なマス目が一つだけ用意されていて、ブロックを移動させることで
イラストを完成させたり、数字を並べ替えたり、道をつないだりするゲームです。

##unity1week
文字通り一週間でMade With Unityなゲームを完成させようという~~デスマーチです。~~
愉快な企画です。

今回、私も初参加しました。
毎回テーマが設定されて、そのテーマに沿った(こじつけでも可)ゲームを作成します。
今回のテーマは「つながる」でした。

入れ替えパズルの上をUnityちゃんに歩いてもらってゴールを目指すというものを作りました。


[【IQ150】](https://unityroom.com/games/kento_no_tsunagaru_game)


##入れ替えロジック
完成当初の入れ替えロジックは複雑に考えすぎてとんでもない遠回りな実装方法を取り入れてました。
今回作ったゲームもぐちゃぐちゃな実装のまま放置しているのでプレイする度に何かしらバグります。

もっとシンプルにできないかと思って考えてみました。

最終的に思いついた(というか上司からヒントもらった)のは
**透明なブロックを動かして衝突した色付きブロックと入れ替える**という方法です。
![MoveTransparent.png](https://qiita-image-store.s3.amazonaws.com/0/244071/761f6c7a-9db6-0725-284d-4df85fd68354.png)
上のとてもわかり易い図の通りです。

透明なブロックの移動範囲(今回は移動できる回数にしました)を3×3で定めて、
そのエリア内を移動させます。

以下、コードです。

```C#

    int x_MoveCount = 1;
    int z_MoveCount = 1;
    Vector3 thisObjPosition;
    Vector3 saveThisObjPosition;
   
    void Update()
    {

        if (Input.anyKey == false)
        {
            return;
        }

        thisObjPosition = this.gameObject.transform.position;

        if (Input.GetKeyDown(KeyCode.LeftArrow) && x_MoveCount > -1)
        {
            saveThisObjPosition = this.gameObject.transform.position;
            thisObjPosition.x -= 1;
            this.gameObject.transform.position = thisObjPosition;
            x_MoveCount -= 1;
        }

        if (Input.GetKeyDown(KeyCode.RightArrow) && x_MoveCount < 1)
        {
            saveThisObjPosition = this.gameObject.transform.position;
            thisObjPosition.x += 1;
            this.gameObject.transform.position = thisObjPosition;
            x_MoveCount += 1;
        }

        if (Input.GetKeyDown(KeyCode.UpArrow) && z_MoveCount < 1)
        {
            saveThisObjPosition = this.gameObject.transform.position;
            thisObjPosition.z += 1;
            this.gameObject.transform.position = thisObjPosition;
            z_MoveCount += 1;
        }

        if (Input.GetKeyDown(KeyCode.DownArrow) && z_MoveCount > -1)
        {
            saveThisObjPosition = this.gameObject.transform.position;
            thisObjPosition.z -= 1;
            this.gameObject.transform.position = thisObjPosition;
            z_MoveCount -= 1;
        }
    }

    void OnTriggerStay(Collider other)
    {
        //衝突してほしいゲームオブジェクトでなければ抜ける
        if (other.gameObject.tag != "Area")
        {
            return;
        }
        other.transform.position = saveThisObjPosition;
    }
```

##透明なブロックの移動前の位置を保存
左の矢印キーを押した場合を例に見ていきます。

```C#
 if (Input.GetKeyDown(KeyCode.LeftArrow) && x_MoveCount > -1)
        {
            saveThisObjPosition = this.gameObject.transform.position;
            thisObjPosition.x -= 1;
            this.gameObject.transform.position = thisObjPosition;
            x_MoveCount -= 1;
        }
```
`saveThisObjPosition = this.gameObject.transform.position;`
で移動前の位置を保存してからポジションを変更しています。


```C#
 void OnTriggerEnter(Collider other)
    {
        //衝突してほしいゲームオブジェクトでなければ抜ける
        if (other.gameObject.tag != "Area")
        {
            return;
        }
        other.transform.position = saveThisObjPosition;
    }
```
衝突したら、その移動前のポジションを
衝突したゲームオブジェクトに代入しています。
`other.transform.position = saveThisObjPosition;`

![GifCapture-201903232133398707.gif](https://qiita-image-store.s3.amazonaws.com/0/244071/247f4cbd-b906-b370-27e9-67ae5032f33e.gif)

できました。
ただこれだと同時入力(同一フレーム内で2ボタンの入力を受け取る)が可能？なのか
めちゃくちゃ早く動かすとバグります。
どうやらUnityには**キーボード入力の受付を一時的に止める**みたいな機能はないっぽいです。
~~なのでInputManagerみたいなのを自分で作って同時入力できないようにする必要があります。~~

~~もう少し調べて見つかったらまた記事まとめます。~~
追記

```C#


 if (Input.anyKey == false)
 {
    isCheckInput = false;
    return;
 }

 if (isCheckInput == false)
 {
    if (Input.GetKeyDown(KeyCode.RightArrow))
    {     
    isCheckInput = true;
    }
 }
```

こんな感じでいけました！
教えてくださった方ありがとうございます！

[同時押し禁止についてはこちら](https://qiita.com/OKsaiyowa/items/e640a303daceab8f9bf9)