UnityのEditor拡張から値を更新したあとUnity再起動すると値がもとに戻る問題

#環境
Unity 2018.3.12f1
#はじめに
Inspector上で値を変更した変数がUnityを再起動したときにもとに戻ってしまう原因はおもに２つあります。

- 変数がSerializeされていないもしくはSerializeできない型を使っている
- Sceneが値の変更を検知していない（本題）

#変数がSerializeされていないもしくはSerializeできない型を使っている
これに関してはUnityのドキュメントを参照すると詳しく書いてありますが、Serialize属性をつけるかシリアライズ可能な型を使うことで回避できます。
https://docs.unity3d.com/ja/current/Manual/script-Serialization.html
> - Serializable 属性をもつカスタムの非抽象クラスと非ジェネリッククラス
> - Serializable 属性をもつカスタム構造体
> - UnityEngine.Object から派生するオブジェクトへの参照
> - プリミティブなデータ型 (int、float、double、bool、string など)
> - Enum 型
> - 特定の Unity ビルトイン型: Vector2、Vector3、Vector4、Rect、Quaternion、Matrix4x4、Color、Color32、LayerMask、AnimationCurve、Gradient、RectOffset、GUIStyle

#Sceneが値の変更を検知していない（本題）
ここからが本題ですがまず問題のコードを見てください。
#問題のコード
```Testclass.cs
using UnityEngine;
using UnityEditor;
public class Testclass : MonoBehaviour
{
    [HideInInspector]
    public int hoge = 0;
}

[CustomEditor(typeof(Testclass))]
public class TestclassInspector : Editor {
    public override void OnInspectorGUI() {
        var obj = target as Testclass;
        base.OnInspectorGUI();
        obj.hoge = EditorGUILayout.IntField(obj.hoge);
    }
}
```
Testclassはint型の変数hogeを持っています。customEditorから値を操作するためにpublicにした上で[HideInInspector]でInspector上で非表示にしていますが、プリミティブ型なのでserializeされているはず。

適当なオブジェクトにアタッチして
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165205/715da7b0-ad47-86f6-4702-9ab2eca399da.png)
値を変更。Ctrl+Sで保存
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165205/2541ee03-d731-5c33-64f4-4416e28ce877.png)
Unityを再起動。値は変更前に戻ってしまっています。:sweat_drops:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165205/3f4d6291-6c4e-ae92-f139-a10c831eae45.png)
#原因
このようなことが起きる理由はEditorGUILayout.〇〇Fieldで変更したときにsceneに変更された情報が反映されておらず、保存できてないからです。
反映されたかどうかはHierarchy上のシーン名の隣に__*__がつくことでわかります。
例えば、値を9999にしたあとTransformのPositionのxをちょっとだけ動かすことで*がつきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165205/97774039-46ff-8fd8-8479-08695c9a46e0.png)


保存してUnityを再起動すると値も保存されてたことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165205/54d6c976-48c7-5487-89f5-64f64888bac8.png)

#なぜこういうことがおきるのか
EditorからもとのコンポーネントにアクセスするにはSerializedObjectを通す必要があり、EditorGUILayout.〇〇Fieldで取得した値を再代入する方法それを介さないはSceneに変更が通知されないということでしょう。
ただ、SerializedPropertyを使用したアクセスの方法は、クラスや配列を扱うのが大変なのでできればEditorGUILayout.〇〇Fieldで扱いたいわけです。

#解決法
```Testclass.cs
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
public class Testclass : MonoBehaviour
{
    [HideInInspector]
    public int hoge = 0;
}

[CustomEditor(typeof(Testclass))]
public class TestclassInspector : Editor {
    public override void OnInspectorGUI() {
        var obj = target as Testclass;
        base.OnInspectorGUI();

        EditorGUI.BeginChangeCheck();

        obj.hoge = EditorGUILayout.IntField(obj.hoge);

        if (EditorGUI.EndChangeCheck())
        {
            var scene = SceneManager.GetActiveScene();
            EditorSceneManager.MarkSceneDirty(scene);
        }
    }
}
```

#解説
まず必要な名前空間を呼び出し

```.cs
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
```
値を変更する部分をEditorGUI.BeginChangeCheck();とEditorGUI.EndChangeCheck()ではさみます。
変更時にEndChangeCheck()がtrueを返すので、そのたびにEditorScenemanager.MarkSceneDiryを使ってシーンが汚れたことを手動で書き込みます。

```.cs
        EditorGUI.BeginChangeCheck();

        obj.hoge = EditorGUILayout.IntField(obj.hoge);

        if (EditorGUI.EndChangeCheck())
        {
            var scene = SceneManager.GetActiveScene();
            EditorSceneManager.MarkSceneDirty(scene);
        }
```

#結論
EditorGUILayout.〇〇Fieldを使う場合は必ずMarkSceneDirtyしましょう。