How to do a custom editor for a ScriptableObject that is a property of a MonoBehaviour

I have what I thought would be a simple situation, but I can’t seem to get a custom editor to work.

I have a fairly complex ScriptableObject in my real project, but for testing I created a very simple stand-in:

public class TestData : ScriptableObject {
	public string s1;
	public float f1 = 3.14159f;
}

An instance of this class is a public property of my component class. Again, I have made a simplified version for testing:

public class TestComponent : MonoBehaviour {
	[SerializeField]
	public TestData tData = TestData.CreateInstance<TestData>();
}

I have a custom editor for the inner class:

[CustomEditor(typeof(TestData))]
[CanEditMultipleObjects]
public class TestDataEditor : Editor {
				
	SerializedProperty s1;
	SerializedProperty f1;
		
	public override void OnInspectorGUI() {
		Debug.Log("TestDataEditor.OnInspGUI");
		base.OnInspectorGUI();
		EditorGUILayout.LabelField("TestDataEditor begins");
		s1 = serializedObject.FindProperty("s1");
		f1 = serializedObject.FindProperty("f1");
		EditorGUILayout.PropertyField(s1);
		EditorGUILayout.PropertyField(f1);
		EditorGUILayout.LabelField("TestDataEdito ends");
	}
}

Finally, there is a custom editor for the outer class:

[CustomEditor(typeof(TestComponent))]
[CanEditMultipleObjects]
public class TestEditor : Editor {
			
	Editor tDataEditor;
	SerializedProperty tData;
	
	public override void OnInspectorGUI() {
		tData = serializedObject.FindProperty("tDdata");
		EditorGUILayout.LabelField("This is TestEditor");
		if (tData != null) {
			if (tDataEditor == null) {
				tDataEditor = Editor.CreateEditor((Object) tData.objectReferenceValue);
			}
			tDataEditor.OnInspectorGUI();
		}
	}
}

When I create a GameObject with the TestComponent attached, I see the LabelField and its message from the outer editor, but the FindProperty(“tData”) is always returning null. This means the rest of the OnInspectorGUI() logic is skipped.

is there a “right way” to do this, other than what I am trying to do? I also am not clear on what parameter I should be passing to Editor.CreateEditor(), so I would appreciate any advice on that.

I have been all over the forums and search engines, and so far every solution I’ve seen posted for something akin to this has not worked with current Unity versions (I’m running 5.3.4). I’ve spent many hours trying to solve this, and I’m finally admitting I’m stumped.

For what it’s worth, if I turn the real version of my ScriptableObject into a MonoBehaviour, I have a working custom editor for that more complex situation that is doing just fine. What I am attempting to do is to take my MonoBehaviour and refactor it as a ScriptableObject to allow it to be used away from a GameObject context.

Thanks in advance to anyone who can help.

I’ve been working on a similar thing without the MonoBehaviour extension but using ScriptableObjects and asked/solved this question:
https://answers.unity.com/questions/1454591/plug-and-play-unityeventsmethods.html?childToView=1454648#answer-1454648

You might find something useful in there. It looks to me like you need to be calling

AssetDatabase.CreateAsset(ScriptableObject yourAsset, relPath);
AssetDatabase.SaveAssets();`

At some point to actually save what you want to disk.

Calling tDataEditor.OnInspectorGUI(), in another OnInspectorGUI() feels wrong as well, I’d find the path of the object and navigate to it instead.

Personally I found the EditorWindow extension far more useful in this regard because it doesn’t require you to use the inspector view.

I know this is a few years old but I’m curious if you ever found a good, clean solution for this? I have a nearly identical setup and am having the exact same problem. My custom editor simply drops any ScriptableObject that I drop into my custom field and returns null. If I plug one in at runtime and click my custom button to rerun my methods, it clears the value and again returns null. I’ve seen some people saying you need to add the IfDirtyOrNull() method (or whatever the updated version was, can’t remember off the top of my head) so it doesnt clear it but I’ve done that and it still wipes or doesn’t read.