Unity Customizes Three Ways of Displaying ScriptableObject Properties

Keywords: Attribute Unity

1. Inherit Editor and rewrite OnInspector GUI method

Editor Official Documents

Effect

Realization

Define a test class TestClass, a serializable class DataClass

[CreateAssetMenu]
public class TestClass : ScriptableObject
{
    [Range(0, 10)]
    public int intData;
    public string stringData;
    public List<DataClass> dataList;
}

[System.Serializable]
public class DataClass
{
    [Range(0, 100)]
    public int id;
    public Vector3 position;
    public List<int> list;
}
//Specified type
[CustomEditor(typeof(TestClass))]
public class TestClassEditor  : Editor
{
    SerializedProperty intField;
    SerializedProperty stringField;

    void OnEnable()
    {
        //Gets the specified field
        intField = serializedObject.FindProperty("intData");
        stringField = serializedObject.FindProperty("stringData");
    }

    public override void OnInspectorGUI()
    {
        // Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
        serializedObject.Update();
        EditorGUILayout.IntSlider(intField, 0, 100, new GUIContent("initData"));
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PropertyField(stringField);
        if(GUILayout.Button("Select"))
        {
            stringField.stringValue = EditorUtility.OpenFilePanel("", Application.dataPath, "");
        }
        EditorGUILayout.EndHorizontal();

        // Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
        //You need to modify attributes before OnInspector GUI, otherwise you cannot modify values
        serializedObject.ApplyModifiedProperties();

        base.OnInspectorGUI();
    }
}

Editor nesting

adopt Edtiro.CreateEditor Editor can be nested.

Create a class TestClass2 that contains the properties of a TestClass.

[CreateAssetMenu]
public class TestClass2 : ScriptableObject
{
    public TestClass data;
}

Create an asset for Test2Class. The default display of its Inspector panel is:

It does not show the properties of TestClass. If you want to view the properties of TestClass, you must double-click and jump to the corresponding interface, but you can't see the properties of TestClass2.

If you want to see and modify the properties of TestClass directly in the Inspector panel of Test2Class, you can override the Editor of TestClass2 and nest the Editor of TestClass in it.

[CustomEditor(typeof(TestClass2))]
public class TestClass2Editor : Editor
{
    Editor cacheEditor;
    public override void OnInspectorGUI()
    {
        // Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
        serializedObject.Update();
        //Display the default UI for TestClass2
        base.OnInspectorGUI();

        GUILayout.Space(20);
        var data = ( (TestClass2)target ).data;
        if(data != null)
        {
            //Editor for Creating TestClass
            if (cacheEditor == null)
                cacheEditor = Editor.CreateEditor(data);
            GUILayout.Label("this is TestClass2");
            cacheEditor.OnInspectorGUI();
        }
    }
}


This allows you to view and edit the properties of TestClass directly in the TestClass2 panel.

2. Use Property Drawer

Property Drawer official document

If you want to modify a particular type of display, using the inheritance of Editor can become very cumbersome, because all use of a specific type of asset need to implement a custom Editor, which is very inefficient. In this case, the attributes of the specified type can be uniformly displayed by inheriting Property Drawer.

Effect

Add a select File button for all string attributes in the Inspector panel, and assign the path of the selected file directly to the variable.

Realization

[CustomPropertyDrawer(typeof(string))]
public class StringPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Rect btnRect = new Rect(position);
        position.width -= 60;
        btnRect.x += btnRect.width - 60;
        btnRect.width = 60;
        EditorGUI.BeginProperty(position, label, property);
        EditorGUI.PropertyField(position, property, true);
        if (GUI.Button(btnRect, "select"))
        {
            string path = property.stringValue;
            string selectStr = EditorUtility.OpenFilePanel("Select Files", path, "");
            if (!string.IsNullOrEmpty(selectStr))
            {
                property.stringValue = selectStr;
            }
        }

        EditorGUI.EndProperty();
    }
}

After adding a Property Drawer, all ** string variables in the Inspector panel add an additional Select button.

Matters needing attention

  1. Property Drawer is only valid for serializable classes, and non-serializable classes cannot be displayed in the Inspector panel.
  2. OnGUI method can only use GUI-related methods, not Layout-related methods.
  3. Property Drawer modifies the way all attributes of the corresponding type are displayed, such as creating a MonobeBehavior with string attributes:

3. Use Property Attribute

Property Attribute Official Document

If you want to modify the display of attributes of specified types of some classes, you can't meet the requirement by using Property Drawer directly. At this time, you can combine Property Attribute and Property Attribute to realize the requirement.

Effect

Add slider to display int or float attributes of partially specified classes. The upper and lower limits of slider can be set according to classes and attributes.

Realization

public class RangeAttribute : PropertyAttribute
{
    public float min;
    public float max;

    public RangeAttribute(float min, float max)
    {
        this.min = min;
        this.max = max;
    }
}

[CustomPropertyDrawer(typeof(RangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // First get the attribute since it contains the range for the slider
        RangeAttribute range = attribute as RangeAttribute;

        // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label);
        else
            EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
    }
}

Modify TestClass and DataClass

[CreateAssetMenu]
public class TestClass : ScriptableObject
{
    [Range(0, 10)]
    public int intData;
    public string stringData;
    public List<DataClass> dataList;
}

[System.Serializable]
public class DataClass
{
    [Range(0, 100)]
    public int id;
    public Vector3 position;
    public List<int> list;
}

Other

  • All the classes that need to be modified to display need to be satisfied Unity's serialization rules
  • These display modes can be used for Serializable Class, not necessarily scriptable Object. Just in the editor, it's common to use Scriptable Object to save temporary data, so use Scriptable Object as an example.

demo Download

For reprinting, please indicate the source: http://blog.csdn.net/wuwangxinan/article/details/72773297

Posted by ptolomea on Thu, 27 Jun 2019 12:05:24 -0700