Search Unity

Creating Editor for SerializedProperty

Discussion in 'Immediate Mode GUI (IMGUI)' started by PedroGV, Dec 6, 2016.

  1. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Is there a way to create and editor for a serialized property?

    So far, there's no such option on Editor.CreateEditor (...) overloads, so I was wondering how to achieve it when I have no info on the object that represents the serialized property (not the serialized object that contains it).
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    You can create a custom property drawer for it. Then you can use EditorGUI.PropertyField(your serialized property) without having to know the type of your serialized property.

    The exception is if the property points to an object reference and you want to draw a custom editor for that object reference. For example, say you have this class:

    Code (csharp):
    1. public class MyBehaviour : MonoBehaviour {
    2.     public ScriptableObject so;
    3. }
    and you've defined subclasses of ScriptableObject such as:

    Code (csharp):
    1. [CreateAssetMenu]
    2. public GameData : ScriptableObject {
    3.     public string aString;
    4. }
    with a custom editor:

    Code (csharp):
    1. [CustomEditor(typeof(GameData), true)] // 'true' to also apply editor to subclasses.
    2. public class GameDataEditor : Editor {
    3.     public override OnInspectorGUI() {
    4.         serializedObject.Update();
    5.         EditorGUILayout.LabelField("Game Data:");
    6.         EditorGUILayout.PropertyField(serializedObject.FindProperty("aString"));
    7.         serializedObject.ApplyModifiedProperties();
    8.     }
    9. }
    Then your custom editor for MyBehaviour can create an editor for its "so" without having to know the type ahead of time:

    Code (csharp):
    1. public class MyBehaviourEditor : Editor {
    2.  
    3.     private Editor m_editor;
    4.  
    5.     void OnEnable() {
    6.         var editor = Editor.CreateEditor(serializedObject.FindProperty("so").objectReferenceValue);
    7.     }
    8.  
    9.     public override voice OnInspectorGUI() {
    10.         editor.OnInspectorGUI();
    11.     }
    12. }
    I'm not sure if this is what you're asking, but I hope it helps.
     
  3. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Thanks @TonyLi for the explanation. Does EditorGUILayout.PropertyField(...) also work for missing scripts? I mean, when a component in the component list of a game object / user prefab returns null but I get the corresponding serialized property with something like:

    Code (CSharp):
    1. var serializedComponents = serializedObject.FindProperty ( "m_Component" );
    2. ...
    3. var serializedProperty = serializedComponents.GetArrayElementAtIndex ( i );
    Remember that "serializedProperty" corresponds to a missing script (which as component is null).
     
  4. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    And the answer to my question regarding EditorGUILayout.PropertyField(...) for missing scripts is "No": it returns a null editor and throws the following exception message: "type is not a supported pptr value".

    Any workaround?
     
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Try this:

    Code (csharp):
    1. for (int i = 0; i < serializedComponents.arraySize; i++) {
    2.     var serializedProperty = serializedComponents.GetArrayElementAtIndex( i );
    3.     if (serializedProperty.FindPropertyRelative("second").objectReferenceValue == null) continue; // Skip null.
    4.     //...your regular code...
    5. }
     
  6. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Actually I need to work with the "lost" reference to the missing script which is null in order to create a custom editor for it (for MonoBehaviour type). I don't know how Unity accomplish to get a general editor to deal with this out of null objectReferenceValue.
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Wow, if I understand you correctly, that's really obscure. You want to provide an editor for the serialized data that a GameObject or prefab still has for a missing script, right? If so, then as you've already remarked I don't think you can use Editor.CreateEditor. But you could go by the fileID to determine how to draw the properties and/or iterate over them using SerializedProperty.Next, passing each one to EditorGUI.PropertyField.
     
  8. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    > that's really obscure.

    Indeed. It's what Unity's editor does when a missing script is found and assigns a GenericEditor somehow with a non-null serializedObject.
     
  9. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    When there's a missing script, doesn't it just show a warning help box?

     
  10. Izzy2000

    Izzy2000

    Joined:
    Dec 18, 2013
    Posts:
    49
    might be little off topic:

    i think Unity Editor saves the serialized data, in this case the MonoBehaviour's serializable's, to the Scene_*.unity file. If you deleted a script, creating a new c# won't remedy it. You might have to dig up a backup version and copy over the .cs and .meta from File Explorer, to get rid of those Missing Mono Script message(s).

    Lesson is, Don't Delete the existing one thats in your Assets folder, if you're just going to replace it with an Updated version, and you wont have to worry about this issue.
     
  11. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    @TonyLi: I know, but this is for my own editor extension.

    @Izzy2000: you can remove the offending component by code, but it has 2 flaws: (1) it does not create an Undo entry, and (2) even if you save the scene, Unity's editor still watches the script "in the shadows" and so the only way to make sure is really deleted is by closing and reopening Unity's editor.
     
  12. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Right, so in that case you'll have to go by "fileID" or just iterate over the properties using SerializedProperty.Next.
     
  13. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Yes, but the thing is that the ""m_LocalIdentfierInFile" value is only available on the SerializedObject. And SerializedProperty.Next does not take me to a property linked to the missing script.
     
  14. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Sorry, no ideas. I hope you'll post the solution here when you find it, in case it can help others in the future.
     
  15. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Sure do, ... if I eventually find it. Thanks for your help, anyways.
     
  16. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    @TonyLi: so far, this is a deadend.

    It seems this is all handled from the unmanaged side of Unity3d.

    So far, all I have achieved is getting the property "second" from the missing behaviour. Then, with the help of EditorGUILayout.PropertyField select a script file and then assign the corresponding behaviour as a new component (since as we all know, direct instantiation of components with "new" is not allowed).

    But this approach has two problems: (1) even if you apply modifications and save the scenes and project, unless you close and re-open Unity, the missing script seems to be there (as if all modifications were disregarded), and (2) I have found no way to copy the serialized data of the missing script into the newly created one (since I don't know which property or set of properties hold those values), so the added behaviour is created with default values.

    So, as said at the begining of this post, this is kinda dead end.
     
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Sounds like it. It's probably safer anyway. Since it's well beyond the public API, Unity could change it whenever they want.
     
  18. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    On the bright side, the latest version of my plugin has been released on the Asset Store.

    http://u3d.as/CQm
     
    TonyLi likes this.
  19. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Looks good! Drag to reorder would be a nice addition. I always find it awkward to have to repeatedly click and then select Move Up or Move Down.
     
    PedroGV likes this.
  20. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Thanks. Added to the roadmap.

    I'm close to find a solution, all I need is to know which property holds the set of key/value pairs for each field serialized for the missing script component. Any clues?
     
  21. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Nothing more than what I posted above, sorry.
     
  22. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    Maybe someone on Unity's Team could tell me whether there is a way to get or create a generic inspector for a missing script? (where both, the component it-self and the objectreferencevalue of the "second" property, are null).
     
  23. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    @TonyLi: v1.9.6 is out with drag-reordering (ditto when you drag'n'drop a script into a game object).
     
    TonyLi likes this.
  24. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Just picked up a copy! I'm primarily working in Unity 5.3.6 at the moment, and unfortunately Spotlight Inspector seems to require 5.4.3 or higher, so I'll have to try it out later on a different project.
     
  25. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    @TonyLi: thanks! Actually, you can go ahead and install it with your current project. Even if I submitted it with v5.4.3, it actually works with every published version from 5.0 and up. Just import it to that project and enjoy!

    (I clarified it on the description section of the asset but maybe I should resubmit it with v5.0)
     
  26. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Definitely. It will help with your sales. Many people are still using 5.2 and 5.3.
     
  27. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,532
    I think the only effective way to do this is to cache the component, create a new instance of it and when it is missing just draw the default editor for the cached component (change the editor target). The property must exist, so if you create the component in memory then you can manipulate it. However, what you're actually trying to do in this scenario escapes me - I really can't think of a use case. Why do you need to handle missing components?

    The "missing" component is a special case due to the special way unity does null checks. IIRC there is no way to tell if a component is in the "missing" state as it has something to do with the pointer on the cpp side or something.
     
  28. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    @LaneFox: thanks for replying. It's for my plugin (please follow the link on my signature to know more). The problem is not on the component it-self, but in the associated SerializedObject to it. Also, you're suggestion, in case it worked, wouldn't apply for the case when you re-open the editor. You're right, it's a special case, but not for the missing pointer on the native side, but for the way it creates and reapplies a serializedobject mock to the GenericEditor (given that, from the managed side of things, you always need a non-null component to get an editor).

    @TonyLi: ok, I will.
     
  29. PedroGV

    PedroGV

    Joined:
    Nov 1, 2010
    Posts:
    415
    New version is up for Unity v5.2+