Draw ScrptableObject inspector in other inspector

I want to share some data between instances of a prefab and I need to be sure this data is the same, either prefab changes applied or not. I decided to use ScriptableObject instance for this, and now I need to display SO properties in the Inspector of script, where SO is a public property.

So… I made empty property attribute

public class DisplayScriptableObjectPropertiesAttribute : PropertyAttribute
{
}

and custom property drawer

[CustomPropertyDrawer(typeof(DisplayScriptableObjectPropertiesAttribute))]
public class DisplayScriptableObjectPropertiesDrawer : PropertyDrawer
{
 // Draw the property inside the given rect
 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
 {
 	EditorGUI.PropertyField(position, property);
 }
}

…but since I’m not too experienced with SerializedProperty I dont know how to display child properties in generic way if SerializedProperty is a ScriptableObject.

My use case

[Serializable]
[CreateAssetMenu]
public class GameManagerSettings : ScriptableObject
{
[SerializeField] public bool SharedBool;
}

public class GameManager : MonoBehaviour
{
 [DisplayScriptableObjectProperties]
 public GameManagerSettings Settings;
...
}

and I need to display “SharedBool” in “GameManager” inspector.

Okay, I find the way to do it. My code probably is not as good as it may be (especially in GetPropertyHeight function), but it did the trick.

[CustomPropertyDrawer(typeof(DisplayScriptableObjectPropertiesAttribute))]
public class DisplayScriptableObjectPropertiesDrawer : PropertyDrawer
{
	// Draw the property inside the given rect
	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
	{
		var e = Editor.CreateEditor(property.objectReferenceValue);

		position.height = 16;
		EditorGUI.PropertyField(position, property);
		position.y += 20;

		if (e != null)
		{
			position.x += 20;
			position.width -= 40;
			var so = e.serializedObject;
			so.Update();

			var prop = so.GetIterator();
			prop.NextVisible(true);
            while (prop.NextVisible(true))
			{
				position.height = 16;
				EditorGUI.PropertyField(position, prop);
				position.y += 20;
			}
			if (GUI.changed)
				so.ApplyModifiedProperties();
		}
	}

	public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
	{
		float height = base.GetPropertyHeight(property, label);
        var e = Editor.CreateEditor(property.objectReferenceValue);
		if (e != null)
		{
			var so = e.serializedObject;
			var prop = so.GetIterator();
			prop.NextVisible(true);
			while (prop.NextVisible(true)) height += 20;
		}
		return height;
	}
}

this is my SO:

[Serializable]
[CreateAssetMenu]
public class GameManagerSettings : ScriptableObject
{
	public bool MyBool;
	public int MyInt;
}

where I use it:

public class GameManager : MonoBehaviour
{
	[DisplayScriptableObjectProperties]
	public GameManagerSettings Settings;
...

and how it’s looks like:
alt text

Deadcow_ ,Thanks for your answer. On top of your Foundation, I modified some code. Now correctly displays information about children and SerializedPropertys .

[CustomPropertyDrawer(typeof(DisplayScriptableObjectPropertiesAttribute))]
public class DisplayScriptableObjectPropertiesDrawer : PropertyDrawer
{
    bool showProperty = false;
    float DrawerHeight = 0;
    string button = "-";
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var e = Editor.CreateEditor(property.objectReferenceValue);
        var indent = EditorGUI.indentLevel;

    Rect temp = new Rect(position.x , position.y,16 , 16);

    if (GUI.Button(temp, button))
        if (showProperty)
        {
            showProperty = false;
            button = "-";
        }
        else
        {
            showProperty = true;
            button = "|";
        }

        DrawerHeight = 0;
        position.height = 16;
        EditorGUI.PropertyField(position, property);
        position.y += 20;

        if (!showProperty) return;

        if (e != null)
        {
            position.x += 20;
            position.width -= 40;
            var so = e.serializedObject;
            so.Update();

            var prop = so.GetIterator();
            //Debug.Log(" prop.hasVisibleChildren " + prop.hasVisibleChildren);
            prop.NextVisible(true);

            int depthChilden = 0;
            bool showChilden = false;

            while (prop.NextVisible(true))
            {

                if (prop.depth == 0) { showChilden = false; depthChilden = 0; }

                if (showChilden && prop.depth > depthChilden)
                {
                    continue;
                }
                position.height = 16;

                EditorGUI.indentLevel = indent + prop.depth;

                if (EditorGUI.PropertyField(position, prop))
                {
                    showChilden = false;
                }
                else
                {
                    showChilden = true;
                    depthChilden = prop.depth;
                }

                position.y += 20;
                SetDrawerHeight(20);
            }
           

            if (GUI.changed)
            {
                so.ApplyModifiedProperties();
            }
        }
    }

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    float height = base.GetPropertyHeight(property, label);
    height += DrawerHeight;
    return height;
}

void SetDrawerHeight(float height)
{
    this.DrawerHeight += height;
}

}

76653-sp160822-180548.png

76654-sp160822-180605.png

Unity mimics true object referencing when working with instances of ScriptableObject and its subclasses. Which conceptually implies drag’n’dropping objects onto fields of the corresponding type. What you can also do is double-click on such field, and you will be sent directly to the inspector of the instance of the game manager settings.

There is another tool within Unity serialization, that enables inline editing in contrast with ScriptableObject — classes that don’t extend anything (well System.Object) and that are marked with [Serializable] attribute. Such classes will be edited in place, but won’t exist outside of the owner. That said, this is not impossible to do what you want, but are these [Serializable] classes actually what you want?

My rule of thumb for deciding between drag’n’drop or inline is based on answering a simple question: will you reuse the instance? If yes — drag’n’drop (ScriptableObject), if not — inline ([Serializable] class).

Outstanding, best free SO inlining solution I found anywhere. I’ve fixed a minor button overlapping issue and turned it into a Unity package, to make it easier for others. Tested and working perfectly on Unity 2020.1. Kudos to everyone who contributed to this!