【Unity】MonoBehaviour.Reset()の使い方

# 始めに
自作スクリプトにインスペクタでコンポーネントをぽちぽち割り当てていくのが面倒くさい…。
そんなときに使えるのがMonoBehaviourにあるReset()というメッセージ。
意外と融通が利いて便利なのですが、あまり使われていない？気がしますので、使い方の例を含めて紹介させて頂きます。

# Reset()とは
https://docs.unity3d.com/ja/current/ScriptReference/MonoBehaviour.Reset.html
要するにエディタでコンポーネント初期化のときに呼ばれるメソッドです。
具体的にはインスペクタで「Add Component」したときだったり、
右クリック or 歯車アイコンをクリックして出てくるメニューから「Reset」を選択したときに呼ばれます。
![reset_addcomponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483690/b8f8adaa-9e86-f4f0-457d-66d6c01f870b.png)
![reset_menu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483690/30091562-81c1-64ce-5654-05665deaa0fe.png)

# 使い方
主にSerializeFieldの値を初期化するのに使います。
といっても宣言で値を代入しておけばAdd Component／Resetしたときに勝手にその値にしてくれるので、
それで事足りるならReset()を用意する必要はありません。

```cs
    [SerializeField]
    private int _testValue = 100;   // 特に何もしなくてもResetしたら100に戻る
```

## 宣言で代入できない値での初期化に使う
**宣言で代入できない値をデフォルトにしたい**（そしてそれを任意でインスペクタから変更できるようにもしたい）、そんなときがReset()の出番です。

```cs:ResetTest.cs
using UnityEngine;

public class ResetTest : MonoBehaviour
{
    [SerializeField]
    private Rigidbody _rigidbody = null;

    [SerializeField]
    private Transform _child = null;

    [SerializeField]
    private Camera _mainCamera = null;

    [SerializeField]
    private Light _lightInScene = null;

    [SerializeField]
    private Texture _textureInResources = null;

    [SerializeField]
    private Texture _textureInAssets = null;
    
    [SerializeField]
    private float _rigidbodyMass = 0f;

    private void Reset()
    {
        // 一緒に付いているコンポーネントをセットする
        _rigidbody = GetComponent<Rigidbody>();

        // 子オブジェクト「Child」をセットする
        _child = transform.Find("Child");

        // メインカメラをセットする
        _mainCamera = Camera.main;

        // シーン中のLightコンポーネントをセットする
        _lightInScene = GameObject.FindObjectOfType<Light>();

        // Resources中のTextureアセットをセットする
        _textureInResources = Resources.Load<Texture>("texture");

#if UNITY_EDITOR
        // Resources外のTextureアセットをセットする
        _textureInAssets = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture>("Assets/Textures/texture.png");
#endif

        // 参照取得済み他コンポーネントの値を使ってセットする
        if (_rigidbody != null)
        {
            _rigidbodyMass = _rigidbody.mass;
        }
    }
}
```

これをAdd Conponentしてやると、こんな感じで勝手に設定済みの状態にしてくれます。![reset_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483690/802dedd1-efa9-c47c-ffa0-1f8a66df546e.png)
もちろん、この後各フィールドを変更するのも自由ですし、また上記の状態にResetすることもできます。

## 他コンポーネントの設定値の変更に使う
さらには、他のコンポーネントの設定値を変更することもできます。

```cs
    private void Reset()
    {
        // Transformの位置を変更
        transform.position = new Vector3(100f, 100f, 100f);
        
        // Rigidbodyのconstraintsを変更
        var rigidbody = GetComponent<Rigidbody>();
        if (rigidbody != null)
        {
            rigidbody.constraints = RigidbodyConstraints.FreezeRotation;
        }
    }
```

ただこれは相手側のコンポーネントからすれば、非実行時なのに外部から突然設定を書き換えられてしまうということですから、意図しない挙動に繋がる可能性もあります。
使いどころは要検討と言えるでしょう。

## それ以外に使う
そもそも、エディタモードで実行できることなら大体できるみたいです。

```cs
    private void Reset()
    {
        // Add Componentしたと思ったらエディタが終了するトラップみたいなコンポーネント
        // ！！！絶対やめましょう！！！
#if UNITY_EDITOR
        UnityEditor.EditorApplication.Exit(0);
#endif
    }
```

# 注意点
と、便利なReset()ですが一つ注意点があります。
それは呼び出されるのはエディタモードのときのみということ。
つまり、ランタイムでスクリプトからAddComponent()した場合は呼び出されません。

```cs
        gameObject.AddComponent<ResetTest>();   // ResetTest.Reset()は呼び出されない
```

この場合、SerializeField各値は宣言での代入値（上のResetTest.csの例で言うとnull(None)とか0fとか）になります。

なお参考までに、やろうと思えばReset()を直接呼び出すことはできます。
（publicにすればクラス外からでも呼び出し可能）

```cs
        gameObject.AddComponent<ResetTest>().Reset();   // Reset()はpublicで定義
```
ただ、これはReset()をどこでも自由に呼び出せるということになりますから、
もし別の誰かが内部値のクリアメソッドか何かと勘違いして呼び出したりして、それ以降インスペクタでやった設定がリセットされた状態で動作していることに気づかなかったりすると、バグに繋がりそうな気がしないでもないです。
私見ではインスペクタ設定の補助程度の範囲で使った方が無難かなと思います。

# 参考
- http://kan-kikuchi.hatenablog.com/entry/MonoBehaviour_Reset
- https://qiita.com/JunShimura/items/6576eb63c19c2c9f0c0a