After looking through docs, this forum, and a lot of searching, I guess it is time to post here. So the gist of the problem is that SerializedObject.ApplyModifiedPropertiesWithoutUndo is not working as expected. It probably is something I am not doing right, or perhaps a bug with ApplyModifiedPropertiesWithoutUndo?
I have a RootObject derived from MonoBehaviour. It has a public reference to ChildObject, which is derived from ScritableObject. I have a RootUI derived from Editor which is the custom editor window for RootObject. The idea is that I want to display both RootObject and ChildObject properties using just the RootUI editor window. I am using SerializedProperty to display the values, and have it automatically handle undo.
The issue comes up with Undo. In ChildObject, I have 2 ints. One of them (external int) is used to display to the user. The other int (internal) is to track changes, and if its different from the external int, I do some work and update the internal value after work is done. I then use ApplyModifiedPropertiesWithoutUndo to save the ChildObject state, believing that the internal value’s change will not be part of the undo record.
When undo happens, I have to do some work again with the changed values, but I need to know which values were undone. The problem is that in my undo handler, both my internal and external ints are the same (and have already been updated to previous value). I would expect that only the external one should have changed, not both.
Simplified version of the code below:
RootObject:
public class RootObject : MonoBehaviour
{
public ChildObject _childObject;
public void Initialize()
{
if(_childObject == null)
{
_childObject = ScriptableObject.CreateInstance<ChildObject>();
}
}
}
ChildObject:
public class ChildObject : ScriptableObject
{
[SerializeField]
private int _internalInt;
[SerializeField]
private int _externalInt;
public bool CheckInts()
{
Debug.LogFormat("CheckInt:: Internal: {0}, External: {1}", _internalInt, _externalInt);
return (_internalInt != _externalInt);
}
public void UpdateInternal()
{
_internalInt = _externalInt;
Debug.LogFormat("UpdateInternal:: Internal: {0}, External: {1}", _internalInt, _externalInt);
}
}
RootUI (Custom Editor):
[CustomEditor(typeof(RootObject))]
public class RootUI : Editor
{
// This is a custom editor to display both RootObject and ChildObject Inspector UI
public RootObject _rootObject;
public ChildObject _childObject;
public void OnEnable()
{
_rootObject = target as RootObject;
// Make sure root object has a child
_rootObject.Initialize();
_childObject = _rootObject._childObject;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
bool guiEnabled = GUI.enabled;
EditorGUI.BeginChangeCheck();
// Serialize the child object
SerializedObject childSerializedObject = new SerializedObject(_childObject);
// Create serialized property to display the int slider
SerializedProperty childExternalInt = childSerializedObject.FindProperty("_externalInt");
EditorGUILayout.IntSlider(childExternalInt, 0, 10);
// If there were changes, and our ints are different, then update the internal int
if (EditorGUI.EndChangeCheck())
{
// Apply any changes
childSerializedObject.ApplyModifiedProperties();
serializedObject.ApplyModifiedProperties();
if (_childObject.CheckInts())
{
childSerializedObject.Update();
// Get the external and internal int properties
childExternalInt = childSerializedObject.FindProperty("_externalInt");
SerializedProperty childInternalInt = childSerializedObject.FindProperty("_internalInt");
// Update the internal int using external int's value
int oldInternal = childInternalInt.intValue;
childInternalInt.intValue = childExternalInt.intValue;
Debug.LogFormat("Internal old: {0}, new: {1}: ", oldInternal, childInternalInt.intValue);
// Disable undo on this change
childSerializedObject.ApplyModifiedPropertiesWithoutUndo();
}
}
GUI.enabled = guiEnabled;
}
public void OnSceneGUI()
{
if ((Event.current.type == EventType.ValidateCommand && Event.current.commandName.Equals("UndoRedoPerformed")))
{
Event.current.Use();
}
if ((Event.current.type == EventType.ExecuteCommand && Event.current.commandName.Equals("UndoRedoPerformed")))
{
// Handle Undo where we want to check the difference between external and internal int
SerializedObject childSerializedObject = new SerializedObject(_childObject);
SerializedProperty childExternalInt = childSerializedObject.FindProperty("_externalInt");
SerializedProperty childInternalInt = childSerializedObject.FindProperty("_internalInt");
// Check property values
Debug.LogFormat("Undo:: Internal: {0}, External: {1}", childInternalInt.intValue, childExternalInt.intValue);
// Check actual object int values
_childObject.CheckInts();
}
}
}
RootUI::OnSceneGUI is where I handle the undo event. The ints are the same. Any ideas?
Using Unity 5.6