- Home /
Display Custom Inspectors for each class in a List<>
I'm not sure how to proceed with this, but I've been investigating PropertyDrawer and PropertyAttribute and I'm not sure if they're going to work for what I want.
Here's the setup:
public class ContainerClass : Monobehaviour
{
public List<BaseBehavior> behaviors;
public void AddToList(BaseBehavior b)
{
if(!behaviors.Contains(b))
behaviors.Add(b);
}
...
public void BunchaFunctions() { //etc }
}
[System.Serializable]
public class BaseBehavior : ScriptableObject
{
public float force;
public Vector3 direction;
}
[System.Serializable]
public class FirstBehavior : BaseBehavior
{
public float additionalForce;
public float delayTime;
}
[System.Serializable]
public class SecondBehavior : BaseBehavior
{
public bool isStupendous;
public Vector3 exampleVector;
}
Here's the loop in my editor class where I might expand / view each Behavior class:
ContainerObject control = target as ContainerObject ;
int index = 0;
foreach (BaseBehavior in control.behaviors)
{
foldouts[index] = EditorGUILayout.Foldout(foldouts[index], behavior.ReportType().ToString());
if (foldouts[index])
{
// this is where I assume display info goes?
}
index++;
}
What I have so far is an Editor class that creates a custom inspector for ContainerClass. The custom inspector allows me to select and add various behavior types from my project to the List. All good so far. I can display the list of behaviors, but now I need to get into each Behavior and edit its parameters.
Normally this wouldn't be a problem without a custom inspector, as you can just expand the list, look at each element and expand the available parameters that way. Unfortunately, implementing a custom inspector overrides that ability.
How can I get that back without coding an inspector for each class? Or at least making something general enough that it works for any new Behaviors written, regardless of what variables the new Behaviors might have (so long as they inherit from BaseBehavior). At this stage, it doesn't necessarily need to be pretty, so long as the variables are accessible.
At some point I found myself delving into System.Reflection to pull apart each behavior and send back arrays of variables to be displayed. That seems like overkill, and presents its own world of problems.
What are my options in this scenario?
Thanks in advance
Answer by Loius · Nov 12, 2013 at 11:51 PM
You can use Editor.CreateEditor(object) to get the actual, for-srs editor for an object. Then in your custom OnInspectorGUI, call each editor's OnInspectorGUI.
I've got a couple snippets from my own "Another Inspector" which is sorta tied in to custom classes, but it gets the point across:
This is my EditorWindow's ONGUI:
public void OnGUI() {
inspect = EditorGUILayout.ObjectField(inspect,typeof(UnityEngine.Object),true);
if ( old != inspect || (null==editors && null!=inspect)) {
if ( typeof(GameObject) == inspect.GetType() ) {
editors = new AnInspector[0];
foreach(UnityEngine.Object ob in (inspect as GameObject).GetComponents<Component>()) {
editors = editors.With(new AnInspector(Editor.CreateEditor(ob)));
// Debug.Log(editors[editors.Length-1].GetType().FullName);
}
} else {
editors = new AnInspector[]{new AnInspector(Editor.CreateEditor(inspect))};
}
}
if ( null != inspect && null != editors ) {
using ( new GUIUtils.Scroll(ref scroll) ) {
foreach(AnInspector editor in editors) {
editor.OnInspectorGUI();
}
}
}
old = inspect;
}
And this is AnInspector:
private class AnInspector {
public AnInspector(Editor e) { editor = e; }
Editor editor;
bool open=true;
public void OnInspectorGUI() {
GUIUtils.StyledFoldout(ref open, "Component: " + editor.target.GetType().Name.AsSpacedCamelCase(),()=>{
editor.OnInspectorGUI();
},"","");
// },"serverupdatechangeseton");
// },"tl selectionbutton predropglow");
GUILayout.Button("",GUILayout.Height(1f));
}
}
http://docs.unity3d.com/Documentation/ScriptReference/Editor.CreateEditor.html
GUIUtils just wraps things I find awkward to do all the time (like Begin/End Layout functions)
Here's Scroll:
public class Scroll : IDisposable {
public Scroll(ref Vector2 scroll, params GUILayoutOption[] options) { scroll=GUILayout.BeginScrollView(scroll, options); }
public Scroll(ref Vector2 scroll, string style, params GUILayoutOption[] options ) { scroll=GUILayout.BeginScrollView(scroll,style,options); }
public Scroll(ref Vector2 scroll, GUIStyle style, params GUILayoutOption[] options ) { scroll=GUILayout.BeginScrollView(scroll,style,options); }
public void Dispose() { GUILayout.EndScrollView(); }
}
StyledFoldout's more complex, but same concept, just wrapping layouting:
public static void StyledFoldout(ref bool state, string text, EmptyDelegate function, string style = "box", string style2 = "textarea") {
using ( new Vertical(style) ) {
Foldout(ref state, text);
if ( state )
using ( new Indent() )
using ( new Vertical(style2) )
function();
}
}
Vertical and Indent are disposables like Scroll.
And Foldout:
public static bool Foldout(ref bool open, GUIContent text ) {
Init();
if ( open ) {
if ( GUILayout.Button( text, openFoldoutStyle, GUILayout.Height(20) ) ) {
GUI.FocusControl ("");
GUI.changed = false; // force change-checking group to take notice
GUI.changed = true;
open = false;
}
} else {
if ( GUILayout.Button( text, closedFoldoutStyle, GUILayout.Height(20) ) ) {
GUI.FocusControl ("");
GUI.changed = false; // force change-checking to take notice
GUI.changed = true;
open = true;
}
}
return open;
}
awesome! I was trying to do something like this, but I couldn't quite get it happening. I'll check this out right now, thanks!
hmm, kinda lost me with your custom classes.
AnInspector doesn't inherit from Editor, but you have it appearing as an editor out of an IEnumerator from editors which appear to be of type Editor.
also, your GUIUtils is a custom class that seems to be doing some of the heavy lifting, and that's some of the portion that I don't understand. I grasp (I believe) how to create a series of editors for each class and iterate through them, but I'm not sure how you're passing the class references.
This almost makes sense to me, but the portions I can't see are confusing. Could you possibly make it more general?
for example, this: using ( new GUIUtils.Scroll(ref scroll) ) {
looks to me like you're including a library, but ins$$anonymous$$d you're calling a function from a new instance of a class and passing it a reference variable. er, what? Sorry, that's over my head
Essentially I'm keeping a list of AnInspectors (which are just wrappers). AnInspector isn't an editor, it just has a function named OnInspectorGUI which calls its wrapped editor's OnInspectorGUI function. The only reason for AnInspector is so I can have its "open" boolean, so I can collapse specific editors (like transforms or whatever)
Creating an AnInspector with new AnInspector(UnityEngine.Object object) sets that AI's editor to a new editor created for that object. So when I call that editor's OnInspectorGUI, it inspects that object.
.With is a custom function too, whoops. That's adding on the the end of the array, I forget how to do that The Right Way. It's easy enough to build the array at the right size to start with though.
public static T[] With<T>(this T[] arr, params T[] other) {
List<T> l = new List<T>(arr);
l.AddRange(other);
return l.ToArray();
}
I'll put the GUIUtils description in the answer, it's long. x)
no, don't worry about it. I actually just solved it in the last few $$anonymous$$utes, and was about to post here about it. I got it! thanks, I appreciate it! I just had to strip some things apart from what you posted, and it took longer than I was hoping. All set!
Your answer
Follow this Question
Related Questions
Call a function from CustomPropertyDrawer of Arbitrary class 1 Answer
How to get "name" member of a ScriptableObject, as a SerializedProperty 2 Answers
How do I associate my custom object for the serializedProperty objectReferenceValue 1 Answer
Applying a PropertyDrawer to elements of a List<> 1 Answer
How to work with custom class (ScriptableObject) SerializedProperty? 1 Answer