using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Dreamteck.Splines; using Ichni.Editor; using Ichni.RhythmGame.Beatmap; using Sirenix.OdinInspector; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; using UnityEngine.UIElements; using Inspector = Ichni.Editor.Inspector; namespace Ichni.RhythmGame { public abstract partial class GameElement : SerializedMonoBehaviour, IBaseElement, IComparable { //物体名 public string elementName; //标识 GUID public Guid elementGuid; //标签 public List tags; //父游戏物体 public GameElement parentElement; //与游戏物体连接的Tab public HierarchyTab connectedTab; //子物体列表 public List childElementList = new List(); //次级模块 public List submoduleList = new List(); //存档类 public BaseElement_BM matchedBM { get; set; } public virtual int HierarchyPriority => 0; /// /// 首次初始化 /// /// 物体名 public virtual void Initialize(string name, Guid elementGuid, List tags, bool isFirstGenerated, GameElement parentElement) { this.elementName = name; this.elementGuid = elementGuid; this.tags = tags; EditorManager.instance.beatmapContainer.gameElementList.Add(this); submoduleList = new List(); if (isFirstGenerated) { SetDefaultSubmodules(); } SetParent(parentElement); EditorManager.instance.uiManager.hierarchy.GenerateTab(this, parentElement); //GameManager.beatMapContainer.beatMapElementList.Add(this); //serialNumber = totalSerialNumber++; //SetTransformObserver(); } /// /// 设置次级模块 /// public virtual void SetDefaultSubmodules() { } public virtual void SetEditorSubmodules() { } /// /// 在所有物体生成完毕后,执行的初始化方法 /// public virtual void AfterInitialize() { matchedBM?.AfterExecute(); } /// /// 设置父物体 /// /// 父物体 public void SetParent(GameElement parentElement) { if (parentElement != null) { parentElement.childElementList.Add(this); this.parentElement = parentElement; transform.SetParent(parentElement.transform); } } public int CompareTo(GameElement other) { return HierarchyPriority.CompareTo(other.HierarchyPriority); } } public abstract partial class GameElement //存档,删除,复制,粘贴 { public virtual void Refresh() { if (connectedTab != null) connectedTab.tabButtonText.text = this.elementName; } /// /// 用于生成存档 /// public virtual void SaveBM() { throw new NotImplementedException(); } /// /// 当物体被删除时执行的方法 /// public virtual void OnDelete() { } /// /// 删除物体,包括所有子物体 /// public virtual void Delete() { if (childElementList is { Count: > 0 }) { for (int i = 0; i < childElementList.Count; i++) { childElementList[i].Delete(); //删除子GameElement、 } } OnDelete(); //LogWindow.Log("Deleted element: " + elementName); EditorManager.instance.beatmapContainer.gameElementList.Remove(this); //从保存列表中剔除 if (connectedTab != null) { Destroy(connectedTab.gameObject); } Destroy(gameObject); //销毁 } } public abstract partial class GameElement//Editor 专 { public class EnableType : IBaseElement { public Type type; public bool enable; public BaseElement_BM matchedBM { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } }; public List enableTypes; /// /// 扫描childElementList中的类型并加入enableTypes(去重,兼容性好) /// public void ScanAndAddEnableTypes() { // 初始化enableTypes列表 if (enableTypes == null) enableTypes = new List(); // 记录已存在的类型,避免重复 HashSet existingTypes = new HashSet(); foreach (var et in enableTypes) { if (et != null && et.type != null) existingTypes.Add(et.type); } // 遍历所有子元素类型,若未添加则加入enableTypes foreach (var child in childElementList) { var childType = child.GetType(); if (!existingTypes.Contains(childType)) { enableTypes.Add(new EnableType { type = childType, enable = true }); existingTypes.Add(childType); } } } public virtual void SetUpInspector() //被点击时设置第一层Inspector { ScanAndAddEnableTypes(); IHaveInspection inspector = EditorManager.instance.uiManager.inspector; var container = inspector.GenerateContainer("Element Info"); //基础信息 var info = container.GenerateSubcontainer(3); var nameInputField = inspector.GenerateInputField(this, info, GetType().Name + "'s Name", nameof(elementName)); var guidText = inspector.GenerateParameterText(this, info, "Element GUID", nameof(elementGuid)); var tagsListButton = inspector.GenerateButton(this, info, "Tags List", () => { inspector.GenerateCompositeParameterWindow(this, "Tags List", nameof(tags)).SetAsStringList(); }); // 只用反射方式生成enableTypes的UI if (enableTypes != null && enableTypes.Count > 0) { var elcontainer = inspector.GenerateContainer("Enable Children Display"); var enableTypeContainer = elcontainer.GenerateSubcontainer(3); var type = enableTypes.GetType().GetGenericArguments()[0]; int elcount = 0; for (int idx = 0; idx < enableTypes.Count; idx++) { elcount++; if (elcount > 3) { elcount = 0; enableTypeContainer = elcontainer.GenerateSubcontainer(3); } var et = enableTypes[idx]; inspector.GenerateToggle( et, enableTypeContainer, et.type.Name, nameof(et.enable) // 传递字段名字符串 ); } } //次级模块 foreach (var submodule in submoduleList) { submodule.SetUpInspector(); } } /// /// 获取所有子GameElement /// /// 是否包括自身 public List GetAllGameElementsFromThis(bool includeThis = true) { void GetAllChildrenRecursively(GameElement parent, List elements) { foreach (var child in parent.childElementList) { elements.Add(child); GetAllChildrenRecursively(child, elements); } } List gameElements = new List { this }; GetAllChildrenRecursively(this, gameElements); if (!includeThis) gameElements.Remove(this); return gameElements; } /// /// 根据enableTypes筛选子元素(只返回enable为true的类型对应的子元素) /// public List GetChildrenByTypes() { if (enableTypes == null || enableTypes.Count == 0) return new List(); var enabledTypes = new HashSet(); foreach (var et in enableTypes) { if (et.enable) enabledTypes.Add(et.type); } // 问题1:只匹配类型本身,不能处理继承关系(如子类/接口) // 问题2:如果childElementList有null元素会抛异常 // 问题3:如果enableTypes有重复type,没影响但没必要 // 更健壮的写法如下(支持继承和接口,避免null): return childElementList.FindAll(child => child != null && enabledTypes.Any(t => t.IsAssignableFrom(child.GetType())) ); } } namespace Beatmap { [System.Serializable] public abstract class GameElement_BM : BaseElement_BM { [System.NonSerialized] public static Dictionary identifier = new(); //存档类的标识符 [System.NonSerialized] public GameElement matchedElement; //存档类对应的游戏物体 public string elementName; public List tags; public Guid elementGuid; public GameElement_BM() { } public GameElement_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement) { this.elementName = elementName; this.elementGuid = elementGuid; this.tags = tags; this.attachedElementGuid = attachedElement?.elementGuid ?? Guid.Empty; identifier.TryAdd(this.elementGuid, this); } public static GameElement_BM GetElementBM(Guid id) { if (identifier.TryGetValue(id, out GameElement_BM element_BM)) { return element_BM; } return null; } public static GameElement GetElement(Guid id) { if (identifier.TryGetValue(id, out GameElement_BM element_BM)) { return element_BM.matchedElement; } return null; } /// /// 复制物体 /// /// 父物体 public abstract GameElement DuplicateBM(GameElement attached); public static List GetAllAttachedBaseElements(GameElement_BM gameElement, List clip) { Guid elementGuid = gameElement.elementGuid; List result = new List(); foreach (BaseElement_BM element in clip) { if (element.attachedElementGuid == elementGuid) { result.Add(element); } } return result; } } } }