331 lines
14 KiB
C#
331 lines
14 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Cielonos.MainGame.Characters;
|
||
using Sirenix.OdinInspector;
|
||
using SLSUtilities.FunctionalAnimation;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame
|
||
{
|
||
public partial class FunctionalAnimationSubmodule : SubmoduleBase<AnimationSubcontrollerBase>
|
||
{
|
||
private AnimationSubcontrollerBase animationSc => owner;
|
||
private CharacterBase character => owner.owner;
|
||
private Animator animator => owner.animator;
|
||
private Player player => character as Player;
|
||
public string animatorLayerName;
|
||
|
||
|
||
public Dictionary<string, FuncAnimData> collection;
|
||
|
||
public RuntimeFuncAnim currentRuntimeFuncAnim;
|
||
public FuncAnimData currentData => currentRuntimeFuncAnim.funcAnimData;
|
||
public AnimationClip currentClip => currentData.animationClip;
|
||
|
||
|
||
public float currentPlaySpeedMultiplier = 1f;
|
||
[ShowInInspector]
|
||
public float currentPlayTime => currentRuntimeFuncAnim?.currentPlayTime ?? 0f;
|
||
public float currentTotalPlayTime => currentRuntimeFuncAnim?.currentTotalPlayTime ?? 0f;
|
||
public float currentNormalizedPlayTime => Mathf.Min(1, currentPlayTime / currentClip.length);
|
||
[ShowInInspector]
|
||
public float currentFrame => currentRuntimeFuncAnim?.currentPlayTime * currentRuntimeFuncAnim?.funcAnimData.animationClip.frameRate ?? 0f;
|
||
public float currentScaledClipLength => currentClip.length / currentPlaySpeedMultiplier;
|
||
|
||
|
||
public FunctionalAnimationSubmodule(AnimationSubcontrollerBase owner, string animatorLayerName) : base(owner)
|
||
{
|
||
this.animatorLayerName = animatorLayerName;
|
||
collection = new Dictionary<string, FuncAnimData>();
|
||
}
|
||
|
||
public void Reset(FuncAnimData animation)
|
||
{
|
||
if (animation == null) return;
|
||
|
||
Remove(animation.animInfo.animationName);
|
||
Add(animation);
|
||
}
|
||
|
||
public void Add(FuncAnimData animation)
|
||
{
|
||
collection[animation.animInfo.animationName] = animation;
|
||
if (character is Player player)
|
||
{
|
||
player.animationSc.animatorOverride[animation.animInfo.stateName] = animation.animationClip;
|
||
}
|
||
}
|
||
|
||
public void Remove(string animationName)
|
||
{
|
||
if (collection.ContainsKey(animationName))
|
||
{
|
||
collection.Remove(animationName);
|
||
}
|
||
}
|
||
}
|
||
|
||
public partial class FunctionalAnimationSubmodule
|
||
{
|
||
private static readonly int ActionSpeed = Animator.StringToHash("ActionSpeed");
|
||
|
||
public bool CheckPlayability(RuntimeFuncAnim oldFuncAnim, RuntimeFuncAnim newFuncAnim)
|
||
{
|
||
if (oldFuncAnim != null)
|
||
{
|
||
var oldData = oldFuncAnim.funcAnimData;
|
||
var newData = newFuncAnim.funcAnimData;
|
||
|
||
FuncAnimInterval specifiedInterval = oldData.intervals.FirstOrDefault(interval =>
|
||
interval.intervalType == IntervalType.Custom && interval.intervalName == "SpecifiedActionDisruption");
|
||
|
||
if (specifiedInterval != null)
|
||
{
|
||
if (oldData.interactions.TryGetValue("SpecifiedActionDisruption", out List<string> interactions))
|
||
{
|
||
if (interactions.Contains(newData.animInfo.animationName))
|
||
{
|
||
return animationSc.currentIntervals.Any(interval => interval == specifiedInterval);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return CheckPlayability(newFuncAnim.funcAnimData.animInfo.disruptionType);
|
||
}
|
||
|
||
public bool CheckPlayability(DisruptionType disruptionType = DisruptionType.NormalAction)
|
||
{
|
||
if (character.statusSm.isDead)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (currentRuntimeFuncAnim == null || disruptionType == DisruptionType.Death || disruptionType == DisruptionType.Must)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (disruptionType == DisruptionType.ForcedAction)
|
||
{
|
||
return animationSc.disruptionStatus[DisruptionType.ForcedAction] ||
|
||
animationSc.disruptionStatus[DisruptionType.NormalAction] ||
|
||
animationSc.disruptionStatus[DisruptionType.Movement];
|
||
}
|
||
|
||
if (disruptionType == DisruptionType.NormalAction)
|
||
{
|
||
return animationSc.disruptionStatus[DisruptionType.NormalAction] || animationSc.disruptionStatus[DisruptionType.Movement];
|
||
}
|
||
|
||
if (disruptionType == DisruptionType.Movement)
|
||
{
|
||
return animationSc.disruptionStatus[DisruptionType.Movement];
|
||
}
|
||
|
||
throw new Exception("Invalid DisruptionType for checking playability.");
|
||
}
|
||
|
||
public bool CheckDisruption(DisruptionType disruptionType)
|
||
{
|
||
return disruptionType switch
|
||
{
|
||
DisruptionType.None => false,
|
||
DisruptionType.Must or DisruptionType.Death => true,
|
||
_ => animationSc.disruptionStatus[disruptionType]
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过Data播放动画
|
||
/// </summary>
|
||
/// <param name="funcAnimData">动画数据</param>
|
||
/// <param name="animationSpeedMultiplier">播放速度倍率</param>
|
||
/// <param name="transitionDuration">过渡时间</param>
|
||
/// <param name="isNormalizedTransition">过渡时间是否归一化</param>
|
||
/// <param name="runtimeStartEvents">运行时开始事件列表</param>
|
||
/// <returns></returns>
|
||
public bool Play(FuncAnimData funcAnimData, float animationSpeedMultiplier = 1, float transitionDuration = 0.1f,
|
||
bool isNormalizedTransition = false, List<FuncAnimPayloadBase> runtimeStartEvents = null)
|
||
{
|
||
var newRtFuncAnim = new RuntimeFuncAnim(funcAnimData, character);
|
||
|
||
if (!CheckPlayability(currentRuntimeFuncAnim, newRtFuncAnim))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (currentRuntimeFuncAnim != null && !currentRuntimeFuncAnim.isDisrupted)
|
||
{
|
||
currentRuntimeFuncAnim.isDisrupted = true;
|
||
currentRuntimeFuncAnim.InvokeDisruptionEvents();
|
||
currentRuntimeFuncAnim.InvokeEndEvents();
|
||
ResetPlayerPreinput();
|
||
|
||
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
|
||
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
|
||
animationSc.disruptionStatus[DisruptionType.Movement] = false;
|
||
}
|
||
|
||
float oldClipLength = currentRuntimeFuncAnim != null ? currentClip.length : 1f;
|
||
currentRuntimeFuncAnim = newRtFuncAnim;
|
||
currentRuntimeFuncAnim.ClearRuntimeEvents();
|
||
runtimeStartEvents?.ForEach(payload => currentRuntimeFuncAnim.AddStartEvent(payload));
|
||
|
||
FuncAnimInfo animInfo = currentData.animInfo;
|
||
currentPlaySpeedMultiplier = animInfo.isAffectedBySpeedMultiplier ? animationSpeedMultiplier : 1;
|
||
|
||
animationSc.isDisablingMoveXZ = false;
|
||
animationSc.isDisablingMoveY = false;
|
||
|
||
currentRuntimeFuncAnim.currentPlayTime = animInfo.overrideStartFrame / currentClip.frameRate;
|
||
float normalizedTimeOffset = currentPlayTime / currentClip.length;
|
||
float normalizedTransitionDuration = isNormalizedTransition ? transitionDuration : transitionDuration / oldClipLength;
|
||
animator.CrossFade(animInfo.stateName, normalizedTransitionDuration, animator.GetLayerIndex(animatorLayerName), normalizedTimeOffset);
|
||
float actionSpeed = animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
|
||
animator.SetFloat(ActionSpeed, actionSpeed);
|
||
|
||
currentRuntimeFuncAnim.InvokeStartEvents();
|
||
currentRuntimeFuncAnim.isDisrupted = false;
|
||
|
||
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
|
||
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
|
||
currentRuntimeFuncAnim.SetUpdateUntilEventsStatus();
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过动画名称播放动画
|
||
/// </summary>
|
||
/// <param name="animationName">动画名称,详见FuncAnimData</param>
|
||
/// <param name="animationSpeedMultiplier">播放速度倍率</param>
|
||
/// <param name="transitionDuration">过渡时间</param>
|
||
/// <param name="isNormalizedTransition">过渡时间是否归一化</param>
|
||
/// <param name="runtimeStartEvents">运行时开始事件列表</param>
|
||
/// <returns></returns>
|
||
public bool Play(string animationName, float animationSpeedMultiplier = 1, float transitionDuration = 0.1f,
|
||
bool isNormalizedTransition = false, List<FuncAnimPayloadBase> runtimeStartEvents = null)
|
||
{
|
||
if (!collection.TryGetValue(animationName, out FuncAnimData funcAnimData))
|
||
{
|
||
Debug.LogWarning($"[FunctionalAnimationSubmodule] Animation '{animationName}' not found in collection.");
|
||
return false;
|
||
}
|
||
|
||
return Play(funcAnimData, animationSpeedMultiplier, transitionDuration, isNormalizedTransition, runtimeStartEvents);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止指定名称的动画,默认为强制停止
|
||
/// </summary>
|
||
public bool Stop(string animationName, DisruptionType disruptionType = DisruptionType.Must, float transitionDuration = 0.1f)
|
||
{
|
||
if (currentRuntimeFuncAnim == null) return true;
|
||
if (currentRuntimeFuncAnim.funcAnimData.animInfo.animationName != animationName) return false;
|
||
return Stop(disruptionType, transitionDuration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止当前动画
|
||
/// </summary>
|
||
/// <param name="disruptionType">打断类型</param>
|
||
/// <param name="transitionDuration">过渡时间</param>
|
||
/// <returns>是否停止成功</returns>
|
||
public bool Stop(DisruptionType disruptionType, float transitionDuration = 0.1f)
|
||
{
|
||
//Debug.Log($"[FunctionalAnimationSubmodule] Attempting to stop animation due to disruption type: {disruptionType}");
|
||
|
||
if(disruptionType == DisruptionType.None) return false;
|
||
|
||
if(currentRuntimeFuncAnim == null) return true;
|
||
|
||
if (disruptionType != DisruptionType.Death && disruptionType != DisruptionType.Must)
|
||
{
|
||
if (!CheckDisruption(disruptionType))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!currentRuntimeFuncAnim.isDisrupted)
|
||
{
|
||
currentRuntimeFuncAnim.isDisrupted = true;
|
||
currentRuntimeFuncAnim.InvokeDisruptionEvents();
|
||
currentRuntimeFuncAnim.InvokeEndEvents();
|
||
ResetPlayerPreinput();
|
||
}
|
||
|
||
float oldClipLength = currentRuntimeFuncAnim != null ? currentClip.length : 1f;
|
||
float normalizedTransitionDuration = transitionDuration / oldClipLength;
|
||
animator.CrossFade("Empty", normalizedTransitionDuration, animator.GetLayerIndex(animatorLayerName));
|
||
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
|
||
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
|
||
animationSc.disruptionStatus[DisruptionType.Movement] = false;
|
||
currentRuntimeFuncAnim = null;
|
||
return true;
|
||
}
|
||
|
||
public void UpdateTime()
|
||
{
|
||
if (currentRuntimeFuncAnim == null)
|
||
{
|
||
currentPlaySpeedMultiplier = 1f;
|
||
return;
|
||
}
|
||
|
||
float step = owner.owner.selfTimeSm.DeltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
|
||
currentRuntimeFuncAnim.currentPlayTime += step;
|
||
currentRuntimeFuncAnim.currentTotalPlayTime += step;
|
||
if (currentPlayTime >= currentClip.length)
|
||
{
|
||
UpdateEvents();
|
||
|
||
if (currentClip.isLooping)
|
||
{
|
||
currentRuntimeFuncAnim.currentPlayTime %= currentClip.length;
|
||
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
|
||
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
|
||
currentRuntimeFuncAnim.ResetUpdateUntilEventsStatus();
|
||
currentRuntimeFuncAnim.InvokeStartEvents();
|
||
}
|
||
else
|
||
{
|
||
if (!currentRuntimeFuncAnim.isDisrupted)
|
||
{
|
||
currentRuntimeFuncAnim.isDisrupted = true;
|
||
currentRuntimeFuncAnim.InvokeDisruptionEvents();
|
||
currentRuntimeFuncAnim.InvokeEndEvents();
|
||
ResetPlayerPreinput();
|
||
}
|
||
|
||
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
|
||
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
|
||
animationSc.disruptionStatus[DisruptionType.Movement] = false;
|
||
currentRuntimeFuncAnim = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void UpdateEvents()
|
||
{
|
||
currentRuntimeFuncAnim?.UpdateAnimEvent();
|
||
currentRuntimeFuncAnim?.InvokeUpdateEvents();
|
||
currentRuntimeFuncAnim?.InvokeUpdateUntilEvents();
|
||
}
|
||
|
||
private void ResetPlayerPreinput() => player?.inputSc.preinputSubmodule.Reset();
|
||
}
|
||
|
||
public partial class FunctionalAnimationSubmodule
|
||
{
|
||
public float GetIntervalScaledDuration(IntervalType type)
|
||
{
|
||
if (currentData == null) return 0f;
|
||
FuncAnimInterval interval = currentData.intervals.FirstOrDefault(i => i.intervalType == type);
|
||
if (interval == null) return 0f;
|
||
float baseDuration = interval.EndTime - interval.StartTime;
|
||
return baseDuration / (currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier);
|
||
}
|
||
}
|
||
} |