Created
May 16, 2025 09:47
-
-
Save shane-harper/3014f80d52fa74d6f9728bbd85f21a22 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class Example : MonoBehaviour | |
{ | |
[SerializeReference] public List<IExampleInterface> List = new(); | |
} | |
#if UNITY_EDITOR | |
[UnityEditor.CustomEditor(typeof(Example), true)] | |
public class ExampleEditor : UnityEditor.Editor | |
{ | |
private UnityEditorInternal.ReorderableList _list; | |
private void OnEnable() | |
{ | |
_list = SerializableTypeList.Create<IExampleInterface>(serializedObject, "List", ((Example)target).List); | |
} | |
public override void OnInspectorGUI() | |
{ | |
serializedObject.Update(); | |
_list.DoLayoutList(); | |
serializedObject.ApplyModifiedProperties(); | |
} | |
} | |
#endif | |
public interface IExampleInterface | |
{ | |
} | |
[Serializable] | |
public struct ExampleTypeOne : IExampleInterface | |
{ | |
public string Text; | |
} | |
[Serializable] | |
public struct ExampleTypeTwo : IExampleInterface | |
{ | |
public int Number; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEditor; | |
using UnityEditorInternal; | |
using UnityEngine; | |
/// <summary> | |
/// Utility class for creating ReorderableLists that can be used to easily manage lists containing different types within a component | |
/// </summary> | |
public static class SerializableTypeList | |
{ | |
/// <summary> | |
/// Create a new ReorderableList | |
/// </summary> | |
/// <param name="serializedObject">Serialized object containing the list</param> | |
/// <param name="propertyName">Name of the list property</param> | |
/// <param name="elements">Access to the list for adding new entries</param> | |
/// <typeparam name="T">Required type/interface</typeparam> | |
public static ReorderableList Create<T>(SerializedObject serializedObject, string propertyName, IList elements) | |
{ | |
var serializedProperty = serializedObject.FindProperty(propertyName); | |
return new ReorderableList(serializedObject, serializedProperty, true, true, true, true) | |
{ | |
headerHeight = 0, | |
drawElementCallback = (rect, index, _, _) => | |
{ | |
// Indent rect so that foldout arrow doesn't overlap the drag handle | |
const float indentWidth = 10f; | |
rect.x += indentWidth; | |
rect.width -= indentWidth; | |
// Get display name and draw property | |
var element = serializedProperty.GetArrayElementAtIndex(index); | |
var typeName = element.managedReferenceFullTypename; | |
var split = typeName.Split('.', ' '); | |
var label = new GUIContent(ObjectNames.NicifyVariableName(split[split.Length - 1])); | |
EditorGUI.PropertyField(rect, element, label, true); | |
}, | |
elementHeightCallback = index => | |
{ | |
// Use height of element | |
var element = serializedProperty.GetArrayElementAtIndex(index); | |
return EditorGUI.GetPropertyHeight(element, true); | |
}, | |
onAddDropdownCallback = (_, _) => | |
{ | |
// Display a context menu containing each type | |
var types = GetSerializableTypes<T>(); | |
var menu = new GenericMenu(); | |
foreach (var type in types) | |
{ | |
var displayName = ObjectNames.NicifyVariableName(type.Name); | |
menu.AddItem(new GUIContent(displayName), false, () => | |
{ | |
// Attempt to add new instance of selected type | |
try | |
{ | |
var instance = (T)Activator.CreateInstance(type); | |
elements.Add(instance); | |
serializedObject.ApplyModifiedProperties(); | |
EditorUtility.SetDirty(serializedObject.targetObject); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogErrorFormat(serializedObject.targetObject, "Failed to add instance of type '{0}'. {1}", type, ex.Message); | |
} | |
}); | |
} | |
menu.ShowAsContext(); | |
} | |
}; | |
} | |
private static IEnumerable<Type> GetSerializableTypes<T>() | |
{ | |
return AppDomain.CurrentDomain.GetAssemblies() | |
.SelectMany(s => s.GetTypes()) | |
.Where(p => p.GetCustomAttributes(typeof(SerializableAttribute), true).Length > 0) | |
.Where(p => !p.IsAbstract && typeof(T).IsAssignableFrom(p)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment