Search code examples
c#unity-game-engineserializationunity-editorscriptable-object

Changes made to Scriptable Object are lost even after calling EditorUtility.SetDirty()


I have a script called HandSkeleton for which I wrote a custom editor. HandSkeleton has a field of type HandSkeletonData which is a ScriptableObject. I wrote a custome inspector which can save some values from HandSkeleton to the HandSkeletonData it's referencing.

Here is the code:

[CustomEditor(typeof(HandSkeleton))]
public class HandSkeletonEditor : UnityEditor.Editor
{
    private const string BasePath = "Assets/ScriptableObjects/HandSkeletonData";

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        var handSkeleton = (HandSkeleton)target;

        if (GUILayout.Button("Save hand skeleton data"))
        {
            var handSkeletonTransform = handSkeleton.transform;
            var rootName = handSkeletonTransform.root.name;
            var parentName = handSkeletonTransform.parent.name;
            var skeletonName = handSkeletonTransform.name;

            if (handSkeleton.handSkeletonData == null)
            {
                var so = CreateInstance<HandSkeletonData>();

                if (!AssetDatabase.IsValidFolder(BasePath + $"/{rootName}"))
                    AssetDatabase.CreateFolder(BasePath, rootName);

                AssetDatabase.CreateAsset(so, $"{BasePath}/{rootName}/{parentName}_{skeletonName}.asset");

                serializedObject.FindProperty(nameof(handSkeleton.handSkeletonData))
                    .objectReferenceValue = so;

                serializedObject.ApplyModifiedProperties();
            }
            
            var rotations = handSkeleton.EditorInitialized.GetAllFingerSegmentRotations()
                .Select(x => (Quaternion[])x.Clone())
                .ToArray();

            // here I change the Scriptable Object
            handSkeleton.handSkeletonData.FingerSegmentRotations = rotations;

            EditorUtility.SetDirty(handSkeleton.handSkeletonData);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }
}

The issue is that the changes made to handSkeletonData are lost after I refresh the editor. I found many other posts where people had the same issue but it was mostly caused by not calling EditorUtility.SetDirty(). Am I calling it wrong? Or is there something else I'm doing wrong?


Solution

  • Turns out the issue wasn't the way I was saving the Scriptable Object, but rather what was saved inside it - a 2D array of Quaternions. Unity simply doesn't serialize 2D arrays. To fix this I changed the 2D array to a 1D array and am accessing it through a property. If anyone is interested here is the code for this:

        [HideInInspector] [SerializeField] private Quaternion[] fingerSegmentRotations;
    
        public Quaternion[][] FingerSegmentRotations
        {
            get
            {
                if (fingerSegmentRotations == null || fingerSegmentRotations.Length == 0) return null;
    
                var rotations = new Quaternion[HandSkeleton.FingerCount][];
                for (var i = 0; i < HandSkeleton.FingerCount; i++)
                {
                    rotations[i] = new Quaternion[HandSkeleton.SegmentCount];
    
                    for (var j = 0; j < HandSkeleton.SegmentCount; j++)
                        rotations[i][j] = fingerSegmentRotations[i * HandSkeleton.SegmentCount + j];
                }
    
                return rotations;
            }
            set
            {
                fingerSegmentRotations = new Quaternion[HandSkeleton.FingerCount * HandSkeleton.SegmentCount];
    
                for (var i = 0; i < HandSkeleton.FingerCount; i++)
                for (var j = 0; j < HandSkeleton.SegmentCount; j++)
                    fingerSegmentRotations[i * HandSkeleton.SegmentCount + j] = value[i][j];
            }
        }