using System.Collections.Generic; using Games.LogicObj; using GCGame.Table; using Module.Log; using UnityEngine; using UnityEngine.Events; namespace Games.AnimationModule { // 动画机修改为正常流程,使用Trigger推动动画机内部的转换 // AnimationLogic仅仅保证每一帧末尾将最后收到的动画触发到动画机 // 动画机本身已经不再具备随意跳帧的功能,普攻跳帧由动画机转换配置 public class AnimationLogic : MonoBehaviour { public bool cullAnimator { get { return _cullAnimator; } set { if (_cullAnimator != value) { _cullAnimator = value; RefreshCullAnimator(); } } } private bool _cullAnimator = true; private static readonly Queue _effectBuffer = new Queue(); private Animator _animationControl; private List _boneList; private Tab_Animation _inAnimator; // 暂时只有冻结效果,更多效果就扩展这个参数 private AnimationFreezeData _freezeData; // 当前Animator中,正在播放的动画真实Hash; private int _inAnimatorHash; // 三层动画状态 - 动画机状态/希望动画机状态/当前帧希望转入状态 // _nextInComponent搜集一帧内的转化需求,仅仅保留最终需求; // _toAnimator记录当前转换需求,防止重复切入循环动画,额外叠加Trigger;此外负责处理动画结束; // _inAnimator仅仅处理动画退出的情况。 private Tab_Animation _nextInComponent; // 特殊跳转方式,跳转Trigger的名称;同_nextInComponent一个级别。 private string _nextTransitionName; private Tab_Animation _toAnimator; private int _visibleRendererCount; public event UnityAction onPlayAnim; public event UnityAction onAnimEnter; public event UnityAction onPlayEffect; public event UnityAction onRemoveEffect; public event UnityAction onPlaySound; public event UnityAction onStopSound; public float animationSpeed { get; private set; } public bool isFreeze { get; private set; } /// /// 初始化动画逻辑数据 /// public void InitAnimLogicData(Animator animator, List rendererList) { _animationControl = animator; animationSpeed = 1f; isFreeze = false; RefreshCullAnimator(); // 破烂动画机,消耗高,需要做CullCompletely,然而Cull状态被推两个Trigger会导致执行顺序问题; // 然后这破烂是否处于Cull状态还无法获得,只能自己抓全部Renderer的Visible事件来监视Animator的可视状态; if (rendererList == null) // 特殊的Bypass,给非ObjPartRoot用的 _visibleRendererCount = 1; else { _visibleRendererCount = 0; var unityAction = new UnityAction(OnBecomeVisible); for (var i = 0; i < rendererList.Count; i++) { var itemRenderer = rendererList[i].CachedRenderer; if (itemRenderer != null) { var listener = itemRenderer.gameObject.EnsureComponent(); if (listener.isVisible) _visibleRendererCount++; // 注:即使Listener被摧毁也可以在最后一帧获得OnBecomeInvisible listener.onBecameVisible += unityAction; } } } OnEnable(); } public void SetAnimationSpeed(float animSpeed) { if (animationSpeed != animSpeed) { animationSpeed = animSpeed; RefreshAnimationSpeed(); } } private void OnBecomeVisible(bool isVisible) { if (isVisible) _visibleRendererCount++; else _visibleRendererCount--; } private void SetAnimFreeze(bool freeze) { if (isFreeze != freeze) { isFreeze = freeze; RefreshAnimationSpeed(); } } private void RefreshAnimationSpeed() { if (_animationControl != null) _animationControl.speed = isFreeze ? 0f : animationSpeed; } private void RefreshCullAnimator() { if (_animationControl != null) _animationControl.cullingMode = _cullAnimator ? AnimatorCullingMode.CullCompletely : AnimatorCullingMode.AlwaysAnimate; } // 注:物体初始化帧可能遭遇两次OnEnable。第一次由Unity触发,不存在_animationControl;第二次由InitAnimLogicData触发。 private void OnEnable() { if (_animationControl != null) { var animatorBehavior = _animationControl.GetBehaviour(); if (animatorBehavior == null) LogModule.WarningLog(string.Format("动画机没有配置状态转换事件,于物体{0}", transform.GetHierarchyName())); else { animatorBehavior.OnAnimStateEnter -= OnAnimationEnter; animatorBehavior.OnAnimStateEnter += OnAnimationEnter; _toAnimator = TableManager.GetAnimationByID(Obj_Character.idleAnimId, 0); _inAnimatorHash = GetNameHash(_toAnimator); } if (_boneList == null) { _boneList = new List(); var root = transform.Find("Bip001"); if (root != null) { var children = root.GetComponentsInChildren(); for (var i = 0; i < children.Length; i++) { _boneList.Add(new BoneData(children[i])); } } } } } // 防止动画速度在回收时被其他组件调整的情况 private void OnDisable() { if (!GameManager.applicationQuit) { if (_boneList != null) { for (var i = 0; i < _boneList.Count; i++) _boneList[i].Reset(); } animationSpeed = 1f; isFreeze = false; _freezeData = null; RefreshAnimationSpeed(); cullAnimator = true; } } // 注:动画自己转向自己时,实际会先开始状态,然后再退出状态,直接End会导致新开始的特效也消失; // 因此特殊处理这类情况,自己切换到自己,将在开始时结束之前特效,然后开始新的; private void OnAnimationEnter(AnimatorStateInfo stateInfo) { _inAnimatorHash = stateInfo.shortNameHash; // 注:已知问题,有一些动画没有对应动画表,会导致状态错误;但是一般那些都是只有单一动画的友好NPC,所以不造成问题 //var nextAnimData = GetStateDataByHash(stateInfo.shortNameHash); // 仅仅执行预期的动画转换,非预期转换直接跳过;防御上次动画因阻断未完全执行的诡异情况; if (_toAnimator != null && GetNameHash(_toAnimator) == stateInfo.shortNameHash) { // 注:自己转入自己时,当前normalizedTime会为转入前播放位置,不能用作时间参考 // 自己转入自己并且跳过时间的情况没有处理 var skipTime = _toAnimator == _inAnimator ? 0f : stateInfo.normalizedTime * stateInfo.length * stateInfo.speed; PlayStartEffect(_toAnimator, skipTime); ConsumeInAnimator(); _inAnimator = _toAnimator; _toAnimator = null; if (_inAnimator.FreezeStart >= 0f && _inAnimator.FreezeDuration > 0f) { var startTime = Time.time + _inAnimator.FreezeStart; var endTime = startTime + _inAnimator.FreezeDuration; _freezeData = new AnimationFreezeData(startTime, endTime); } else _freezeData = null; SetAnimFreeze(_inAnimator.FreezeStart == 0f); } else // 每次转换都应该消耗掉InAnimator,只是toAnimator时需要在特殊时机消耗 { ConsumeInAnimator(); } if (onAnimEnter != null) onAnimEnter(_inAnimatorHash); } public void PlayStartEffect(Tab_Animation animData, float skipTime) { if (onPlayEffect != null) { PushEffectsToBuffer(animData.StartEffect); while (_effectBuffer.Count > 0) onPlayEffect(_effectBuffer.Dequeue(), skipTime); } if (onPlaySound != null) onPlaySound(animData.SoundID); } public void RemoveStartEffect(Tab_Animation animData) { if (onRemoveEffect != null) { PushEffectsToBuffer(animData.StartEffect); while (_effectBuffer.Count > 0) onRemoveEffect(_effectBuffer.Dequeue()); } if (onStopSound != null) onStopSound(animData.SoundID); } private static void PushEffectsToBuffer(string animText) { var animTexts = animText.Split(';'); for (var i = 0; i < animTexts.Length; i++) { int temp; if (int.TryParse(animTexts[i], out temp) && temp >= 0) _effectBuffer.Enqueue(temp); } } private void ConsumeInAnimator() { if (_inAnimator != null) { RemoveStartEffect(_inAnimator); PlayEndEffect(_inAnimator); _inAnimator = null; } } public void PlayEndEffect(Tab_Animation animData) { if (onPlayEffect != null) { PushEffectsToBuffer(animData.EndEffect); while (_effectBuffer.Count > 0) onPlayEffect(_effectBuffer.Dequeue(), 0f); } } /// /// 检查当前播放的动画是否为需要动画 /// public bool IsPlayAnim(int animId) { var animData = TableManager.GetAnimationByID(animId, 0); return IsPlayAnim(animData); } /// /// 检查当前播放的动画是否为需要动画 /// public bool IsPlayAnim(Tab_Animation animData) { var result = false; if (AnimPlayable() && animData != null) { var nameHash = GetNameHash(animData); result = nameHash == _animationControl.GetCurrentAnimatorStateInfo(0).shortNameHash; } return result; } public bool AnimPlayable() { return _animationControl != null && _animationControl.runtimeAnimatorController != null; } // 防止一帧收到多个Play指令,导致Animator抽风,动画统一在LateUpdate更新 public void Play(int animId, string transitionName = default(string)) { if (animId > -1) { var animData = GetAnimInfoById(animId); if (animData != null) Play(animData, transitionName); } } public void Play(Tab_Animation animData, string transitionName = default(string)) { // 不做过多判断 - 直接缓存NextAnim if (AnimPlayable() && animData != null) { _nextInComponent = animData; _nextTransitionName = transitionName; if (onPlayAnim != null) onPlayAnim(animData, transitionName); } } public bool IsInTransition() { return AnimPlayable() && _animationControl.IsInTransition(0); } /// /// 初始动画专用流程,会立刻执行动画转换而不会搜集一帧内的情况 /// // 注:滥用这个接口,可能导致Animator Trigger无法清空的问题 // 注:等待到LateUpdate的流程会实际使动画机慢一帧,但是找不到一个介于Update和动画机状态刷新之间的时间点 // 调整ExecuteOrder同热更新会有后患 public void PlayStartShow(int animId) { if (AnimPlayable() && animId > -1) { var animData = GetAnimInfoById(animId); if (animData != null) { var transitionName = animData.AnimName.ToLower(); if (_animationControl.parameters.FindIndex(a => a.name == transitionName) > -1) { _nextInComponent = null; _toAnimator = animData; //AddAnimLogicData(animData); _animationControl.SetTrigger(transitionName); } // else // { // animData = GetAnimInfoById(0); //默认Idle // if(animData != null) // { // transitionName = animData.AnimName.ToLower(); // if(_animationControl.parameters.FindIndex(a => (a.name) == transitionName) > -1) // { // _nextInComponent = null; // _toAnimator = animData; //AddAnimLogicData(animData); // _animationControl.SetTrigger(transitionName); // } // } // } } } } private void LateUpdate() { // 注:不可见情况下,允许推进动画指令,但是不允许执行 if (AnimPlayable() && (_visibleRendererCount > 0 || _animationControl.cullingMode != AnimatorCullingMode.CullCompletely)) { if (_nextInComponent != null) { var nextHash = GetNameHash(_nextInComponent); // 特殊处理转换到同一动画状态,但是需要不同特效的稀有情况 if (_toAnimator != null && GetNameHash(_toAnimator) == nextHash) { _toAnimator = _nextInComponent; } // 特殊检测 - 循环动画不刷新自己; // Hack:循环动画不会因为ExitTime结束,因此_currentAnim必然对应自己 else if (_nextInComponent.NextAnimID > -1 || _inAnimatorHash != GetNameHash(_nextInComponent)) { // 特殊处理普攻接普攻,如果有SkipTim,使用衔接切换 var transitionName = string.IsNullOrEmpty(_nextTransitionName) ? _nextInComponent.AnimName.ToLower() : _nextTransitionName; // 如果动画机不存在对应的状态,跳过参数设置 if (_animationControl.parameters.FindIndex(a => a.name == transitionName) > -1) { _toAnimator = _nextInComponent; _animationControl.SetTrigger(transitionName); } } _nextInComponent = null; } if (_freezeData != null) { if (isFreeze) { if (_freezeData.freezeEnd < Time.time) { SetAnimFreeze(false); _freezeData = null; } } else { if (_freezeData.freezeStart < Time.time) SetAnimFreeze(true); } } } } public static Tab_Animation GetAnimInfoById(int animId) { return CommonUtility.TryGetTable(animId, a => TableManager.GetAnimationByID(a, 0)); } public void StartEffect(int effectId) { if (onPlayEffect != null) onPlayEffect(effectId, 0f); } public void SetCurrAnimatorEnable(string state, bool isEnable = true) { if (AnimPlayable()) _animationControl.SetBool(state, isEnable); } /// /// 如果当前正播放targetAnimId并且没有下个动画,就切换到Idle动画 /// /// 目标动画Id /// 站立状态使用的通用动画 public void SwitchLoopToIdle(int targetAnimId, int idleId) { if (AnimPlayable()) { var animData = GetAnimInfoById(targetAnimId); if (animData.NextAnimID < 0 && IsPlayAnim(animData)) Play(idleId); } } public bool HasMotionState(string state, int layer = 0) { if (_animationControl == null || _animationControl.runtimeAnimatorController == null) return false; return _animationControl.HasState(layer, Animator.StringToHash(state.ToLower())); } public static string GetTransitionName(int fromId, int toId) { var fromAnim = GetAnimInfoById(fromId); var toAnim = GetAnimInfoById(toId); if (fromAnim != null && toAnim != null) return GetTransitionName(fromAnim, toAnim); return string.Empty; } public static string GetTransitionName(Tab_Animation from, Tab_Animation to) { return from.AnimName.ToLower() + "To" + to.AnimName.ToLower(); } private static int GetNameHash(Tab_Animation data) { // 注:测试发现,使用StringToHash比查表速度更快。因此不搞StringToHash表。 return Animator.StringToHash(data.AnimName.ToLower()); } public class AnimationFreezeData { public readonly float freezeStart; public readonly float freezeEnd; public AnimationFreezeData(float freezeStart, float freezeEnd) { this.freezeStart = freezeStart; this.freezeEnd = freezeEnd; } } private class BoneData { public readonly Transform bone; public readonly Vector3 position; public readonly Quaternion rotation; public readonly Vector3 scale; public BoneData(Transform root) { bone = root; position = bone.localPosition; rotation = bone.localRotation; scale = bone.localScale; } public void Reset() { bone.localPosition = position; bone.localRotation = rotation; bone.localScale = scale; } } } }