update component templated drawing

This commit is contained in:
Mikhail 2026-03-30 20:11:16 +08:00
parent 35642db710
commit 0c2e5c41bc
9 changed files with 279 additions and 116 deletions

View File

@ -1005,48 +1005,6 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
#endregion #endregion
#region PredicateTypesKey
private readonly struct PredicateTypesKey : IEquatable<PredicateTypesKey>
{
public readonly Type[] types;
public readonly Type[] withoutTypes;
public PredicateTypesKey(Type[] types, Type[] withoutTypes)
{
this.types = types;
this.withoutTypes = withoutTypes;
}
public bool Equals(PredicateTypesKey other)
{
if (types.Length != other.types.Length) { return false; }
if (withoutTypes.Length != other.withoutTypes.Length) { return false; }
for (int i = 0; i < types.Length; i++)
{
if (types[i] != other.types[i])
{
return false;
}
}
for (int i = 0; i < withoutTypes.Length; i++)
{
if (withoutTypes[i] != other.withoutTypes[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is PredicateTypesKey key && Equals(key);
}
public override int GetHashCode()
{
return HashCode.Combine(types);
}
public static implicit operator PredicateTypesKey((Type[], Type[]) types) { return new PredicateTypesKey(types.Item1, types.Item2); }
}
#endregion
#region ReferenceDropDown #region ReferenceDropDown
private class ReferenceDropDown : AdvancedDropdown private class ReferenceDropDown : AdvancedDropdown
{ {

View File

@ -86,32 +86,37 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
} }
} }
internal class ComponentTemplatesDropDown : MetaObjectsDropDown<IComponentTemplate> internal class ComponentTemplatesDropDown : MetaObjectsDropDown<ComponentTemplateTypeCache>
{ {
public ComponentTemplatesDropDown() private ComponentTemplatesDropDown() { }
{
IEnumerable<(IComponentTemplate template, ITypeMeta meta)> itemMetaPairs = ComponentTemplateTypeCache.Dummies.ToArray().Select(dummy =>
{
ITypeMeta meta;
if (dummy is ITypeMeta withMetaOverride)
{
meta = withMetaOverride;
}
else
{
meta = dummy.Type.GetMeta();
}
return (dummy, meta);
});
//TODO оптимизировать или вырезать
itemMetaPairs = itemMetaPairs.OrderBy(o => o.meta.Group.Name);
Setup(itemMetaPairs);
}
private bool _isCheckUnique; private bool _isCheckUnique;
private SerializedProperty _arrayProperty; private SerializedProperty _arrayProperty;
private SerializedProperty _fieldProperty; private SerializedProperty _fieldProperty;
public static Dictionary<PredicateTypesKey, ComponentTemplatesDropDown> _dropDownsCache = new Dictionary<PredicateTypesKey, ComponentTemplatesDropDown>(32);
public static ComponentTemplatesDropDown Get(PredicateTypesKey key)
{
if(_dropDownsCache.TryGetValue(key, out var result) == false)
{
result = new ComponentTemplatesDropDown();
IEnumerable<(ComponentTemplateTypeCache template, ITypeMeta meta)> itemMetaPairs = ComponentTemplateTypeCache.All.ToArray()
.Where(o =>
{
return key.Check(o.Type);
})
.Select(o =>
{
return (o, o.Meta);
});
//TODO оптимизировать или вырезать
itemMetaPairs = itemMetaPairs.OrderBy(o => o.meta.Group.Name);
result.Setup(itemMetaPairs);
_dropDownsCache[key] = result;
}
return result;
}
public void OpenForArray(Rect position, SerializedProperty arrayProperty, bool isCheckUnique) public void OpenForArray(Rect position, SerializedProperty arrayProperty, bool isCheckUnique)
{ {
_isCheckUnique = isCheckUnique; _isCheckUnique = isCheckUnique;
@ -139,14 +144,14 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
Type componentType = item.Obj.GetType(); Type componentType = item.Obj.GetType();
IComponentTemplate cmptmp = item.Obj; var data = item.Obj;
if (_arrayProperty != null && cmptmp != null) if (_arrayProperty != null && data != null)
{ {
int index = _arrayProperty.arraySize; int index = _arrayProperty.arraySize;
if (_isCheckUnique) if (_isCheckUnique)
{ {
if (cmptmp.IsUnique) if (data.IsUnique)
{ {
for (int i = 0, iMax = _arrayProperty.arraySize; i < iMax; i++) for (int i = 0, iMax = _arrayProperty.arraySize; i < iMax; i++)
{ {
@ -163,7 +168,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
if (_fieldProperty != null) if (_fieldProperty != null)
{ {
_fieldProperty.managedReferenceValue = cmptmp.Clone_Reflection(); _fieldProperty.managedReferenceValue = data.CreateInstance();
_fieldProperty.serializedObject.ApplyModifiedProperties(); _fieldProperty.serializedObject.ApplyModifiedProperties();
} }
} }
@ -208,7 +213,8 @@ namespace DCFApixels.DragonECS.Unity.Editors
{ {
private string _name; private string _name;
private bool _isContainsNull; private bool _isContainsNull;
private IEnumerable<(T, ITypeMeta)> _itemMetaPairs; public IEnumerable<(T, ITypeMeta)> _itemMetaPairs;
public MetaObjectsDropDown() : base(new AdvancedDropdownState()) public MetaObjectsDropDown() : base(new AdvancedDropdownState())
{ {
minimumSize = new Vector2(220f, EditorGUIUtility.singleLineHeight * 20); minimumSize = new Vector2(220f, EditorGUIUtility.singleLineHeight * 20);

View File

@ -142,7 +142,8 @@ namespace DCFApixels.DragonECS.Unity.Editors
entityEditorBlockDrawers.Add(drawer); entityEditorBlockDrawers.Add(drawer);
} }
if (type.IsUnityObject() == false && type.GetConstructor(Type.EmptyTypes) != null) if (type.IsUnityObject() == false &&
(type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null))
{ {
serializableTypes.Add(type); serializableTypes.Add(type);
if (hasMetaID) if (hasMetaID)

View File

@ -0,0 +1,72 @@
using System;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Internal
{
internal readonly struct PredicateTypesKey : IEquatable<PredicateTypesKey>
{
public readonly Type[] Types;
public readonly Type[] WithoutTypes;
public PredicateTypesKey(Type[] types) : this(types, Type.EmptyTypes) { }
public PredicateTypesKey(Type[] types, Type[] withoutTypes)
{
Types = types;
WithoutTypes = withoutTypes;
}
public bool Check(Type type)
{
bool isAssignable = false;
foreach (Type predicateTypes in Types)
{
if (predicateTypes.IsAssignableFrom(type))
{
isAssignable = true;
break;
}
}
if (isAssignable)
{
foreach (Type withoutType in WithoutTypes)
{
if (withoutType.IsAssignableFrom(type))
{
isAssignable = false;
break;
}
}
}
return isAssignable;
}
public bool Equals(PredicateTypesKey other)
{
if (Types.Length != other.Types.Length) { return false; }
if (WithoutTypes.Length != other.WithoutTypes.Length) { return false; }
for (int i = 0; i < Types.Length; i++)
{
if (Types[i] != other.Types[i])
{
return false;
}
}
for (int i = 0; i < WithoutTypes.Length; i++)
{
if (WithoutTypes[i] != other.WithoutTypes[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is PredicateTypesKey key && Equals(key);
}
public override int GetHashCode()
{
return HashCode.Combine(Types, WithoutTypes);
}
public static implicit operator PredicateTypesKey((Type[], Type[]) types) { return new PredicateTypesKey(types.Item1, types.Item2); }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ddc48a3a4dbc1c40a37a48e5adb86d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -12,13 +12,13 @@ namespace DCFApixels.DragonECS
public struct ComponentTemplateProperty : IEquatable<ComponentTemplateProperty> public struct ComponentTemplateProperty : IEquatable<ComponentTemplateProperty>
{ {
[SerializeReference] [SerializeReference]
private IComponentTemplate _template; private ITemplateNode _template;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComponentTemplateProperty(IComponentTemplate template) public ComponentTemplateProperty(ITemplateNode template)
{ {
_template = template; _template = template;
} }
public IComponentTemplate Template public ITemplateNode Template
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _template; } get { return _template; }
@ -28,23 +28,41 @@ namespace DCFApixels.DragonECS
public Type Type public Type Type
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _template.Type; } get { return _template is IComponentTemplate tml ? tml.Type : _template.GetType(); }
} }
public bool IsNull public bool IsNull
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _template == null; } get { return _template == null; }
} }
private IComponentTemplate Tmpl
{
get { return _template as IComponentTemplate; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(short worldID, int entityID) { _template.Apply(worldID, entityID); } public void Apply(short worldID, int entityID) { _template.Apply(worldID, entityID); }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public object GetRaw() { return _template.GetRaw(); } public void OnGizmos(Transform transform, IComponentTemplate.GizmosMode mode) { Tmpl?.OnGizmos(transform, mode); }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnGizmos(Transform transform, IComponentTemplate.GizmosMode mode) { _template.OnGizmos(transform, mode); } public void OnValidate(UnityEngine.Object obj) { Tmpl?.OnValidate(obj); }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnValidate(UnityEngine.Object obj) { _template.OnValidate(obj); } public object GetRaw()
{
if(_template is IComponentTemplate tmpl)
{
return tmpl.GetRaw();
}
return _template;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetRaw(object raw) { _template.SetRaw(raw); } public void SetRaw(object raw)
{
if (_template is IComponentTemplate tmpl)
{
tmpl.SetRaw(raw);
}
_template = (IComponentTemplate)raw;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ComponentTemplateProperty other) { return _template == other._template; } public bool Equals(ComponentTemplateProperty other) { return _template == other._template; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -67,15 +85,10 @@ namespace DCFApixels.DragonECS
public sealed class ComponentTemplateReferenceAttribute : PropertyAttribute, IReferenceButtonAttribute public sealed class ComponentTemplateReferenceAttribute : PropertyAttribute, IReferenceButtonAttribute
{ {
public readonly Type[] PredicateTypes; public Type[] PredicateTypes;
public readonly bool IsHideButtonIfNotNull;
Type[] IReferenceButtonAttribute.PredicateTypes { get { return PredicateTypes; } } Type[] IReferenceButtonAttribute.PredicateTypes { get { return PredicateTypes; } }
bool IReferenceButtonAttribute.IsHideButtonIfNotNull { get { return IsHideButtonIfNotNull; } } bool IReferenceButtonAttribute.IsHideButtonIfNotNull { get { return true; } }
public ComponentTemplateReferenceAttribute() public ComponentTemplateReferenceAttribute() { }
{
PredicateTypes = new Type[] { typeof(IComponentTemplate) };
IsHideButtonIfNotNull = true;
}
} }
public sealed class ComponentTemplateAttribute : PropertyAttribute { } public sealed class ComponentTemplateAttribute : PropertyAttribute { }
} }

View File

@ -9,7 +9,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
[CustomPropertyDrawer(typeof(ComponentTemplateProperty), true)] [CustomPropertyDrawer(typeof(ComponentTemplateProperty), true)]
internal class ComponentTemplatePropertyDrawer : ExtendedPropertyDrawer internal class ComponentTemplatePropertyDrawer : ExtendedPropertyDrawer
{ {
private ComponentTemplateReferenceDrawer _drawer = new ComponentTemplateReferenceDrawer(); private ComponentTemplateReferenceDrawer _drawer = new ComponentTemplateReferenceDrawer(new PredicateTypesKey(new Type[] { typeof(ITemplateNode) }));
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{ {
property.Next(true); property.Next(true);
@ -30,17 +30,48 @@ namespace DCFApixels.DragonECS.Unity.Editors
internal class ComponentTemplateReferenceDrawer : ExtendedPropertyDrawer<ComponentTemplateReferenceAttribute> internal class ComponentTemplateReferenceDrawer : ExtendedPropertyDrawer<ComponentTemplateReferenceAttribute>
{ {
private const float DamagedComponentHeight = 18f * 2f; private const float DamagedComponentHeight = 18f * 2f;
private static ComponentTemplatesDropDown _componentDropDown; private ComponentTemplatesDropDown _componentDropDown;
private PredicateTypesKey? _predicateOverride;
#region Properties #region Properties
private float Padding => Spacing; private float Padding => Spacing;
protected override bool IsStaticInit => _componentDropDown != null; protected override bool IsInit => _componentDropDown != null;
#endregion #endregion
#region Init public ComponentTemplateReferenceDrawer() { }
protected override void OnStaticInit() public ComponentTemplateReferenceDrawer(PredicateTypesKey key)
{ {
_componentDropDown = new ComponentTemplatesDropDown(); _predicateOverride = key;
}
#region Init
protected override void OnInit()
{
PredicateTypesKey key;
if(_predicateOverride == null)
{
Type[] withOutTypes = Type.EmptyTypes;
if (fieldInfo != null)
{
withOutTypes = fieldInfo.TryGetAttribute(out ReferenceButtonWithOutAttribute a) ? a.PredicateTypes : Array.Empty<Type>();
}
if (Attribute != null)
{
var types = Attribute.PredicateTypes;
if(types == null || types.Length == 0)
{
types = new Type[] { typeof(ITemplateNode) };
}
key = new PredicateTypesKey(types, withOutTypes);
}
else
{
key = new PredicateTypesKey(new Type[] { typeof(object) }, withOutTypes);
}
_predicateOverride = key;
}
_componentDropDown = ComponentTemplatesDropDown.Get(_predicateOverride.Value);
_componentDropDown.OnSelected += SelectComponent; _componentDropDown.OnSelected += SelectComponent;
} }
@ -49,11 +80,11 @@ namespace DCFApixels.DragonECS.Unity.Editors
private static void SelectComponent(ComponentTemplatesDropDown.Item item) private static void SelectComponent(ComponentTemplatesDropDown.Item item)
{ {
//EcsGUI.Changed = true; //EcsGUI.Changed = true;
currentProperty.managedReferenceValue = item.Obj.Clone_Reflection(); object inst = item.Obj.CreateInstance();
currentProperty.managedReferenceValue = inst;
currentProperty.isExpanded = false; currentProperty.isExpanded = false;
currentProperty.serializedObject.ApplyModifiedProperties(); currentProperty.serializedObject.ApplyModifiedProperties();
} }
#endregion #endregion
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
@ -134,7 +165,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
return; return;
} }
meta = template is ITypeMeta metaOverride ? metaOverride : template.Type.GetMeta(); meta = template is ITypeMeta metaOverride ? metaOverride : _predicateOverride.Value.Types[0].GetMeta();
} }
else else
{ {

View File

@ -1,6 +1,7 @@
#if UNITY_EDITOR #if UNITY_EDITOR
using DCFApixels.DragonECS.Unity.Internal; using DCFApixels.DragonECS.Unity.Internal;
using DCFApixels.DragonECS.Unity.RefRepairer.Editors; using DCFApixels.DragonECS.Unity.RefRepairer.Editors;
using System;
using UnityEditor; using UnityEditor;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
@ -15,13 +16,15 @@ namespace DCFApixels.DragonECS.Unity.Editors
private ReorderableList _reorderableComponentsList; private ReorderableList _reorderableComponentsList;
private int _reorderableComponentsListLastCount; private int _reorderableComponentsListLastCount;
private static readonly Type[] _predicateTypes = new Type[] { typeof(ITemplateNode) };
protected abstract bool IsSO { get; } protected abstract bool IsSO { get; }
#region Init #region Init
protected override bool IsInit { get { return _componentDropDown != null; } } protected override bool IsInit { get { return _componentDropDown != null; } }
protected override void OnInit() protected override void OnInit()
{ {
_componentDropDown = new ComponentTemplatesDropDown(); _componentDropDown = ComponentTemplatesDropDown.Get(new PredicateTypesKey(_predicateTypes, Type.EmptyTypes));
_componentsProp = serializedObject.FindProperty("_componentTemplates"); _componentsProp = serializedObject.FindProperty("_componentTemplates");

View File

@ -1,10 +1,9 @@
#if DISABLE_DEBUG #if DISABLE_DEBUG
#undef DEBUG #undef DEBUG
#endif #endif
using DCFApixels.DragonECS.Unity.Internal;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEngine; using UnityEngine;
@ -142,35 +141,104 @@ namespace DCFApixels.DragonECS
#if UNITY_EDITOR #if UNITY_EDITOR
namespace DCFApixels.DragonECS.Unity.Editors namespace DCFApixels.DragonECS.Unity.Editors
{ {
internal static class ComponentTemplateTypeCache internal class ComponentTemplateTypeCache
{ {
private static Type[] _types; private static ComponentTemplateTypeCache[] _all;
private static IComponentTemplate[] _dummies; internal static ReadOnlySpan<ComponentTemplateTypeCache> All
internal static ReadOnlySpan<Type> Types
{ {
get { return _types; } get { return _all; }
}
internal static ReadOnlySpan<IComponentTemplate> Dummies
{
get { return _dummies; }
} }
static ComponentTemplateTypeCache() static ComponentTemplateTypeCache()
{ {
Type interfaceType = typeof(IComponentTemplate); List<ComponentTemplateTypeCache> list = new List<ComponentTemplateTypeCache>(256);
foreach (var type in UnityEditorUtility._serializableTypes)
_types = UnityEditorUtility._serializableTypes.Where(type => interfaceType.IsAssignableFrom(type)).ToArray();
//foreach (var type in _types)
//{
// EcsDebugUtility.GetTypeMeta(type);
//}
_dummies = new IComponentTemplate[_types.Length];
for (int i = 0; i < _types.Length; i++)
{ {
_dummies[i] = (IComponentTemplate)Activator.CreateInstance(_types[i]); //Debug.Log(type.Name);
if (typeof(ITemplateNode).IsAssignableFrom(type) && (typeof(IComponentTemplate).IsAssignableFrom(type) || typeof(IEcsComponentMember).IsAssignableFrom(type)))
{
ComponentTemplateTypeCache element = new ComponentTemplateTypeCache(type);
list.Add(element);
} }
} }
_all = list.ToArray();
}
public readonly Type Type;
public readonly Type ComponentType;
public readonly bool IsUnique;
private ITypeMeta _meta;
public ITypeMeta Meta
{
get
{
if (_meta == null)
{
_meta = ComponentType.GetMeta();
}
return _meta;
}
}
private bool _defaultValueTypeInit = false;
private object _defaultValueDummy;
public object DefaultValue
{
get
{
if (_defaultValueTypeInit == false)
{
if (Type.IsValueType)
{
FieldInfo field;
field = Type.GetField("Default", BindingFlags.Static | BindingFlags.Public);
if (field != null && field.FieldType == Type)
{
_defaultValueDummy = field.GetValue(null);
}
if(_defaultValueDummy == null)
{
field = Type.GetField("Empty", BindingFlags.Static | BindingFlags.Public);
if (field != null && field.FieldType == Type)
{
_defaultValueDummy = field.GetValue(null);
}
}
}
_defaultValueTypeInit = true;
}
return _defaultValueDummy;
}
}
public ComponentTemplateTypeCache(Type type)
{
Type = type;
IsUnique = false;
if (typeof(IComponentTemplate).IsAssignableFrom(type))
{
var ct = (IComponentTemplate)Activator.CreateInstance(type);
IsUnique = ct.IsUnique;
ComponentType = ct.Type;
if (ct is ITypeMeta metaOverride)
{
_meta = metaOverride;
}
}
else
{
ComponentType = Type;
}
}
public object CreateInstance()
{
if(DefaultValue != null)
{
return DefaultValue.Clone_Reflection();
}
return Activator.CreateInstance(Type);
}
} }
} }
#endif #endif