- Home /
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?
Answer by Bunny83 · Aug 02, 2019 at 02:17 PM
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.
I added the kb.SetValue(x, val)
things just now. Without this the val.type field would not work, just like the val.key, and I will be unable to change anything in the inspector. But it actually works fine in the editor... And, is Undo affect this behaviour?
You shouldn't use $$anonymous$$arkSceneDirty unless there is no other way. The usual way is to use the SerializedObject / SerializedProperty approach from an inspector. However in your case where you directly mess with the object instances you should use the Undo class like I said. All you have to do is call Undo.RecordObject before you apply any changes to your object. This will make unity to create a temporal serialized snapshot of your object and at the end of the current callback it will automatically make a diff if something has changed and will serialize the object properly. It also creates an undo action for your changes.
Using "$$anonymous$$arkSceneDirty" in an inspector is just plain wrong. You could currently inspect a prefab or scriptableobject that is not part of the current scene. using $$anonymous$$arkSceneDirty would mark the scene dirty even no change has been made and still won't save your changes to the prefab or scriptable object.
It seems a bit strange that you kind of ignored my answer, added the things I've mentioned to your question and then posting your own answer which suggests the wrong approach ^^.
Also there's still no need for reflection. As I mentioned you could have written just a PropertyDrawer for your Setting type. So everywhere where you use a field of type "Setting" Unity would use that PropertyDrawer to display and edit it. Property drawers take some time to get used to because they abstract away all the underlying types. They work directly with the serialized data. They also abstract away editing multiple selected objects at the same time.
Note that a PropertyDrawer now provides different ways how to provide the GUI for a certain property. The old approach uses the I$$anonymous$$GUI system just like you did by overriding OnGUI and GetPropertyHeight. The new version allows you to create GUI elements for the property.
Oh, all right, sorry for my negligence. I didn't get "acturally communicate your changes to Unity" means like that. $$anonymous$$aybe I just didn't get used to the "key nouns with a short useful comment" style tutorial. Thanks for those information.
Answer by NarallandaKaratoga · Aug 02, 2019 at 02:52 PM
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.)
Your answer

Follow this Question
Related Questions
How to make a Custom inspector? 2 Answers
'Unsupported type' error in custom editor script 4 Answers
Trouble setting the object reference in a property drawer 0 Answers
Preview procedural texture in scene without serializing it? 1 Answer
Don't save gameobjects to scene file (No hide flags) 0 Answers