Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to use Custom property drawers in a ScriptableObject inspector?

Discussion in 'Scripting' started by seldom, May 11, 2014.

  1. seldom

    seldom

    Joined:
    Dec 4, 2013
    Posts:
    118
    My custom property drawers are being applied to fields of MonoBehaviours when drawing a gameobject inspector. But an inspector of a ScriptableObject asset ignores custom property drawers, it shows the default field drawers instead.

    Is there a way around this?
     
    rakkarage likes this.
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Works for me...



    Code (csharp):
    1.  
    2. //menu.cs  -  example
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class menu : MonoBehaviour {
    7.  
    8.     public myOb someObject;
    9.  
    10.     void Reset () {
    11.         someObject = (myOb) ScriptableObject.CreateInstance( typeof( myOb ) );
    12.         someObject.asd = new Vector3( 1, 1, 1 );
    13.     }
    14.  
    15. }
    16.  
    17.  
    18.  
    19. //myOb.cs
    20. using UnityEngine;
    21. using System.Collections;
    22.  
    23. public class myOb: ScriptableObject {
    24.     public Vector3 asd;
    25.     public float qwe;
    26. }
    27.  
    28.  
    29. //obDrawer.cs
    30. using UnityEngine;
    31. using UnityEditor;
    32. using System.Collections;
    33.  
    34. [CustomPropertyDrawer( typeof( myOb ) )]
    35. public class obDrawer : PropertyDrawer {
    36.  
    37.     public override float GetPropertyHeight ( SerializedProperty property, GUIContent label ) {
    38.         return 32;
    39.     }
    40.  
    41.     public override void OnGUI ( Rect position, SerializedProperty property, GUIContent label ) {
    42.         position = EditorGUI.PrefixLabel( position, GUIUtility.GetControlID( FocusType.Passive ), label );
    43.         position.height = 16;
    44.  
    45.         EditorGUIUtility.labelWidth = 50f;
    46.         SerializedObject SO = new SerializedObject( property.objectReferenceValue );
    47.  
    48.         SerializedProperty myProp = SO.FindProperty( "asd" );
    49.         myProp.vector3Value = EditorGUI.Vector3Field( position, "asd: ", myProp.vector3Value );
    50.  
    51.         myProp = SO.FindProperty( "qwe" );
    52.         position.y += 16;
    53.         myProp.floatValue = EditorGUI.FloatField( position, "qwe: ", myProp.floatValue );
    54.  
    55.         SO.ApplyModifiedProperties();
    56.     }
    57. }
     
  3. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I think he meant without a wrapping MonoBehaviour...

    ScriptableObject can be saved as .asset and browsed directly.
     
  4. seldom

    seldom

    Joined:
    Dec 4, 2013
    Posts:
    118
    Thank you both for your replies. LightStriker is right, I'm working with .asset files that are ScriptableObjects. They contain rules for population a scene with GameObjects, which is why they are not part of a scene themselves. Selecting such a file will bring up the usual inspector, with default property drawers for all public fields. It's just that this inspector ignores my custom drawers.
     
  5. gfoot

    gfoot

    Joined:
    Jan 5, 2011
    Posts:
    550
    It works fine for me, within ScriptableObject assets. Here's a cut-down example:

    Code (csharp):
    1.  
    2. // MyPropertyType.cs
    3.  
    4. using System;
    5.  
    6. [Serializable]
    7. public class MyPropertyType
    8. {
    9.     public string Value;
    10. }
    11.  
    Code (csharp):
    1.  
    2. // Editor/MyPropertyDrawer.cs
    3.  
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. [CustomPropertyDrawer(typeof(MyPropertyType))]
    8. public class MyPropertyDrawer : PropertyDrawer
    9. {
    10.     private float _height = 100;
    11.     private float _indent = 30;
    12.  
    13.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    14.     {
    15.         return _height;
    16.     }
    17.    
    18.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    19.     {
    20.         EditorGUI.BeginProperty(position, label, property);
    21.        
    22.         EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive),
    23.                               new GUIContent(label.text));
    24.  
    25.         var valueProperty = property.FindPropertyRelative("Value");
    26.        
    27.         float labelHeight = base.GetPropertyHeight(property, label);
    28.  
    29.         var region = new Rect(position.x + _indent, position.y + labelHeight, position.width - _indent - 10, position.height - labelHeight - 10);
    30.        
    31.         EditorGUI.BeginChangeCheck();
    32.         var newValue = EditorGUI.TextArea(region, valueProperty.stringValue);
    33.         if (EditorGUI.EndChangeCheck())
    34.         {
    35.             valueProperty.stringValue = newValue;
    36.         }
    37.  
    38.         EditorGUI.EndProperty();
    39.     }
    40. }
    41.  
    Code (csharp):
    1.  
    2. // TestScriptableObject.cs
    3.  
    4. using UnityEngine;
    5.  
    6. public class TestScriptableObject : ScriptableObject
    7. {
    8.     public MyPropertyType Field1;
    9.     public MyPropertyType Field2;
    10. }
    11.  
    Code (csharp):
    1.  
    2. // Editor/CreateTestScriptableObject.cs
    3.  
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. public class CreateTestScriptableObject
    8. {
    9.     [MenuItem("Assets/Create/TestScriptableObject")]
    10.     public static void CreateAsset()
    11.     {
    12.         var asset = ScriptableObject.CreateInstance<TestScriptableObject>();
    13.         AssetDatabase.CreateAsset(asset, "Assets/Whatever.asset");
    14.         AssetDatabase.SaveAssets();
    15.     }
    16. }
    17.  
    Execute the menu option to create "Whatever.asset", then select it, and the Inspector should show multi-line TextArea boxes for editing the strings within the MyPropertyType fields.
     
  6. gfoot

    gfoot

    Joined:
    Jan 5, 2011
    Posts:
    550
    Make sure you haven't got the Inspector in "debug" mode though - in that mode it won't use custom PropertyDrawers. I guess it's a feature, as it lets you edit things the PropertyDrawers don't expose.
     
    CoughE likes this.
  7. seldom

    seldom

    Joined:
    Dec 4, 2013
    Posts:
    118
    Thank you for the full example. Based on it I figured out why my custom drawer was ignored: the ScriptableObject class name has to match the name of the file it lives in, like it is for MonoBehaviours.

    This is pretty obscure, because with non-matching names the asset still works fine besides ignoring custom drawers.
     
  8. CxydaIO

    CxydaIO

    Joined:
    May 12, 2014
    Posts:
    61
    Hey one question, i have also a Scriptable Object type and used the scripts above to get my PropertyDrawer working, it works well for single Objects but as soon as i want to display a list of this Object in the inspector i get a strange behaviour. When i change lets say element 1 of the list every other element of this List gets the same value assigned. How can i fix this?
     
  9. Estecka

    Estecka

    Joined:
    Oct 11, 2013
    Posts:
    62
    I've had the same problem as OP, except all my files were already matching the classes names.
    In the end it turned out I was using an old asset I created very early, the PropertyDrawer did in fact work on newly created assets.
     
    rakkarage likes this.