Files
Cielonos/Assets/Scripts/SLSUtilities/FunctionalAnimation/RuntimeFuncAnim.cs

296 lines
11 KiB
C#
Raw Normal View History

2026-07-18 03:16:20 -04:00
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<string, bool> updateUntilStatus;
private List<FuncAnimEvent> 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<string, bool>();
playedEndEvents = new List<FuncAnimEvent>();
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<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(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(FuncAnimDisruptionCause cause)
{
var validDataEvents = dataEvents.endEvents.Exclude(playedEndEvents)
.Where(evt => evt.payload == null || evt.payload.CanExecuteOnDisruption(cause)).ToList();
validDataEvents.Invoke();
var validRuntimeEvents = runtimeEvents.endEvents.Exclude(playedEndEvents)
.Where(evt => evt.payload == null || evt.payload.CanExecuteOnDisruption(cause)).ToList();
validRuntimeEvents.Invoke();
}
public RuntimeFuncAnim AddStartEvent(FuncAnimPayloadBase payload)
{
payload.runtimeFuncAnim = this;
runtimeEvents.startEvents.Add(payload);
return this;
}
public void InvokeStartEvents()
{
InvokePayloads(dataEvents.startEvents);
InvokePayloads(runtimeEvents.startEvents);
}
public RuntimeFuncAnim AddDisruptionEvent(FuncAnimPayloadBase payload)
{
payload.runtimeFuncAnim = this;
runtimeEvents.disruptionEvents.Add(payload);
return this;
}
public void InvokeDisruptionEvents(FuncAnimDisruptionCause cause, IReadOnlyCollection<string> suppressedEventNames = null)
{
InvokePayloads(dataEvents.disruptionEvents
.Where(p => p == null || p.CanExecuteOnDisruption(cause) &&
!IsSuppressedDisruptionEvent(p, suppressedEventNames))
.ToList());
InvokePayloads(runtimeEvents.disruptionEvents
.Where(p => p == null || p.CanExecuteOnDisruption(cause) &&
!IsSuppressedDisruptionEvent(p, suppressedEventNames))
.ToList());
}
private static bool IsSuppressedDisruptionEvent(FuncAnimPayloadBase payload, IReadOnlyCollection<string> suppressedEventNames)
{
if (payload == null || suppressedEventNames == null || suppressedEventNames.Count == 0)
{
return false;
}
if (suppressedEventNames.Contains("*"))
{
return true;
}
return suppressedEventNames.Contains(payload.EventName);
}
private void InvokePayloads(List<FuncAnimPayloadBase> payloads)
{
if (payloads == null) return;
foreach (var payload in payloads)
{
if (payload != null)
{
if (payload.isOnce && payload.hasExecuted) continue;
payload.Invoke();
payload.hasExecuted = true;
}
}
}
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<bool> payload)
{
payload.runtimeFuncAnim = this;
runtimeEvents.updateUntilEvents.Add(payload);
updateUntilStatus.Add(payload.EventName, false);
return this;
}
public void InvokeUpdateUntilEvents()
{
foreach (FuncAnimPayloadBase<bool> updateUntilEvent in dataEvents.updateUntilEvents)
{
if (!updateUntilStatus[updateUntilEvent.EventName])
{
updateUntilStatus[updateUntilEvent.EventName] = updateUntilEvent.Invoke();
}
}
foreach (FuncAnimPayloadBase<bool> 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<FuncAnimPayloadBase<bool>> events = null)
{
updateUntilStatus.Clear();
if (events == null)
{
foreach (FuncAnimPayloadBase<bool> evt in dataEvents.updateUntilEvents)
{
updateUntilStatus.Add(evt.EventName, false);
}
foreach (FuncAnimPayloadBase<bool> evt in runtimeEvents.updateUntilEvents)
{
updateUntilStatus.Add(evt.EventName, false);
}
}
else
{
foreach (FuncAnimPayloadBase<bool> evt in events)
{
updateUntilStatus.Add(evt.EventName, false);
}
}
}
public void ResetUpdateUntilEventsStatus()
{
List<string> 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<FuncAnimInterval> GetEnablingIntervals() => GetEnablingIntervals(currentPlayTime);
public List<FuncAnimInterval> GetEnablingIntervals(float time)
{
if (funcAnimData == null || funcAnimData.intervals == null)
{
Debug.LogWarning($"[FuncAnimData.RuntimeInfo] Parent data or intervals list is null.");
return new List<FuncAnimInterval>();
}
return funcAnimData.intervals.Where(interval =>
{
if(Mathf.Approximately(interval.StartTime, interval.EndTime)) return false; //忽略瞬时区间
return time >= interval.StartTime && time <= interval.EndTime; //在区间内
}).ToList();
}
}
}