diff --git a/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs b/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs index ce24694..d6e0f46 100644 --- a/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs +++ b/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs @@ -1,4 +1,7 @@ -using System; +using DCFApixels.DragonECS.Unity.Internal; +using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace DCFApixels.DragonECS @@ -6,8 +9,26 @@ namespace DCFApixels.DragonECS [CreateAssetMenu(fileName = nameof(EcsPipelineTemplateSO), menuName = EcsConsts.FRAMEWORK_NAME + "/" + nameof(EcsPipelineTemplateSO), order = 1)] public class EcsPipelineTemplateSO : ScriptableObject, IEcsModule { - private string[] _layers; + private static string[] _defaultLayers = new string[] + { + EcsConsts.PRE_BEGIN_LAYER, + EcsConsts.BEGIN_LAYER, + EcsConsts.BASIC_LAYER, + EcsConsts.END_LAYER, + EcsConsts.POST_END_LAYER, + }; + + [SerializeField] + private string[] _layers = _defaultLayers.ToArray(); + + [SerializeField] private SystemRecord[] _systems; + + [SerializeField] + [SerializeReference] + [ReferenceButton] + private IEcsModule[] _modules; + void IEcsModule.Import(EcsPipeline.Builder b) { b.Layers.MergeWith(_layers); @@ -53,9 +74,64 @@ namespace DCFApixels.DragonECS } } + public void Validate() + { + ValidateLayers(); + ValidateSystems(); + } + private void ValidateLayers() + { + Dictionary builtinLayerIndexes = new Dictionary(); + foreach (var item in _defaultLayers) + { + builtinLayerIndexes.Add(item, -1); + } + + List newLayers = _layers.Distinct().ToList(); + + for (int i = 0; i < newLayers.Count; i++) + { + var layer = newLayers[i]; + if (builtinLayerIndexes.ContainsKey(layer)) + { + builtinLayerIndexes[layer] = i; + } + } + + int lastNewLayersCount = newLayers.Count; + foreach (var pair in builtinLayerIndexes) + { + if(pair.Value < 0) + { + newLayers.Add(pair.Key); + } + } + for (int i = lastNewLayersCount; i < newLayers.Count; i++) + { + builtinLayerIndexes[newLayers[i]] = i; + } + + { + int i = 0; + foreach (var pair in builtinLayerIndexes.OrderBy(o => o.Value)) + { + newLayers[pair.Value] = _defaultLayers[i]; + i++; + } + } + + _layers = newLayers.ToArray(); + } + private void ValidateSystems() + { + + } + + [Serializable] public struct SystemRecord { [SerializeReference] + [ReferenceButton] public IEcsProcess system; public string layer; public int sortOrder; diff --git a/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs b/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs index d64bb4c..663a042 100644 --- a/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs +++ b/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs @@ -1,13 +1,33 @@ #if UNITY_EDITOR -using UnityEngine; using UnityEditor; +using UnityEngine; namespace DCFApixels.DragonECS.Unity.Editors { - //[CustomEditor(typeof(EcsPipelineTemplateSO))] - //internal class EcsPipelineTemplateSOEditor - //{ - // - //} + [CustomEditor(typeof(EcsPipelineTemplateSO))] + internal class EcsPipelineTemplateSOEditor : Editor + { + public override void OnInspectorGUI() + { + EditorGUI.BeginChangeCheck(); + base.OnInspectorGUI(); + if (EditorGUI.EndChangeCheck()) + { + Validate(); + } + if (GUILayout.Button("Validate")) + { + Validate(); + } + } + + private void Validate() + { + foreach (var target in targets) + { + ((EcsPipelineTemplateSO)target).Validate(); + } + } + } } #endif \ No newline at end of file diff --git a/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs new file mode 100644 index 0000000..8ac98c9 --- /dev/null +++ b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs @@ -0,0 +1,304 @@ +using DCFApixels.DragonECS; +using UnityEngine; + +namespace DCFApixels.DragonECS.Unity.Internal +{ + internal sealed class ReferenceButtonAttribute : PropertyAttribute { } +} + + +#if UNITY_EDITOR +namespace DCFApixels.DragonECS.Unity.Editors +{ + using DCFApixels.DragonECS.Unity.Internal; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEditor.IMGUI.Controls; + using UnityObject = UnityEngine.Object; + + [CustomPropertyDrawer(typeof(ReferenceButtonAttribute))] + internal sealed class ReferenceButtonAttributeDrawer : PropertyDrawer + { + private static bool _isInit; + + private static Type[] _serializableTypes; + private static Dictionary _predicatTypesMenus = new Dictionary(); + + private class ReferenceDropDown : AdvancedDropdown + { + public readonly Type PredicateType; + public ReferenceDropDown(Type predicateType) : base(new AdvancedDropdownState()) + { + PredicateType = predicateType; + + minimumSize = new Vector2(minimumSize.x, EditorGUIUtility.singleLineHeight * 30); + } + protected override AdvancedDropdownItem BuildRoot() + { + int increment = 0; + var root = new Item(null, "Select Type", increment++); + root.AddChild(new Item(null, "", increment++)); + + Dictionary dict = new Dictionary(); + + foreach (var type in _serializableTypes) + { + if (PredicateType.IsAssignableFrom(type)) + { + ITypeMeta meta = type.ToMeta(); + string name = meta.Name; + string description = meta.Description.Text; + MetaGroup group = meta.Group; + var splitedGroup = group.Splited; + + Item parent = root; + if(splitedGroup.Count > 0) + { + int i = 1; + foreach (var subgroup in splitedGroup) + { + Key key = new Key(group, i); + if (dict.TryGetValue(key, out Item item) == false) + { + item = new Item(null, subgroup, increment++); + parent.AddChild(item); + dict.Add(key, item); + } + parent = item; + i++; + } + } + + var leafItem = new Item(type, name, increment++); + parent.AddChild(leafItem); + } + } + return root; + } + + protected override void ItemSelected(AdvancedDropdownItem item) + { + base.ItemSelected(item); + OnSelected((Item)item); + } + + public event Action OnSelected = delegate { }; + + public class Item : AdvancedDropdownItem + { + public readonly Type Type; + public Item(Type type, string name, int id) : base(name) + { + Type = type; + this.id = id; + } + } + + #region Key + private readonly struct Key : IEquatable + { + public readonly MetaGroup Group; + public readonly int Length; + public Key(MetaGroup group, int length) + { + Group = group; + Length = length; + } + public bool Equals(Key other) + { + if (Length != other.Length) + { + return false; + } + IEnumerator splitedEnum = Group.Splited.GetEnumerator(); + IEnumerator splitedEnumOther = Group.Splited.GetEnumerator(); + for (int i = 0; i < Length; i++) + { + splitedEnum.MoveNext(); + splitedEnumOther.MoveNext(); + if (splitedEnum.Current != splitedEnumOther.Current) + { + return false; + } + } + return true; + } + public override bool Equals(object obj) + { + return obj is Key key && Equals(key); + } + public override int GetHashCode() + { + unchecked + { + int state = Length; + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + var x = Group.Splited.GetEnumerator(); + x.MoveNext(); + return x.Current.GetHashCode() ^ state; + }; + } + } + + //private readonly struct Key : IEquatable + //{ + // public readonly string FullName; + // public readonly int Length; + // public Key(string fullName, int length) + // { + // FullName = fullName; + // Length = length; + // } + // public bool Equals(Key other) + // { + // if (Length != other.Length) + // { + // return false; + // } + // for (int i = 0; i < Length; i++) + // { + // if (FullName[i] != other.FullName[i]) + // { + // return false; + // } + // } + // return true; + // } + // public override bool Equals(object obj) + // { + // return obj is Key key && Equals(key); + // } + // public override int GetHashCode() + // { + // unchecked + // { + // int state = Length; + // state ^= state << 13; + // state ^= state >> 17; + // state ^= state << 5; + // return FullName.GetHashCode() ^ state; + // }; + // } + //} + #endregion + + } + + #region Init + private static void Init() + { + if (_isInit) { return; } + + List types = new List(); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var targetTypes = assembly.GetTypes().Where(type => + (type.IsGenericType || type.IsAbstract || type.IsInterface) == false && + type.IsSubclassOf(typeof(UnityObject)) == false && + type.GetCustomAttribute() != null); + + types.AddRange(targetTypes); + } + _serializableTypes = types.ToArray(); + _isInit = true; + } + + private static ReferenceDropDown GetReferenceDropDown(Type predicatType) + { + Init(); + if (_predicatTypesMenus.TryGetValue(predicatType, out ReferenceDropDown menu) == false) + { + menu = new ReferenceDropDown(predicatType); + menu.OnSelected += SelectComponent; + _predicatTypesMenus.Add(predicatType, menu); + } + + return menu; + } + + [ThreadStatic] + private static SerializedProperty currentProperty; + private static void SelectComponent(ReferenceDropDown.Item item) + { + Type type = item.Type; + if (type == null) + { + currentProperty.managedReferenceValue = null; + } + else + { + currentProperty.managedReferenceValue = Activator.CreateInstance(type); + currentProperty.isExpanded = true; + } + + currentProperty.serializedObject.ApplyModifiedProperties(); + } + #endregion + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + if (property.managedReferenceValue != null) + { + return EditorGUI.GetPropertyHeight(property, label, true); + } + else + { + return EditorGUIUtility.singleLineHeight; + } + } + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + Rect selButtnoRect = position; + selButtnoRect.height = EditorGUIUtility.singleLineHeight; + DrawSelectionPopup(selButtnoRect, property, label); + + if (property.managedReferenceValue != null) + { + EditorGUI.PropertyField(position, property, label, true); + } + else + { + EditorGUI.BeginProperty(position, label, property); + EditorGUI.LabelField(position, label); + EditorGUI.EndProperty(); + } + } + + private void DrawSelectionPopup(Rect position, SerializedProperty property, GUIContent label) + { + Rect buttonRect = RectUtility.AddPadding(position, EditorGUIUtility.labelWidth, 0f, 0f, 0f); + object obj = property.hasMultipleDifferentValues ? null : property.managedReferenceValue; + if (GUI.Button(buttonRect, obj == null ? "Select..." : obj.GetMeta().Name, EditorStyles.layerMaskField)) + { + currentProperty = property; + GetReferenceDropDown(fieldInfo.FieldType).Show(buttonRect); + } + } + } +} +#endif + + +[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.SYSTEMS_GROUP)] +[System.Serializable] public class TestSystem0 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.SYSTEMS_GROUP)] +[System.Serializable] public class TestSystem1 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.SYSTEMS_GROUP)] +[System.Serializable] public class TestSystem2 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.SYSTEMS_GROUP)] +[System.Serializable] public class TestSystem3 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP)] +[System.Serializable] public class TestSystem4 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP)] +[System.Serializable] public class TestSystem7 : IEcsProcess { } +[MetaGroup(EcsConsts.PACK_GROUP)] +[System.Serializable] public class TestSystem8 : IEcsProcess { } +[System.Serializable] public class _TestSystemX : IEcsProcess { } +[System.Serializable] public class TestSystem9 : IEcsProcess { } +[System.Serializable] public class TestSystem5 : IEcsProcess { } +[System.Serializable] public class TestSystem6 : IEcsProcess { } \ No newline at end of file diff --git a/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs.meta b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs.meta new file mode 100644 index 0000000..3721240 --- /dev/null +++ b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 999d5f8fb88eafc44b8a88406eb3ec39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/EntityTemplate/Editor/ComponentTemplatePropertyDrawer.cs b/src/EntityTemplate/Editor/ComponentTemplatePropertyDrawer.cs index 80639e6..9d611fd 100644 --- a/src/EntityTemplate/Editor/ComponentTemplatePropertyDrawer.cs +++ b/src/EntityTemplate/Editor/ComponentTemplatePropertyDrawer.cs @@ -61,10 +61,6 @@ namespace DCFApixels.DragonECS.Unity.Editors name = group.Name + name; } - if (string.IsNullOrEmpty(description) == false) - { - name = $"{name} [i]"; - } _genericMenu.AddItem(new GUIContent(name, description), false, SelectComponent, dummy); }