379 lines
12 KiB
C#
379 lines
12 KiB
C#
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<GameElement>
|
||
{
|
||
//物体名
|
||
public string elementName;
|
||
|
||
//标识 GUID
|
||
public Guid elementGuid;
|
||
|
||
//标签
|
||
public List<string> tags;
|
||
|
||
//父游戏物体
|
||
public GameElement parentElement;
|
||
|
||
//与游戏物体连接的Tab
|
||
public HierarchyTab connectedTab;
|
||
|
||
//子物体列表
|
||
public List<GameElement> childElementList = new List<GameElement>();
|
||
|
||
//次级模块
|
||
public List<SubmoduleBase> submoduleList = new List<SubmoduleBase>();
|
||
|
||
//存档类
|
||
public BaseElement_BM matchedBM { get; set; }
|
||
|
||
public virtual int HierarchyPriority => 0;
|
||
|
||
/// <summary>
|
||
/// 首次初始化
|
||
/// </summary>
|
||
/// <param name="name">物体名</param>
|
||
public virtual void Initialize(string name, Guid elementGuid, List<string> tags,
|
||
bool isFirstGenerated, GameElement parentElement)
|
||
{
|
||
this.elementName = name;
|
||
this.elementGuid = elementGuid;
|
||
this.tags = tags;
|
||
EditorManager.instance.beatmapContainer.gameElementList.Add(this);
|
||
submoduleList = new List<SubmoduleBase>();
|
||
|
||
if (isFirstGenerated)
|
||
{
|
||
SetDefaultSubmodules();
|
||
}
|
||
|
||
SetParent(parentElement);
|
||
EditorManager.instance.uiManager.hierarchy.GenerateTab(this, parentElement);
|
||
|
||
//GameManager.beatMapContainer.beatMapElementList.Add(this);
|
||
//serialNumber = totalSerialNumber++;
|
||
//SetTransformObserver();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置次级模块
|
||
/// </summary>
|
||
public virtual void SetDefaultSubmodules()
|
||
{
|
||
|
||
}
|
||
|
||
public virtual void SetEditorSubmodules()
|
||
{
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在所有物体生成完毕后,执行的初始化方法
|
||
/// </summary>
|
||
public virtual void AfterInitialize()
|
||
{
|
||
matchedBM?.AfterExecute();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置父物体
|
||
/// </summary>
|
||
/// <param name="parentElement">父物体</param>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用于生成存档
|
||
/// </summary>
|
||
public virtual void SaveBM()
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当物体被删除时执行的方法
|
||
/// </summary>
|
||
public virtual void OnDelete()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 删除物体,包括所有子物体
|
||
/// </summary>
|
||
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<EnableType> enableTypes;
|
||
|
||
/// <summary>
|
||
/// 扫描childElementList中的类型并加入enableTypes(去重,兼容性好)
|
||
/// </summary>
|
||
public void ScanAndAddEnableTypes()
|
||
{
|
||
// 初始化enableTypes列表
|
||
if (enableTypes == null)
|
||
enableTypes = new List<EnableType>();
|
||
|
||
// 记录已存在的类型,避免重复
|
||
HashSet<Type> existingTypes = new HashSet<Type>();
|
||
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();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取所有子GameElement
|
||
/// </summary>
|
||
/// <param name="includeThis">是否包括自身</param>
|
||
public List<GameElement> GetAllGameElementsFromThis(bool includeThis = true)
|
||
{
|
||
void GetAllChildrenRecursively(GameElement parent, List<GameElement> elements)
|
||
{
|
||
foreach (var child in parent.childElementList)
|
||
{
|
||
elements.Add(child);
|
||
GetAllChildrenRecursively(child, elements);
|
||
}
|
||
}
|
||
|
||
List<GameElement> gameElements = new List<GameElement> { this };
|
||
GetAllChildrenRecursively(this, gameElements);
|
||
|
||
if (!includeThis) gameElements.Remove(this);
|
||
|
||
return gameElements;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 根据enableTypes筛选子元素(只返回enable为true的类型对应的子元素)
|
||
/// </summary>
|
||
public List<GameElement> GetChildrenByTypes()
|
||
{
|
||
if (enableTypes == null || enableTypes.Count == 0)
|
||
return new List<GameElement>();
|
||
|
||
var enabledTypes = new HashSet<Type>();
|
||
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<Guid, GameElement_BM> identifier = new(); //存档类的标识符
|
||
|
||
[System.NonSerialized]
|
||
public GameElement matchedElement; //存档类对应的游戏物体
|
||
|
||
public string elementName;
|
||
public List<string> tags;
|
||
public Guid elementGuid;
|
||
|
||
public GameElement_BM()
|
||
{
|
||
|
||
}
|
||
|
||
public GameElement_BM(string elementName, Guid elementGuid, List<string> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 复制物体
|
||
/// </summary>
|
||
/// <param name="attached">父物体</param>
|
||
public abstract GameElement DuplicateBM(GameElement attached);
|
||
|
||
|
||
public static List<BaseElement_BM> GetAllAttachedBaseElements(GameElement_BM gameElement, List<BaseElement_BM> clip)
|
||
{
|
||
Guid elementGuid = gameElement.elementGuid;
|
||
List<BaseElement_BM> result = new List<BaseElement_BM>();
|
||
foreach (BaseElement_BM element in clip)
|
||
{
|
||
if (element.attachedElementGuid == elementGuid)
|
||
{
|
||
result.Add(element);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
}
|
||
} |