整合SLSUtilities

This commit is contained in:
SoulliesOfficial
2026-01-17 11:35:49 -05:00
parent d94241f36c
commit 7ee2894a63
1338 changed files with 3051541 additions and 507034 deletions

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using Sirenix.OdinInspector;
using SLSFramework.General;
using UnityEngine;
@@ -24,45 +23,49 @@ namespace SLSUtilities.FunctionalAnimation
{
public static bool EditorWantsRepaint = false;
[ReadOnly, ShowInInspector]
public FuncAnimDataCollection parentCollection;
[Title("编辑器设置")]
[EnumToggleButtons]
[OnValueChanged("RequestRepaint")] // <-- 修改
[OnValueChanged("RequestRepaint")]
public TimeDisplayMode timeMode;
[Title("核心动画")]
[Required("必须指定一个动画片段")]
[OnValueChanged("RequestRepaint")] // <-- 修改
[OnValueChanged("RequestRepaint")]
public AnimationClip animationClip;
[Title("编辑时信息")]
public FuncAnimInfo animInfo = new FuncAnimInfo("AnimationName", "StateName", true, new List<string>(),
DisruptionType.NormalAction, 1.0f, 0, true, new Dictionary<string, List<string>>());
[Title("编辑时信息")] public FuncAnimInfo animInfo = new FuncAnimInfo("AnimationName", "StateName",
true, new List<string>(), DisruptionType.NormalAction, 1.0f, 0, true);
[Title("技能区间")]
[ListDrawerSettings(
ListElementLabelName = "@this.GetIntervalLabel()", // <-- 修改为调用方法
ListElementLabelName = "@this.GetIntervalLabel()",
AddCopiesLastElement = true)]
public List<FuncAnimInterval> intervals = new List<FuncAnimInterval>()
{
new FuncAnimInterval(IntervalType.Cancellable),
new FuncAnimInterval(IntervalType.Startup),
new FuncAnimInterval(IntervalType.ExternalDisruption),
new FuncAnimInterval(IntervalType.Active),
new FuncAnimInterval(IntervalType.Invincible),
new FuncAnimInterval(IntervalType.Preinput),
new FuncAnimInterval(IntervalType.ActionDisruption),
new FuncAnimInterval(IntervalType.MovementDisruption),
new FuncAnimInterval(IntervalType.RootMotion)
};
[Title("交互处理")]
[Tooltip("技能交互标签,用于标记技能与其他系统的交互关系,例如可以用来标记哪些技能可以被特定状态打断等")]
public Dictionary<string, List<string>> interactions = new Dictionary<string, List<string>>();
[Title("动画事件")]
public EventCollection eventCollection;
public EventCollection eventCollection = new EventCollection();
[Title("变量存储")]
public VariableCollection variableCollection;
public VariableCollection variableCollection = new VariableCollection();
// (新增) 核心方法:将父级引用传递给子级
// 这让子对象 (Event/Interval) 能够访问 animationClip 来计算帧
[OnInspectorInit("UpdateChildReferences")]
[OnInspectorGUI("UpdateChildReferences")] // 在绘制其他所有内容之前运行
private void UpdateChildReferences()
{
@@ -73,11 +76,32 @@ namespace SLSUtilities.FunctionalAnimation
if (interval != null) interval.parentData = this;
}
}
if (eventCollection.animEvents != null)
{
foreach (var evt in eventCollection.animEvents)
foreach (var animEvent in eventCollection.animEvents)
{
if (evt != null) evt.parentData = this;
if (animEvent is { payload: not null }) animEvent.payload.parentData = this;
}
foreach (var payload in eventCollection.startEvents)
{
if (payload != null) payload.parentData = this;
}
foreach (var payload in eventCollection.disruptionEvents)
{
if (payload != null) payload.parentData = this;
}
foreach (var payload in eventCollection.updateEvents)
{
if (payload != null) payload.parentData = this;
}
foreach (var payload in eventCollection.updateUntilEvents)
{
if (payload != null) payload.parentData = this;
}
}
}
@@ -286,8 +310,7 @@ namespace SLSUtilities.FunctionalAnimation
public class FuncAnimEvent
{
// (新增) 非序列化的父级引用
[NonSerialized]
public FuncAnimData parentData;
private FuncAnimData parentData => payload?.parentData;
// (新增) 辅助方法
private bool ShowSeconds() => parentData == null || parentData.timeMode == TimeDisplayMode.Seconds || parentData.animationClip == null;
@@ -341,9 +364,8 @@ namespace SLSUtilities.FunctionalAnimation
[PropertyOrder(2)]
public FuncAnimPayloadBase payload;
public FuncAnimEvent(FuncAnimData parentData, float triggerTime, FuncAnimPayloadBase payload, bool isEnd)
public FuncAnimEvent(float triggerTime, FuncAnimPayloadBase payload, bool isEnd)
{
this.parentData = parentData;
this.triggerTime = triggerTime;
this.isEnd = isEnd;
this.payload = payload;

View File

@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
namespace SLSUtilities.FunctionalAnimation
{
[CreateAssetMenu(fileName = "FuncAnimDataCollection", menuName = "Functional Animation/FuncAnimDataCollection", order = 0)]
public class FuncAnimDataCollection : SerializedScriptableObject
{
[Title("武器动作集")]
[ListDrawerSettings(ShowFoldout = true, CustomRemoveIndexFunction = "OnRemoveItem")]
[OnValueChanged("OnListChanged", true)]
public List<FuncAnimData> animDataList = new List<FuncAnimData>();
[ListDrawerSettings(ListElementLabelName = "functionName")]
public List<CustomFunction> preloadFunctions = new List<CustomFunction>();
/// <summary>
/// 当列表发生任何变化(添加、拖入、重新排序)时调用
/// </summary>
private void OnListChanged()
{
if (animDataList == null) return;
foreach (var data in animDataList)
{
if (data != null && data.parentCollection != this)
{
data.parentCollection = this;
#if UNITY_EDITOR
EditorUtility.SetDirty(data);
#endif
}
}
}
/// <summary>
/// 自定义删除逻辑:先解绑,再删除
/// </summary>
private void OnRemoveItem(int index)
{
if (index < 0 || index >= animDataList.Count) return;
FuncAnimData dataToRemove = animDataList[index];
if (dataToRemove != null)
{
// 如果它的父级确实是当前这个 Collection (防止误删别人的引用)
if (dataToRemove.parentCollection == this)
{
dataToRemove.parentCollection = null;
#if UNITY_EDITOR
EditorUtility.SetDirty(dataToRemove);
#endif
}
}
animDataList.RemoveAt(index);
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
}
[HideReferenceObjectPicker]
public partial class CustomFunction
{
[LabelText("Function Name")]
public string functionName;
[HideInInspector] // 平时隐藏,点开下面的 Foldout 再显示
public Dictionary<string, string> paramLabels = new Dictionary<string, string>();
[ShowInInspector]
[ShowIf("@paramCollectionType != null")]
[LabelText("Parameter Labels")]
[OnInspectorGUI("UpdateLabelConfig")] // 每次绘制时检查是否有新参数
private Dictionary<string, string> LabelConfigDrawer
{
get => paramLabels;
set => paramLabels = value;
}
[LabelText("Parameter Collection Type")]
[ValueDropdown("GetFilteredTypeList")]
public Type paramCollectionType;
public CustomFunction()
{
functionName = string.Empty;
paramCollectionType = typeof(PC_Void);
}
public string GetParamLabel(string fieldName, string defaultLabel)
{
if (paramLabels != null && paramLabels.TryGetValue(fieldName, out string label))
{
return label;
}
return defaultLabel;
}
private void UpdateLabelConfig()
{
if (paramCollectionType == null) return;
paramLabels ??= new Dictionary<string, string>();
FieldInfo[] fields = paramCollectionType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (var field in fields)
{
if (!paramLabels.ContainsKey(field.Name))
{
if(field.Name == "function") continue; // 跳过 function 字段
paramLabels.Add(field.Name, field.Name);
}
}
}
}
public partial class CustomFunction
{
public IEnumerable<Type> GetFilteredTypeList()
{
IEnumerable<Type> q = typeof(ParameterCollectionBase).Assembly.GetTypes()
.Where(x => !x.IsAbstract)
.Where(x => !x.IsGenericTypeDefinition)
.Where(x => typeof(ParameterCollectionBase).IsAssignableFrom(x));
return q;
}
[HideReferenceObjectPicker]
public abstract class ParameterCollectionBase
{
public CustomFunction function;
}
public class PC_Void : ParameterCollectionBase { }
public class PC_String : ParameterCollectionBase
{
[LabelText("@function.GetParamLabel(\"str0\", \"字符串参数\")")]
public string str0;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a78388ab9bdedb344aa863421d703861

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Sirenix.Serialization;
using UnityEngine;
namespace SLSUtilities.FunctionalAnimation
@@ -45,16 +46,12 @@ namespace SLSUtilities.FunctionalAnimation
[Tooltip("技能是否受速度倍率影响 (例如:角色的攻速属性)")]
public bool isAffectedBySpeedMultiplier;
[Tooltip("技能交互标签,用于标记技能与其他系统的交互关系,例如可以用来标记哪些技能可以被特定状态打断等")]
public Dictionary<string, List<string>> interactionTags;
[Tooltip("技能描述,通常是给开发者看的,便于回忆,可以不写,不会在游戏中显示")]
[TextArea]
public string description;
public FuncAnimInfo(string animationName, string stateName, bool useRootMotion, List<string> tags, DisruptionType disruptionType,
float overridePlaySpeed, int overrideStartFrame, bool isAffectedBySpeedMultiplier, Dictionary<string, List<string>> interactionTags)
float overridePlaySpeed, int overrideStartFrame, bool isAffectedBySpeedMultiplier)
{
this.animationName = animationName;
this.stateName = stateName;
@@ -64,7 +61,6 @@ namespace SLSUtilities.FunctionalAnimation
this.overridePlaySpeed = overridePlaySpeed;
this.overrideStartFrame = overrideStartFrame;
this.isAffectedBySpeedMultiplier = isAffectedBySpeedMultiplier;
this.interactionTags = interactionTags;
this.description = "";
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Cielonos.MainGame.Characters;
using Sirenix.OdinInspector;
using UnityEngine;
@@ -28,11 +27,15 @@ namespace SLSUtilities.FunctionalAnimation
{
[Tooltip("在时间轴上显示的事件名称 (如果为空,则显示类名)")]
[PropertyOrder(-1)] // 把它显示在最上面
[HideInInspector]
public string eventName;
[NonSerialized]
public FuncAnimData parentData;
[NonSerialized]
public RuntimeFuncAnim runtimeFuncAnim;
protected CharacterBase character => runtimeFuncAnim?.executor;
protected IFuncAnimExecutor character => runtimeFuncAnim?.executor;
protected FuncAnimPayloadBase()
{

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace SLSUtilities.FunctionalAnimation
{
public interface IFuncAnimExecutor
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c7e0ef4fb190a594db8961ab69270568

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using SLSFramework.General;
using UnityEngine;
@@ -12,7 +12,7 @@ namespace SLSUtilities.FunctionalAnimation
public string animationName => funcAnimData.animInfo.animationName;
public bool isLooping => funcAnimData.animationClip.isLooping;
public CharacterBase executor;
public IFuncAnimExecutor executor;
public float currentPlayTime;
public bool isDisrupted;
public int dataAnimEventIndex;
@@ -23,8 +23,11 @@ namespace SLSUtilities.FunctionalAnimation
public VariableCollection runtimeVariables;
public Dictionary<string, bool> updateUntilStatus;
private List<FuncAnimEvent> playedEndEvents;
[NonSerialized]
public object currentActionParams;
public RuntimeFuncAnim(FuncAnimData funcAnimData, CharacterBase character)
public RuntimeFuncAnim(FuncAnimData funcAnimData, IFuncAnimExecutor character)
{
this.funcAnimData = funcAnimData;
this.executor = character;
@@ -43,10 +46,24 @@ namespace SLSUtilities.FunctionalAnimation
dataEvents.updateUntilEvents.ForEach(payload => payload.runtimeFuncAnim = this);
}
public T GetParams<T>() where T : class
{
if (currentActionParams is T typedParams)
{
return typedParams;
}
Debug.LogWarning(currentActionParams != null
? $"[RuntimeFuncAnim] 参数类型不匹配。期望: {typeof(T).Name}, 实际: {currentActionParams.GetType().Name}"
: $"[RuntimeFuncAnim] 尝试获取 {typeof(T).Name},但当前参数为空。");
return null;
}
public void AddAnimEvent(float time, FuncAnimPayloadBase payload, bool isEnd = false)
{
payload.runtimeFuncAnim = this;
runtimeEvents.animEvents.Add(new FuncAnimEvent(funcAnimData, time, payload, isEnd));
runtimeEvents.animEvents.Add(new FuncAnimEvent(time, payload, isEnd));
}
public void UpdateAnimEvent()