Unity Udexreal开发插件包
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
10 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace UDE_HAND_INTERACTION
{
public class EditorBase
{
private SerializedObject _serializedObject;
private HashSet<string> _hiddenProperties = new HashSet<string>();
private HashSet<string> _skipProperties = new HashSet<string>();
private Dictionary<string, Action<SerializedProperty>> _customDrawers =
new Dictionary<string, Action<SerializedProperty>>();
private Dictionary<string, Section> _sections =
new Dictionary<string, Section>();
private List<string> _orderedSections = new List<string>();
public class Section
{
public string title;
public bool isFoldout;
public bool foldout;
public List<string> properties = new List<string>();
public bool HasSomethingToDraw()
{
return properties != null && properties.Count > 0;
}
}
public EditorBase(SerializedObject serializedObject)
{
_serializedObject = serializedObject;
}
#region Sections
private Section GetOrCreateSection(string sectionName)
{
if (_sections.TryGetValue(sectionName, out Section existingSection))
{
return existingSection;
}
Section section = CreateSection(sectionName, false);
return section;
}
public Section CreateSection(string sectionName, bool isFoldout)
{
if (_sections.TryGetValue(sectionName, out Section existingSection))
{
Debug.LogError($"Section {sectionName} already exists");
return null;
}
Section section = new Section() { title = sectionName, isFoldout = isFoldout };
_sections.Add(sectionName, section);
_orderedSections.Add(sectionName);
return section;
}
public void CreateSections(Dictionary<string, string[]> sections, bool isFoldout)
{
foreach (var sectionData in sections)
{
CreateSection(sectionData.Key, isFoldout);
AddToSection(sectionData.Key, sectionData.Value);
}
}
public void AddToSection(string sectionName, params string[] properties)
{
if (properties.Length == 0
|| !ValidateProperties(properties))
{
return;
}
Section section = GetOrCreateSection(sectionName);
foreach (var property in properties)
{
section.properties.Add(property);
_skipProperties.Add(property);
}
}
#endregion
#region IMPLEMENTATION
public bool IsInSection(string property)
{
return _skipProperties.Contains(property);
}
public bool IsHidden(string property)
{
return _hiddenProperties.Contains(property);
}
public void DrawFullInspector()
{
SerializedProperty it = _serializedObject.GetIterator();
it.NextVisible(enterChildren: true);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(it);
EditorGUI.EndDisabledGroup();
EditorGUI.BeginChangeCheck();
while (it.NextVisible(enterChildren: false))
{
if (IsInSection(it.name))
{
continue;
}
DrawProperty(it);
}
foreach (string sectionKey in _orderedSections)
{
DrawSection(sectionKey);
}
_serializedObject.ApplyModifiedProperties();
}
public void DrawSection(string sectionName)
{
Section section = _sections[sectionName];
DrawSection(section);
}
private void DrawSection(Section section)
{
if (!section.HasSomethingToDraw())
{
return;
}
if (section.isFoldout)
{
section.foldout = EditorGUILayout.Foldout(section.foldout, section.title);
if (!section.foldout)
{
return;
}
EditorGUI.indentLevel++;
}
foreach (string prop in section.properties)
{
DrawProperty(_serializedObject.FindProperty(prop));
}
if (section.isFoldout)
{
EditorGUI.indentLevel--;
}
}
private void DrawProperty(SerializedProperty property)
{
try
{
if (IsHidden(property.name))
{
return;
}
Action<SerializedProperty> customDrawer;
if (_customDrawers.TryGetValue(property.name, out customDrawer))
{
customDrawer(property);
}
else
{
EditorGUILayout.PropertyField(property, includeChildren: true);
}
}
catch (Exception e)
{
Debug.LogError($"Error drawing property {e.Message}");
}
}
private bool ValidateProperties(params string[] properties)
{
foreach (var property in properties)
{
if (_serializedObject.FindProperty(property) == null)
{
Debug.LogWarning(
$"Could not find property {property}, maybe it was deleted or renamed?");
return false;
}
}
return true;
}
#endregion
}
public class OptionalAttribute : PropertyAttribute
{
[System.Flags]
public enum Flag
{
None = 0,
AutoGenerated = 1 << 0,
DontHide = 1 << 1
}
public Flag Flags { get; private set; } = Flag.None;
public OptionalAttribute() { }
public OptionalAttribute(Flag flags)
{
Flags = flags;
}
}
public class SectionAttribute : PropertyAttribute
{
public string SectionName { get; private set; } = string.Empty;
public SectionAttribute(string sectionName)
{
SectionName = sectionName;
}
}
public class EditorUtils : Editor
{
protected EditorBase _editorDrawer;
private const string OptionalSection = "Optionals";
protected virtual void OnEnable()
{
_editorDrawer = new EditorBase(serializedObject);
_editorDrawer.CreateSections(FindCustomSections(serializedObject), true);
_editorDrawer.CreateSection(OptionalSection, true);
_editorDrawer.AddToSection(OptionalSection, FindOptionals(serializedObject));
}
public override void OnInspectorGUI()
{
_editorDrawer.DrawFullInspector();
}
private static string[] FindOptionals(SerializedObject serializedObject)
{
List<AttributedProperty<OptionalAttribute>> props = new List<AttributedProperty<OptionalAttribute>>();
UnityEngine.Object obj = serializedObject.targetObject;
if (obj != null)
{
FindAttributedSerializedFields(obj.GetType(), props);
}
return props.Where(p => (p.attribute.Flags & OptionalAttribute.Flag.DontHide) == 0)
.Select(p => p.propertyName)
.ToArray();
}
private static Dictionary<string, string[]> FindCustomSections(SerializedObject serializedObject)
{
List<AttributedProperty<SectionAttribute>> props = new List<AttributedProperty<SectionAttribute>>();
UnityEngine.Object obj = serializedObject.targetObject;
if (obj != null)
{
FindAttributedSerializedFields(obj.GetType(), props);
}
Dictionary<string, string[]> sections = new Dictionary<string, string[]>();
var namedSections = props.GroupBy(p => p.attribute.SectionName);
foreach (var namedSection in namedSections)
{
string[] values = namedSection.Select(p => p.propertyName).ToArray();
sections.Add(namedSection.Key, values);
}
return sections;
}
private static void FindAttributedSerializedFields<TAttribute>(Type type,
List<AttributedProperty<TAttribute>> props)
where TAttribute : PropertyAttribute
{
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
foreach (FieldInfo field in fields)
{
TAttribute attribute = field.GetCustomAttribute<TAttribute>();
if (attribute == null)
{
continue;
}
if (field.GetCustomAttribute<SerializeField>() == null)
{
continue;
}
props.Add(new AttributedProperty<TAttribute>(field.Name, attribute));
}
if (typeof(Component).IsAssignableFrom(type.BaseType))
{
FindAttributedSerializedFields<TAttribute>(type.BaseType, props);
}
}
private struct AttributedProperty<TAttribute>
where TAttribute : PropertyAttribute
{
public string propertyName;
public TAttribute attribute;
public AttributedProperty(string propertyName, TAttribute attribute)
{
this.propertyName = propertyName;
this.attribute = attribute;
}
}
}
}