- Home /
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.
Answer by Deadcow_ · Aug 25, 2015 at 10:23 AM
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:
Just for your information, you don't need to create an Editor (which is overkill) to access its SerializedObject field, you can just simply var soso = new SerializedObject(property.objectReferenceValue);
Answer by Neikice · Aug 22, 2016 at 11:14 AM
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;
}
}
Answer by MaDDoX · Aug 28, 2020 at 12:02 AM
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!
Answer by stepan-stulov · Aug 24, 2015 at 08:18 PM
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).
Yes, the main purpose is to reuse single SO instance with different prefab instances, but thanks anyway
Your answer
Follow this Question
Related Questions
Replicate "Apply" and "Revert" button functionality in ScriptableObject Editor Inspector 2 Answers
How to work with custom class (ScriptableObject) SerializedProperty? 1 Answer
Unity inspector field overlap with eachother 1 Answer
Error when trying to Serialize a field that is in a class 0 Answers
Custom Inspector: Using 'serializedObject' to access inherited members 1 Answer