- Home /
Organizing Recursive EditorGUI elements
I am trying to make a dialog editor for my Dialog class, and after getting my dialog initially instantiated and visible in the editor, I hit a wall. I could not conceive of how to organize it without creating a bool inside the dialog class itself. Here is what I have so far, any ideas? Whatever it takes to organize this crap, preferably without adding extra crap to my Dialog class.
This is the Dialog Class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace SpaceScoundrels
{
public class Dialog : ScriptableObject
{
public string Entry;
public Dialog[] Responses;
public Dialog Next(int choice)
{
return Responses[choice];
}
public virtual string GetEntry()
{
return Entry;
}
}
}
A section from my Editor class, it is like 1k lines of code so I onl took out the dialog part.
if (ActiveState == WindowState.DialogEditor)
{
if (activeDialog == null)
{
if (GUILayout.Button("Create Dialog"))
{
activeDialog = CreateInstance<Dialog>();
}
}
else
{
activeDialog.name = EditorGUILayout.TextField("Name", activeDialog.name);
activeDialog.Entry = EditorGUILayout.TextArea(activeDialog.Entry, GUILayout.Height(100));
dialogCount = EditorGUILayout.IntField("Dialog Options", dialogCount);
if (GUILayout.Button("Add Dialogs") && dialogCount > 0)
{
activeDialog.Responses = new Dialog[dialogCount];
for (int i = 0; i < activeDialog.Responses.Length; i++)
{
activeDialog.Responses[i] = CreateInstance<Dialog>();
}
}
dialogHierarchy = 1;
IterateDialog(activeDialog);
if (GUILayout.Button("Save Dialog"))
{
AssetDatabase.CreateAsset(activeDialog, "Assets/GameKit/Dialog/" + activeDialog.name + ".asset");
newItem = null;
}
}
}
The recursion method.
void IterateDialog(Dialog dialog)
{
if (dialog.Responses != null)
{
foreach (Dialog section in dialog.Responses)
{
if (section != null)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Space(10 * dialogHierarchy);
section.Entry = EditorGUILayout.TextField("Entry", section.Entry);
if (GUILayout.Button("Add Dialogs"))
{
section.Responses = new Dialog[3];
for (int i = 0; i < section.Responses.Length; i++)
{
section.Responses[i] = CreateInstance<Dialog>();
}
}
EditorGUILayout.EndHorizontal();
IterateDialog(section);
dialogHierarchy += 1;
}
}
}
}
Finally... my result.
Answer by Bunny83 · Jan 29, 2017 at 06:57 PM
Well, it's not really clear how you want it to be organised. The boolean you talked about, what is it for? Just for expanding / collapsing child dialogs? You can "store" those booleans inside the editor class itself. Of course when you close the editor the settings would be gone.
As of how to improve the layout, there are several things you might change:
First use a fix size for things that have a fix-sized content, like your "Add Dialogs" button
if (GUILayout.Button("Add Dialogs", GUILayout.Width(80))); // 80 might be tweaked to get the best result
Doing this will preserve as much of the flexible space as possible for the dynamic elements. Personally i wouldn't use the auto-labelled version of "TextField". Just using a seperate Label and a simple TextField. That way you can better control the size of each element.
Next problem is that the way you handle your indention makes not much sense. You only increase dialogHierarchy but never decrease it. So elements on the same layer wouldn't be at the same indent-level since your recursive method might increase the value for it's childs but when it returns you stay at that level.
So you either do this:
dialogHierarchy ++;
IterateDialog(section);
dialogHierarchy --;
inside your recursive method, or, what would be the better solution, pass the depth on as parameter
IterateDialog(section, depth + 1);
That way you don't need a class variable for information that is only relevant during the iteration.
To store additional information for each Dialog, you might use a Dictionary<Dialog, "AdditionalStuff">
. So you can easily store whatever you want. For example using a seperate class that holds that additional information for each Dialog. In the case of the "expanding / collapsing" bool you can store a single boolean for each Dialog to indicate if it's children are expanded or not.
public Dictionary<Dialog, EditorDialog> editorDialogs = new public Dictionary<Dialog, EditorDialog>();
public class EditorDialog
{
public bool childsExpanded = false;
// ...
}
EditorDialog GetEditorDialog(Dialog aDialog)
{
EditorDialog result;
if (!editorDialogs.TryGetValue(aDialog, out result))
{
result = new EditorDialog();
editorDialogs.Add(aDialog, result);
}
return result;
}
With that you can simply add this line:
var editorDialog = GetEditorDialog( dialog );
and set / get the "childsExpanded" as you need.
Sorry for the lack of info on what I was envisioning, but this is basically the answer I needed. Thanks so much, I can't believe I didn't think of making another Editor-Only class to handle the dialogs more, because its only for editing purposes I could literally add any amount of data I needed. Also the dictionary idea is excellent, and the dialogHierarchy was a poor attempt at your much better idea of ++, iterate, --. Essentially you covered everything I was unable to think of at the moment. I was desperately trying to figure this out yesterday so I could get my $$anonymous$$m on making quests and dialogs, but I had to call it a day after taxing my brain for so many hours. Thanks again Bunny83, you always have a good answer.
ps: you might want to wrap your initial call of your recursive method in a ScrollView.
It's in general difficult how to manage tree structures in the most usable way. What most implementations do is to seperate the tree from the additional data. Examples are Unity's Hierarchy view and inspector view or if you look at things like the windows registry editor, it's splitted into a treeview on the left and the actual details view on the right.
Another common way to approach this is using seperate editor windows. So one window simply edits one Dialog. It might "show" details of the direct children but not more. You can add a button to each child which will open a seperate window for that child dialog. This can be also be done with a single window if you additionally display the current tree-path somewhere. This is how the windows explorer works (since Windows 7 i think). So clicking on the child buttons would move "into" the depth of the tree. Clicking one of the "parent" buttons of the current tree path would allow to move back up to the root.
Since a tree grows two dimensional it's quite common to display each dialog as a node in a 2d node graph (like Unity's mecanim).
Not sure how I would easily accomplish the node editing but I can't image it would be too difficult. From all your wondrous input, I managed to make this for far. Which my guys can at least work with.
Your answer
Follow this Question
Related Questions
How to combine two buttons in one. Editor window 2 Answers
Custom Editor - Is there any way to detect whether the user is in Prefab editing mode? 1 Answer
EditorGUILayout.TextArea text goes out of bounds 0 Answers
Weird action in Unity editor when pressing the "E" key? 1 Answer
Editor GUI object field stops working 0 Answers