253 lines
10 KiB
C#
253 lines
10 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Cielonos.MainGame.Characters;
|
||
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;
|
||
public float currentPlayTime => currentRuntimeFuncAnim.currentPlayTime;
|
||
public float currentNormalizedPlayTime => Mathf.Min(1, currentPlayTime / currentClip.length);
|
||
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(DisruptionType disruptionType = DisruptionType.NormalAction)
|
||
{
|
||
if (currentRuntimeFuncAnim == null || disruptionType == DisruptionType.ForcedAction)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
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 or DisruptionType.ForcedAction or DisruptionType.ForcedExternal => true,
|
||
_ => animationSc.disruptionStatus[disruptionType]
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过动画名称播放动画
|
||
/// </summary>
|
||
/// <param name="animationName">动画名称,详见FuncAnimData</param>
|
||
/// <param name="animationSpeedMultiplier">播放速度倍率</param>
|
||
/// <param name="transitionDuration">过渡时间</param>
|
||
/// <param name="runtimeStartEvents">运行时开始事件列表</param>
|
||
/// <returns></returns>
|
||
public bool Play(string animationName, float animationSpeedMultiplier = 1, float transitionDuration = 0.1f,
|
||
List<FuncAnimPayloadBase> runtimeStartEvents = null)
|
||
{
|
||
if (!collection.TryGetValue(animationName, out FuncAnimData funcAnimData))
|
||
{
|
||
Debug.LogWarning($"[FunctionalAnimationSubmodule] Animation '{animationName}' not found in collection.");
|
||
return false;
|
||
}
|
||
|
||
var newRtFuncAnim = new RuntimeFuncAnim(funcAnimData, character);
|
||
|
||
if (!CheckPlayability(newRtFuncAnim.funcAnimData.animInfo.disruptionType))
|
||
{
|
||
Debug.LogWarning($"[FunctionalAnimationSubmodule] Cannot play animation '{currentData.animInfo.animationName}' " +
|
||
$"due to disruption type{currentData.animInfo.disruptionType}.");
|
||
return false;
|
||
}
|
||
|
||
if (currentRuntimeFuncAnim != null && !currentRuntimeFuncAnim.isDisrupted)
|
||
{
|
||
currentRuntimeFuncAnim.isDisrupted = true;
|
||
currentRuntimeFuncAnim.InvokeDisruptionEvents();
|
||
currentRuntimeFuncAnim.InvokeEndEvents();
|
||
ResetPlayerPreinput();
|
||
}
|
||
|
||
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 = transitionDuration / currentClip.length;
|
||
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="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)
|
||
{
|
||
if (!CheckDisruption(disruptionType))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!currentRuntimeFuncAnim.isDisrupted)
|
||
{
|
||
currentRuntimeFuncAnim.isDisrupted = true;
|
||
currentRuntimeFuncAnim.InvokeDisruptionEvents();
|
||
currentRuntimeFuncAnim.InvokeEndEvents();
|
||
ResetPlayerPreinput();
|
||
}
|
||
|
||
float normalizedTransitionDuration = transitionDuration / currentClip.length;
|
||
animator.CrossFade("Empty", normalizedTransitionDuration, animator.GetLayerIndex(animatorLayerName));
|
||
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
|
||
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
|
||
animationSc.disruptionStatus[DisruptionType.Movement] = false;
|
||
currentRuntimeFuncAnim = null;
|
||
|
||
Debug.Log("[FunctionalAnimationSubmodule] Animation stopped successfully.");
|
||
|
||
return true;
|
||
}
|
||
|
||
public void UpdateTime()
|
||
{
|
||
if (currentRuntimeFuncAnim == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
currentRuntimeFuncAnim.currentPlayTime += Time.deltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
|
||
|
||
if (currentPlayTime >= currentClip.length)
|
||
{
|
||
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()
|
||
{
|
||
if (currentRuntimeFuncAnim == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
currentRuntimeFuncAnim.UpdateAnimEvent();
|
||
|
||
currentRuntimeFuncAnim.InvokeUpdateEvents();
|
||
|
||
currentRuntimeFuncAnim.InvokeUpdateUntilEvents();
|
||
}
|
||
|
||
private void ResetPlayerPreinput() => player?.inputSc.preinputSubmodule.Reset();
|
||
}
|
||
} |