Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/Submodules/FunctionalAnimationSubmodule.cs
SoulliesOfficial f26f9fd374 爆更
2026-03-20 12:07:44 -04:00

331 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}