猫じゃらしを作ろう #1

猫を飼っている方はご存じだと思いますが、
現実世界で猫じゃらし的なおもちゃに飛びつくのはもちろん、
画面上のマウスカーソルにも興味を示しますよね。

Youtubeなんかで猫じゃらし動画を探すと色々でてきますが、
同じ動画を見せていると猫もマンネリするようなので、いっそのこと作ってしまって飽きないようにしようと思いました。
※ 猫が実際に認識できていない"モニター空間"に触れない動く物体がある、というのは
ストレスになるという説がありますが、あくまで私的な判断で良かれと思って製作しています。

# 必要なもの
Unityは何回か友達に教えて貰ったことがある程度のド素人です。
一つずつ学びながらやっていますので、共有できればと思います。
* Unity Hub ver:3.1.1(多分新しくても問題ないでしょう)
* 猫への愛情
* 遊び心
* C#若干の知識

# 手順
1. [プロジェクトの作成](#プロジェクトの作成)
1. [オブジェクトの生成](#オブジェクトの生成)
1. [360度のいずれかの向きに動かす](#360度のいずれかの向きに動かす)
1. [画面外に出さないようにする](#画面外に出さないようにする)

## プロジェクトの作成
2Dゲームのプロジェクトを作成します。
プロジェクト名は、、、CatPlayingですかね。
猫じゃらしってぐぐっても英語は出なかったです。
![unityhub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/5f8b498b-d2a3-8921-adcc-bb8055ffe7f7.png)
↓
以前にレイアウトを弄った気がするのでこんな感じの画面になっています。お好みで。
日本語化は済ませています。(逆に分かりにくいかもしれません)
![unity_main.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/26065c66-60d0-4cc4-9321-ba788a7d14ab.png)

## オブジェクトの生成
お作法もよくわかりませんが、オブジェクトがないと進まないと思うので、
とりあえずSampleScene配下に入れてみます。
![unity_createobj.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/1bde402d-ab52-0a20-f140-e4d6fb6ed13b.png)

### 2Dリジッドボディコンポーネントをアタッチ
この用語の使い方が合っていないかもしれません。(教えてください)
まずオブジェクトを配置したらリジッドボディにすることが大事だった気がします(今回もこれがないと動きませんでした)
![リジッドボディ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/bbdbadd7-bd2c-203c-abbe-1be19d09283d.png)

### リジッドボディの重力を0にする
重力スケールが1になっているので、この状態でプロジェクトを実行すると円が下に落ちていきます。
その重力は今回考えないので、0にしてしまいしょう。
![リジッドボディ設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/f73394b8-56ce-0177-5328-b996a62f7d6b.png)

これでプロジェクトを実行すると、その場に静止する円のオブジェクトができました。

## 360度のいずれかの向きに動かす
ではいよいよ動かしていきます。

まずはスクリプトを作ってオブジェクトに適用します。
名前は何か違う気がしますが"RandomWalk"で。
![createScript.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/6c21e4c8-c893-3308-6c6c-c33411d84e2a.png)

### Startメソッドの実装
作成したスクリプトを開くと、Start/Updateメソッドが関数定義のみしてあります。
Startはプロジェクト実行時の初期動作、Updateは一定間隔でチェックされる動作でしょうか。

今回はまずStartメソッドに適当な向きに向かって進むように実装してみましょう。
色々調べながらやっていたので、いきなり答えになりますが、向きと速さが必要なようです。(物理的に言うと"速度"？)
```C#
    void Start()
    {
        float direction = 0;
        float speed = 5;

        Vector2 v;
        v.x = Mathf.Cos(Mathf.Deg2Rad * direction) * speed;
        v.y = Mathf.Sin(Mathf.Deg2Rad * direction) * speed;

        Rigidbody2D rd2d = GetComponent<Rigidbody2D>();
        rd2d.velocity = v;
    }
```
これで角度0の方向に速さ5で進むようになりそうです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/0e64329b-d4a1-4cd1-bae2-956377f530fc.gif" width=50%>

では向きをランダムにします。
directionの値を0~360の乱数にしただけです。
```C#
    void Start()
    {
        float direction = Random.Range(0, 360);
        float speed = 5;

        Vector2 v;
        v.x = Mathf.Cos(Mathf.Deg2Rad * direction) * speed;
        v.y = Mathf.Sin(Mathf.Deg2Rad * direction) * speed;

        Rigidbody2D rd2d = GetComponent<Rigidbody2D>();
        rd2d.velocity = v;
    }
```

## 画面外に出さないようにする
### Updateメソッドの実装
Start時に動き出すのは分かったのですが、このままだと画面外に出てしまいますので、
境界を越えたら跳ね返るようにしてみます。

そもそも境界がどうやったら分かるのか、ですが下記で左下・右上の位置が分かるみたいです。
```C#
        Vector3 screen_LeftBottom = Camera.main.ScreenToWorldPoint(Vector3.zero);
        Vector3 screen_RightTop = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
```

これを元に、4辺のx,y座標と自分の進行方向をチェックして、画面外に出そうだったら、反転させるように実装してみます。
```C#
    void Update()
    {
        Vector3 screen_LeftBottom = Camera.main.ScreenToWorldPoint(Vector3.zero);
        Vector3 screen_RightTop = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));

        Rigidbody2D rd2d = GetComponent<Rigidbody2D>();
        Vector2 velocity = rd2d.velocity;
        Vector2 position = transform.position;

        if ((position.y < screen_LeftBottom.y) && (velocity.y < 0))
        {
            velocity.y *= -1;
        }
        if ((position.x < screen_LeftBottom.x) && (velocity.x < 0))
        {
            velocity.x *= -1;
        }
        if ((screen_RightTop.y < position.y) && (0 < velocity.y))
        {
            velocity.y *= -1;
        }
        if ((screen_RightTop.x < position.x) && (0 < velocity.x))
        {
            velocity.x *= -1;
        }

        rd2d.velocity = velocity; 
    }
```
いや～これは自分ながらテキトーなコードですね。今後修正したいです（笑）
でもとりあえずこれで画面外に出ないようにできました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478155/8b48134b-e22f-3dd5-bcec-6339c7df1ff3.gif" width=50%>

これだけでも十分、(作っている途中も)猫ちゃんが遊んでくれていますが、まだまだ作りこんでいきたいですね。

→ NEXT: [Unityっぽく球を反射させてみた](https://qiita.com/tosix13/items/c13096a9238572828683)