unity で簡単な車を作ってみた

#概要
現在，unity の勉強をしています．

勉強の目的は ET ロボコンのシミュレータを作ることですが，
まずは，『地面』を作って，簡単な『移動物』を動作させてみるところから
はじめることにしました．

unity を一度も触ったことがないので，できるところから少しずつ始めています．
※GWの休み(2019/04/27～5/1)を利用して，約5日かけて勉強した記録です・・．

#勉強のステップ
当初は，簡単なモノを動かして，Unityでシミュレーションしているモノの
物理的な特性等を調べようとしていたのですが，気づいたら簡単な車が
出来てしまいました．

以下，今回の勉強のステップです．

* ステップ１：Unity のイロハを知る
    * インストール，プロジェクト作成，チュートリアル実施
* ステップ２：フィールドを作る方法を知る
    * 地面，道路
* ステップ３：モノを移動させる方法を知る
    * 走る，曲がる，止まる
* ステップ４：ホイールコライダを使ってみる
    * モータトルク，ブレーキトルク，ステアリング操作

#参照した書籍/記事

* ステップ１～２
  * 書籍：[Unity 2018 入門] (https://www.amazon.co.jp/Unity2018%E5%85%A5%E9%96%80-%E6%9C%80%E6%96%B0%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%AB%E3%82%88%E3%82%8B%E7%B0%A1%E5%8D%983D-2D%E3%82%B2%E3%83%BC%E3%83%A0%E5%88%B6%E4%BD%9C-Entertainment-IDEA/dp/4797397667/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=PYTWZFKYV835BYCQC0VF)
        * Chapter1～3を精読して，unity の基本的な使い方を理解しました．

 * 記事：[【UnityC#講座】Sceneビューのショートカットやお役立ち機能](https://unity-shoshinsha.biz/archives/tag/%E3%82%B7%E3%83%BC%E3%83%B3%E3%82%AE%E3%82%BA%E3%83%A2)
 * 記事：[Unityは左手座標系です](https://qiita.com/hkomo746/items/8738f619a538b7899988)
        * unity を使って最初に戸惑ったのは，3Dビューや作成したモノの配置方法だったのでこの辺りは大変参考になりました．

* ステップ３
 * 記事：[Rigidbody](https://docs.unity3d.com/jp/current/ScriptReference/Rigidbody.html)
        * オブジェクトに力を与えるために必要なコンポーネント(チョー重要)
 * 記事：[０から始めるUnity物理演算①物理シミュレーションを"書く](http://nexusyuumilo.hateblo.jp/entry/Unity_Physics_Master_1)
        * ここで物理シミュレーションの基礎を教わりました(感謝！！)
 * 記事：[微積分](https://www1.doshisha.ac.jp/~bukka/lecture/quantum/pdftext/pc3-a01.pdf)
        * 簡単な運動方程式を解析的に解く際に参考にさせて頂きました(大分忘れてました・・)
 * 記事：[Impulse!](http://nexusyuumilo.hateblo.jp/entry/unity_physics_5)
        * 物体移動の運動方程式を解いた結果とシミュレーション結果がどうしてもあわなくて，ここの記事を参照したことで自己解決できました(Impulseは初速を与えると理解．物理的に発生させることができるかどうかは置いといて)
 * 記事：[[Unity]RigidbodyのDragから終端速度を得る](https://qiita.com/yuji_yasuhara/items/1f438f0f27f5ef854a73)
        * Dragの物理的な意味付けをしてくれていて，運動方程式まできっちり解析されており，とても参考になりました！(感謝)
 * ステップ３：[[Unity] InputとFixedUpdateと物理演算の関係を整理しよう](https://qiita.com/yuji_yasuhara/items/6f50ecdd5d59e83aac99)
        * UpdateとFixedUpdateの違い，およびシミュレーション時間についてとても分かりやすく説明されてました．
 * 記事：[オブジェクトの角度を取得する](http://pafu-of-duck.hatenablog.com/entry/2018/01/28/155318)
        * 配置したモノの方向をスクリプトから変更する際に，どうも変な値が取れたので色々調べていたらこの記事に助けられました．
 * 記事：[線形減速](http://gamemakerlearning.blog.fc2.com/blog-entry-149.html)
        * 移動しているモノをブレーキかけて止める方法を調べていて，この記事を参考にさせて頂きました．
 * 記事：[Time](http://gamemakerlearning.blog.fc2.com/category7-1.html)
        * Unity上の時間について整理されていたのでブックマーク．

* ステップ４
 * 記事：[Wheel Collider](https://docs.unity3d.com/ja/current/Manual/class-WheelCollider.html)
 * 記事：[ホイールコライダー](https://riptutorial.com/ja/unity3d/example/19160/%E3%83%9B%E3%82%A4%E3%83%BC%E3%83%AB%E3%82%B3%E3%83%A9%E3%82%A4%E3%83%80%E3%83%BC)
 * 記事：[NVIDIA PhysX SDK 3.4.0 Documentation » User's Guide >> Vehicles](https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/Vehicles.html#vehicles)
        * 当初は物体を単に転がしているだけだったのですが，ホイールコライダなるものがあることを知りました・・．Unityの実力を思い知らされました(恐るべし)．


#デモ
今回の成果はなんといっても，ホイールコライダ―を使った車の作成です．
以下ぐらいの構成であれば，３０分程度で作れます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/a0be9344-da76-1153-7ead-69cf001d4c0d.png)

なお，チュートリアルに沿ってやるとビジュアル化できなかったですが，
色々とググって自己解決できました．

以下が，実際にシミュレーションした様子です．
宙に浮いたモノ(キューブ)が重力で車の上に落下しますが，
そのまま車の上で静止し，車を移動させている様子です．
※車輪も移動方向に応じたビジュアライズができています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/31d1328c-9597-4c47-e2cb-fa5e4330b565.png)


##C#スクリプト
今回作成したスクリプトは以下の通りです．
チュートリアルベースで作成し，必要な個所(ブレーキ等)を追加しました．

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleCarController : MonoBehaviour
{
    public List<AxleInfo> axleInfos; // 個々の車軸の情報
    public float maxMotorTorque; //ホイールに適用可能な最大トルク
    public float maxSteeringAngle; // 適用可能な最大ハンドル角度
    public float maxBrakeTorque;



    // 対応する視覚的なホイールを見つけます
    // Transform を正しく適用します
    public void ApplyLocalPositionToVisuals(WheelCollider collider)
    {
        if (collider.transform.childCount == 0)
        {
            return;
        }

        Transform visualWheel = collider.transform.GetChild(0);

        Vector3 position;
        Quaternion rotation;
        collider.GetWorldPose(out position, out rotation);

        visualWheel.transform.position = position;
        visualWheel.transform.rotation = rotation;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float motor = maxMotorTorque * Input.GetAxis("Vertical");
        float steering = maxSteeringAngle * Input.GetAxis("Horizontal");
        bool brake = Input.GetKey(KeyCode.Space);

        foreach (AxleInfo axleInfo in axleInfos)
        {
            if (axleInfo.steering)
            {
                axleInfo.leftWheel.steerAngle = steering;
                axleInfo.rightWheel.steerAngle = steering;
            }
            if (axleInfo.motor)
            {
                axleInfo.leftWheel.motorTorque = motor;
                axleInfo.rightWheel.motorTorque = motor;
            }
            if (brake)
            {
                axleInfo.leftWheel.brakeTorque = maxBrakeTorque;
                axleInfo.rightWheel.brakeTorque = maxBrakeTorque;
            }
            else
            {
                axleInfo.leftWheel.brakeTorque = 0;
                axleInfo.rightWheel.brakeTorque = 0;
            }
            ApplyLocalPositionToVisuals(axleInfo.leftWheel);
            ApplyLocalPositionToVisuals(axleInfo.rightWheel);
        }
    }
}

[System.Serializable]
public class AxleInfo
{
    public WheelCollider leftWheel;
    public WheelCollider rightWheel;
    public bool motor; //このホイールがエンジンにアタッチされているかどうか
    public bool steering; // このホイールがハンドルの角度を反映しているかどうか
    public bool brake;
}
```

#課題
今回，色々と調べて簡単な車まで作成することができたのですが，
まだホイールコライダの物理的な運動方程式をどう組み立てれば
良いのかわかっていません．．

せっかく簡単に作れることはわかったので，
もう少し理論的なところを詰めていきたいなと思っているところです．