- Home /
Instantiating and displaying enum objects using custom inspector
My script extracts all child objects from a parent object. I need to assign materials individually to child objects or material to all objects via parent object.
Currently when I change a child material, all other child object materials get changed aswell. Here is my code
if (includeChildObj == true)
{
EditorGUILayout.Foldout(includeChildObj, "List of Child Objects", includeChildObj);
for (int i = 0; i < Geometry.getChildNames().Count; i++) // loop through all child objects
{
GUILayout.BeginHorizontal();
GUILayout.Label(Geometry.getChildNames()[i]); // display object name
EditorGUILayout.PropertyField(serializedObject.FindProperty("SelectMaterial")); // find an enum which allows the dropdown list
GUILayout.EndHorizontal();
serializedObject.ApplyModifiedProperties();
}
}
This is how it looks in Unity. When I select one child object's material, everything else changes but the selection is supposed to be independent.
Answer by troien · Mar 08, 2019 at 11:59 AM
First of all. serializedObject points to the object(s) you currently have selected in the hierarchy. So calling EditorGUILayout.PropertyField(serializedObject.FindProperty("SelectMaterial"));
a lot of times will just display a dropdown of the item you selected in the hierarchy a lot of times, it won't show a dropdown for each of its parent's childs. If you want this to work, you need to try and modify the other child values instead of your own ;)
On a sidenote, I don't think you use EditorGUILayout.Flodout properly. Because location inside if statement and the third parameter you provide don't make a lot of sense to me.
Below is a simple example of how you could do that, without a lot of testing. So check whether this persists properly and whether Undo/Redo functionality works as intended. As persisting changes when changing scenes or restarting unity can be tricky if you are modifying objects that you don't have selected in the hierarchy.
[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
private bool includeChildObj;
public override void OnInspectorGUI()
{
// Display dropdown for self
EditorGUILayout.PropertyField(serializedObject.FindProperty("SelectMaterial"));
serializedObject.ApplyModifiedProperties();
// Display dropdown for each child
Transform parent = (target as Example).transform.parent;
includeChildObj = EditorGUILayout.Foldout(includeChildObj, "List of Child Objects", true);
if (includeChildObj)
{
foreach (var child in parent.GetComponentsInChildren<Example>())
{
GUILayout.BeginHorizontal();
GUILayout.Label(child.name);
SerializedObject so = new SerializedObject(child);
EditorGUILayout.PropertyField(so.FindProperty("SelectMaterial"));
GUILayout.EndHorizontal();
so.ApplyModifiedProperties();
}
}
}
}
Naturally you can't add CanEditMultipleObjects to this. Because we are using Target which point to just one object instead of all selected objects
If you have a different setup, where you only have this script added to the parent object and not the child objects, then this won't work, in that case, there needs to be some place where you store the value for each child so you can edit it. In your question it isn't exactly clear to me where that is, so I made assumptions...
Wow!! Not all heroes wear capes! Will carefully read it and let you know how it goes.
Yes, I have this script added to the parent object. Idea is that I can add a single script to a parent object, assign this metadata which is then exported to text file.
Where do you then plan to store this enum value in code? As in, you want a dropdown, where is the value of that dropdown supposed to be stored? As that will affect how you do this in editor scriptin
Does each child have a script on them that stores which enum value it has? (probably the easiest solution)
Does the parent just store an array of enum values itself, linking it 'somehow' to the correct child? Can work, but linking can be difficult as the childs can be added/removed in the hierarchy without notifying the parent, possibly breaking any links.
Did you have something else in $$anonymous$$d?
As in order to get a solution to edit it in the Unity inspector, we first need to know what it is we are editing, and where we store this data. In your example, I kind of get the idea that you are atm. not storing this data anywhere at all. And editing non-existing values is impossible :p
None of the child objects have a script that stores a value, that kind of takes the point of this script apart because for production use I would just attach it to all parents and quickly assign and export materials to child objects.
Currently the parent store the material as a single string which is then read by text exporting script.
I have a following solution in my $$anonymous$$d.
private List<String> ChildNameLis = new List<String>();
private List<String> Geometry$$anonymous$$aterials = new List<String>();
OnEnable() {
Reference parent object ->
Get child names ->
Store child names in a ChildNameList ->
}
OnGUI() {
Use For(int = 0; int < ChildNameList .Length; int++)
{
var ChildGeometry = ParentObject.transform.FindChild(ChildNameList [i]);
// So far I have a reference here to my child object.
// And now I would want to somehow say that each of these child objects gets an instance of a class which contains only an enum list of materials
}
}
What do you think of this? Could you perhaps help me visualise how I would assign that enum instance to each of the objects? $$anonymous$$aybe create a 2D array where in 1 column I've got materials and the 2. column I've got child object names?
So although I still have questions on how and why, I think with this code I can help you enough, as although this does not persist anything in the parent yet, I'm not even sure if you want that.
So this code just does the following: the moment that you open the inspector, it fills an array with all child transforms and one array with (default value) enum values. You can then edit those individually. It doesn't store the values anywhere yet after closing the inspector. But I'm still not sure whether you actually want to do that :p As far as what you've told me so far, I think that you can finish this code so it does what you want.
[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
private bool includeChildObj;
Transform[] _childTransforms;
ExampleEnum[] _childValues;
private void OnEnable()
{
// Some magic to get all children of our transform in a single line of code. (requires "using System.Linq;" at the top)
_childTransforms = (target as $$anonymous$$onoBehaviour).transform.Cast<Transform>().ToArray();
// Initialize our array with a default value
_childValues = _childTransforms.Select(x => GetDefaultValue(x)).ToArray();
}
// If you actually set the Renderer.$$anonymous$$aterial to something with the same name as your enum value (if that is what you mean with 'export materials to childs', you could use something like this ins$$anonymous$$d
//private ExampleEnum GetDefaultValue(Transform child)
//{
// ExampleEnum returnValue;
// if (System.Enum.TryParse(child.GetComponent<Renderer>().material.name, out returnValue))
// return returnValue;
// else
// return ExampleEnum.A;
//}
private ExampleEnum GetDefaultValue(Transform child)
{
return ExampleEnum.A;
}
public override void OnInspectorGUI()
{
// Display dropdown for each child
includeChildObj = EditorGUILayout.Foldout(includeChildObj, "List of Child Objects", true);
if (includeChildObj)
{
for (int i = 0; i < _childTransforms.Length; i++)
{
GUILayout.BeginHorizontal();
GUILayout.Label(_childTransforms[i].name);
_childValues[i] = (ExampleEnum)EditorGUILayout.EnumPopup(_childValues[i]);
GUILayout.EndHorizontal();
}
}
}
}