- Home /
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.
Additional data: If I make the ScriptableObject into just a base object, and change its instantiation from CreateInstance() to new TestData(), then the default nested inspector works, but my inner custom editor is still ignored.
UPDATE: I think I found the answer to this in a StackOverflow post, using a backhanded method with a custom PropertyDrawer.
I am testing that method in my code, and if it works I'll post my answer here for the benefit of others, because this one has been really hard to solve.
You should be able to inline OnInspectorGUI() of http://docs.unity3d.com/ScriptReference/Editor.CreateEditor.html or http://docs.unity3d.com/ScriptReference/Editor.CreateCachedEditor.html
Thanks, @ValooFX. $$anonymous$$y custom property drawer works pretty well, but your method may be cleaner. I'll do some experimenting with that and see what it can do.
@ValooFX -- Your method works correctly, as did the one in the other article. As it turns out, I unfortunately had to remove the multi-valued ScriptableObject field from my project because it breaks when used in a Prefab. This was because of the Unity limitation on scene objects not being in a Prefab. What I ended up doing ins$$anonymous$$d was to just make my class a $$anonymous$$onoBehaviour and do a regular component. It took away one cool feature but simplified a whole lot of other things.
Notwithstanding all that, I appreciate your reply because I had not previously realized I could do what you suggest. I did get your method working, and I archived the code so I can reuse that suggestion on a future editing script where prefabs are not involved. Thanks!
@syscrusher, Apparently the comment of @ValooFX you thanked for disappeared. I'm stuck with the same problem as you had and I would be very happy if I could see the solution he proposed. Could you please tell what was that solution?
If you follow the comments carefully you may notice that ValooFX seems to be "Polymo". $$anonymous$$aybe the name got changed.
$$anonymous$$aybe you're right. But in that case I think I don't understand his suggestion. What does he mean by inlining OnInspectorGUI() of CreateEditor?
Answer by mythirdalias · Jan 15, 2018 at 03:08 PM
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.