【Unity(C#)】魚をゆらゆらと自由に泳がせてみた

##魚
良いAssetを教えてもらいました。
[イワシくん](http://gomibakosan45.hatenablog.com/entry/2017/11/29/233657)←

泳ぎのアニメーションが最初からついているので

```C#
イワシのRigidbody.velocity = this.gameObject.transform.forward;
```
これで前に進めてあげるだけで泳いでいる感じが出ます。



##ランダムに方向転換
今回は**AddTorque**で向きを変えたいと思います。


Unity道場を参考にしました。
[Youtube](https://youtu.be/FqjM9oujyNE)←

動画にもありますが、
**AddTorque(回転力)は重ねても破綻することはない**とのこと。

もちろん求めている動きにするには調整して回転力を重ねる必要がありますが

"X軸に回転力が加っている最中にZ軸の回転力を重ねて加える"
"X軸のプラス方向の回転力をX軸のマイナス方向の回転力で打ち消す"

みたいなことも可能と解釈しました。(間違っていたらご指摘ください)

##デモ
実際にTorqueを合成したデモがこちらです。
赤い線はイワシくんの軌跡です。
![Fish_T_Add.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/445302d3-0f80-4e85-f58e-68a9d5af1730.gif)

一度目の方向転換時には
左向きにAddTorque、下向きにAddTorqueを同時に行っています。


##Script
練習がてらUniRxを使って実装しました。
使い方を覚えたかっただけで特に意味はありません。

```C#
using UniRx;
using UnityEngine;
using System;

public class MoveIwashi : MonoBehaviour
{
    [SerializeField]
    float speed = 0.3f;

    [SerializeField]
    float rotationSpeed = 0.01f;

    [SerializeField]
    float rotation_Interval = 3;

    Rigidbody iwasirigid;

    void Start()
    {
        iwasirigid = GetComponent<Rigidbody>();

        Observable.Timer(TimeSpan.FromSeconds(rotation_Interval))
            .Repeat()
            .Subscribe(_ =>
            {
                int count = UnityEngine.Random.Range(0, 6);
                print(count);
                if (count == 0)
                {
                    //左向き、下向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.down * rotationSpeed, ForceMode.Impulse);
                    iwasirigid.AddTorque(Vector3.right * rotationSpeed, ForceMode.Impulse);
                }
                else if (count == 1)
                {
                    //右向き、下向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.up * rotationSpeed, ForceMode.Impulse);
                    iwasirigid.AddTorque(Vector3.right * rotationSpeed, ForceMode.Impulse);
                    
                }
                else if (count == 2)
                {
                    //左向き、上向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.down * rotationSpeed, ForceMode.Impulse);
                    iwasirigid.AddTorque(Vector3.left * rotationSpeed, ForceMode.Impulse);
                }
                else if(count == 3)
                {
                    //右向き、上向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.up * rotationSpeed, ForceMode.Impulse);
                    iwasirigid.AddTorque(Vector3.right * rotationSpeed, ForceMode.Impulse);
                }
                else if(count == 4)
                {
                    //右向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.up * rotationSpeed, ForceMode.Impulse);
                }
                else if(count == 5)
                {
                    //左向きにトルクをかける
                    iwasirigid.AddTorque(Vector3.down * rotationSpeed, ForceMode.Impulse);
                }
            })
            .AddTo(this);

    }

    
    void Update()
    {
        //前に進む
        iwasirigid.velocity = this.gameObject.transform.forward * speed;

        //外積で元に戻す　X軸方向
        Vector3 left = this.gameObject.transform.TransformVector(Vector3.left);
        Vector3 hori_left = new Vector3(left.x, 0, left.z).normalized;
        iwasirigid.AddTorque(Vector3.Cross(left, hori_left) * 4, ForceMode.Force);

        //外積で元に戻す　Z軸方向
        Vector3 forward = this.gameObject.transform.TransformVector(Vector3.forward);
        Vector3 hori_forward = new Vector3(forward.x, 0, forward.z).normalized;
        iwasirigid.AddTorque(Vector3.Cross(forward, hori_forward) * 4, ForceMode.Force);

    }
}
```

##外積で水平に戻す
今回はUnity道場の動画にも紹介があった外積を利用しています。
回転を加えた際に魚がひっくり返ったりしないように
水平を保つ回転力を外積から算出して加えています。

水平を保ちたいだけであれば外積を使わなくてもRigidbodyの設定から可能です。
FreezeRotationで固定したい軸にチェックを入れればOKです。
![IwashiFreezeRotation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/5a80f62d-7028-99d2-8608-4fa9ab234f27.png)



今回は角度を変えながら自由に動き回った後で水平に戻る仕様にしたいので
外積を利用しました。

加える回転力を調節すれば、画像右側のようにゆらゆらと動かすこともできます。
![Fish_C.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/2710b8a4-823a-fd12-8e47-1def8c03a336.gif)


##AngularDrag
自然と水平に戻るようにAngularDragを調整しておきます。
![IwashiRigid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/61077c81-19f4-b8bc-5075-8b663c57eeaf.png)

AngularDragは文字通り、角度に対する抵抗力です。
ここがもし0であれば、角度に対して加えた力は永遠に消滅せずに回転を続けます。

##最後に
終わってから調べて気付きましたが、そもそもイワシはゆらゆらのんびりと泳がないっぽいです。
イワシがどう泳ぐかなんて心底どうでもいいのでこのまま公開することにしました。

今回使ったAddTorqueや外積などを組み合わせると、
ゆらゆらした動きや方向転換が簡単に再現できるのでぜひお試しください。

あとUnity道場は私くらいの初心者レベルだと本当にためになることばかりなので
初学者の方は軽い気持ちで覗いてみることをお勧めします。