Serializale field not being serialized when using reflection?

Recently I Have a script attached to a gameobject, which configure key bindings:

public class KeyBinding : MonoBehavour
{
        [Serializable] public struct Setting { public KeyCode key; public CommandType type; }
        [SerializeField] Setting moveLeftSetting;
        [SerializeField] Setting moveRightSetting;
        [SerializeField] Setting jumpSetting;
        [SerializeField] Setting crouchSetting;
        ....
}

and a custom inspector, using reflections to extract setting variables and change them. The key code uses a different text assignment style, shown as below:

[CustomEditor(typeof(KeyBinding))]
public class KeyBindingWindow : Editor
{
    public override void OnInspectorGUI()
    {
        ....
        foreach(var kb in typeof(KeyBinding).GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
        {
            if(kb.FieldType != typeof(KeyBinding.Setting)) continue;
            var val = (KeyBinding.Setting)kb.GetValue(x);
            ......
            val.type = (CommandType)EditorGUILayout.EnumPopup(val.type, GUILayout.MaxWidth(100));
            .......
            var newVal = EditorGUILayout.TextField(val.key.ToString(), GUILayout.Width(100));
            if(Enum.TryParse<KeyCode>(newVal, true, out var parsedRes)) val.key = parsedRes;
            else val.key = KeyCode.None;
            .......
            kb.SetValue(x, val);
    }
}

While val.key works perfectly inside the editor, val.key will be set to None every time I open-up Unity editor. However val.type still gets the right value. What goes wrong? What should I do to keep the values?

You don’t change any of the actual values inside your KeyBinding class. Your “Setting” type is a struct. Therefore this line:

var val = (KeyBinding.Setting)kb.GetValue(x);

will get you a local copy of that struct. Changes to that struct won’t change the actual values in your KeyBinding class instance. In order to actually change the values you have to write the whole struct back using SetValue after your change. Also keep in mind that you have to use the Undo class to actually communicate your changes to Unity.

Though your approach using reflection seems to be unnecessary complicated. If you have multiple settings you probably want to use an array. If you really want to have individual setting fields you might want to turn the struct into a class and just add the instances to an array.

Also a PropertyDrawer for your Setting type would probably make more sense. Reflection is almost always a bad idea / the wrong design. Reflection might be unavoidable if you want to access things that are not under your control.

Oh damm I figured it out…
I completely forget about this …
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());

Then if Unity Editor doesn’t realize that the scene is changed, pressing Ctrl+S is useless… val.type doesn’t work as well, if I don’t change other things in the scene…


(About the reflection: (1) I don’t need to change inspector files when adding and removing Settings - just making relative things packed, so that people can read and modify it with ease. (2) the re-compilation of editor assembly will not be triggered by modifying KeySetting.)