Search Unity

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.