using System; using System.Collections.Generic; using System.Linq; using SLSUtilities.General; using UnityEngine; namespace SLSUtilities.FunctionalAnimation { public class RuntimeFuncAnim { public FuncAnimData funcAnimData; public string animationName => funcAnimData.animInfo.animationName; public bool isLooping => funcAnimData.animationClip.isLooping; public IFuncAnimExecutor executor; public float currentPlayTime; public float currentTotalPlayTime; public bool isDisrupted; public int dataAnimEventIndex; public int runtimeAnimEventIndex; public EventCollection dataEvents; public EventCollection runtimeEvents; public VariableCollection runtimeVariables; public Dictionary updateUntilStatus; private List playedEndEvents; [NonSerialized] public object currentActionParams; public RuntimeFuncAnim(FuncAnimData funcAnimData, IFuncAnimExecutor character) { this.funcAnimData = funcAnimData; this.executor = character; isDisrupted = false; dataAnimEventIndex = 0; dataEvents = funcAnimData.eventCollection.Clone(); runtimeEvents = new EventCollection(); updateUntilStatus = new Dictionary(); playedEndEvents = new List(); runtimeVariables = funcAnimData.variableCollection!= null ? funcAnimData.variableCollection.Clone() : new VariableCollection(); dataEvents.animEvents.ForEach(animEvt => animEvt.payload.runtimeFuncAnim = this); dataEvents.animEvents.Sort((evt1, evt2) => evt1.triggerTime.CompareTo(evt2.triggerTime)); //确保动画事件按触发时间排序 dataEvents.startEvents.ForEach(payload => payload.runtimeFuncAnim = this); dataEvents.disruptionEvents.ForEach(payload => payload.runtimeFuncAnim = this); dataEvents.updateEvents.ForEach(payload => payload.runtimeFuncAnim = this); dataEvents.updateUntilEvents.ForEach(payload => payload.runtimeFuncAnim = this); } public T GetParams() 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(time, payload, isEnd)); } public void UpdateAnimEvent() { while (dataAnimEventIndex < dataEvents.animEvents.Count) { FuncAnimEvent animEvent = dataEvents.animEvents[dataAnimEventIndex]; if (currentPlayTime >= animEvent.triggerTime) { animEvent.payload.Invoke(); dataAnimEventIndex++; if (animEvent.isEnd) { playedEndEvents.Add(animEvent); } } else { break; } } while (runtimeAnimEventIndex < runtimeEvents.animEvents.Count) { FuncAnimEvent animEvent = runtimeEvents.animEvents[runtimeAnimEventIndex]; if (currentPlayTime >= animEvent.triggerTime) { animEvent.payload.Invoke(); runtimeAnimEventIndex++; if (animEvent.isEnd) { playedEndEvents.Add(animEvent); } } else { break; } } } public void InvokeEndEvents() { dataEvents.endEvents.Exclude(playedEndEvents).Invoke(); runtimeEvents.endEvents.Exclude(playedEndEvents).Invoke(); } public RuntimeFuncAnim AddStartEvent(FuncAnimPayloadBase payload) { payload.runtimeFuncAnim = this; runtimeEvents.startEvents.Add(payload); return this; } public void InvokeStartEvents() { dataEvents.startEvents.Invoke(); runtimeEvents.startEvents.Invoke(); } public RuntimeFuncAnim AddDisruptionEvent(FuncAnimPayloadBase payload) { payload.runtimeFuncAnim = this; runtimeEvents.disruptionEvents.Add(payload); return this; } public void InvokeDisruptionEvents() { dataEvents.disruptionEvents.Invoke(); runtimeEvents.disruptionEvents.Invoke(); } public RuntimeFuncAnim AddUpdateEvent(FuncAnimPayloadBase payload) { payload.runtimeFuncAnim = this; runtimeEvents.updateEvents.Add(payload); return this; } public void InvokeUpdateEvents() { dataEvents.updateEvents.Invoke(); runtimeEvents.updateEvents.Invoke(); } public RuntimeFuncAnim AddUpdateUntilEvent(FuncAnimPayloadBase payload) { payload.runtimeFuncAnim = this; runtimeEvents.updateUntilEvents.Add(payload); updateUntilStatus.Add(payload.eventName, false); return this; } public void InvokeUpdateUntilEvents() { foreach (FuncAnimPayloadBase updateUntilEvent in dataEvents.updateUntilEvents) { if (!updateUntilStatus[updateUntilEvent.eventName]) { updateUntilStatus[updateUntilEvent.eventName] = updateUntilEvent.Invoke(); } } foreach (FuncAnimPayloadBase updateUntilEvent in runtimeEvents.updateUntilEvents) { if (!updateUntilStatus[updateUntilEvent.eventName]) { updateUntilStatus[updateUntilEvent.eventName] = updateUntilEvent.Invoke(); } } } public void ClearRuntimeEvents() { runtimeEvents.animEvents.Clear(); runtimeEvents.startEvents.Clear(); runtimeEvents.disruptionEvents.Clear(); runtimeEvents.updateEvents.Clear(); runtimeEvents.updateUntilEvents.Clear(); SetUpdateUntilEventsStatus(); } public void SetUpdateUntilEventsStatus(List> events = null) { updateUntilStatus.Clear(); if (events == null) { foreach (FuncAnimPayloadBase evt in dataEvents.updateUntilEvents) { updateUntilStatus.Add(evt.eventName, false); } foreach (FuncAnimPayloadBase evt in runtimeEvents.updateUntilEvents) { updateUntilStatus.Add(evt.eventName, false); } } else { foreach (FuncAnimPayloadBase evt in events) { updateUntilStatus.Add(evt.eventName, false); } } } public void ResetUpdateUntilEventsStatus() { List keys = updateUntilStatus.Keys.ToList(); foreach (string key in keys) { updateUntilStatus[key] = false; } } public bool HasIntervalType(IntervalType type) { if (funcAnimData == null || funcAnimData.intervals == null) { Debug.LogWarning($"[FuncAnimData.RuntimeInfo] Parent data or intervals list is null."); return false; } return funcAnimData.intervals.Any(interval => interval.intervalType == type); } public List GetEnablingIntervals() => GetEnablingIntervals(currentPlayTime); public List GetEnablingIntervals(float time) { if (funcAnimData == null || funcAnimData.intervals == null) { Debug.LogWarning($"[FuncAnimData.RuntimeInfo] Parent data or intervals list is null."); return new List(); } return funcAnimData.intervals.Where(interval => { if(Mathf.Approximately(interval.StartTime, interval.EndTime)) return false; //忽略瞬时区间 return time >= interval.StartTime && time <= interval.EndTime; //在区间内 }).ToList(); } } }