using System; using Animancer; using BrewMonster; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace BrewMonster { public struct AnimationQueue { public string AnimationName; public bool IsForceStopPrevious; } public class PlayerVisual : MonoBehaviour { [SerializeField] NamedAnimancerComponent namedAnimancer; [SerializeField] private INFO _playerInfo; private Dictionary _activeStates = new(); [SerializeField] private AnimancerState _currentState; [SerializeField] private Queue _animationQueue = new Queue(); [SerializeField] private List _animationList = new List(); [SerializeField] private bool isHit; [SerializeField] private int id; [SerializeField] private bool isDebug; private const float FadeTime = 100; private const FadeMode FadeMode = Animancer.FadeMode.FixedDuration; QueueActionEvent queueActionEvent; private void PlayActionEventHandler(PlayActionEvent @event) { //when this trigger, clear all the animation in the queue which in the same layer of animancer if (_animationQueue.Count > 0) { _animationQueue.Enqueue(new AnimationQueue { AnimationName = @event.AnimationName, IsForceStopPrevious = @event.IsForceStopPrevious }); _animationList = _animationQueue.Select(q => q.AnimationName).ToList(); return; } InternalPlayAnimation(@event.AnimationName, @event.ITransTime); } public void InitPlayerEventDoneHandler() { namedAnimancer = GetComponentInChildren(); if (namedAnimancer == null) { BrewMonster.BMLogger.LogWarning("InitPlayerEventDoneHandler animancer == null"); return; } var player = GetComponentInParent(); if (player == null) { BMLogger.LogWarning("player == null"); return; } _playerInfo = player.GetPlayInfo(); id = _playerInfo.cid; EventBus.SubscribeChannel(_playerInfo.cid, PlayActionEventHandler); EventBus.SubscribeChannelClass(_playerInfo.cid, QueueActionEventHandler); EventBus.SubscribeChannel(_playerInfo.cid, ClearComActFlagAllRankNodesEventHandler); } // public void InitElsePlayerEventDoneHandler(INFO playerInfo) // { // namedAnimancer = GetComponentInChildren(); // if (namedAnimancer == null) // { // BrewMonster.BMLogger.LogError("animancer == null"); // return; // } // //var player = GetComponentInParent(); // //if (player == null) // //{ // // BrewMonster.BMLogger.LogError("player == null"); // // return; // //} // _playerInfo = playerInfo;//player.GetPlayInfo(); // EventBus.SubscribeChannel(_playerInfo.cid, PlayActionEventHandler); // EventBus.SubscribeChannelClass(_playerInfo.cid, QueueActionEventHandler); // EventBus.SubscribeChannel(_playerInfo.cid, CleearComActFlagAllRankNodesEventHandler); // } private void ClearComActFlagAllRankNodesEventHandler(ClearComActFlagAllRankNodesEvent @event) { _animationQueue.Clear(); _animationList = _animationQueue.Select(q => q.AnimationName).ToList(); if (isHit) { ApplyDamage(); } //todo: this is dummy to force change to idle state // EventBus.PublishChannel(_playerInfo.cid, new PlayActionEvent("站立_通用")); } private void QueueActionEventHandler(QueueActionEvent @event) { if (!EnqueueAnimation(@event)) { BMLogger.LogError("HoangDev : EnqueueAnimation Failed"); } } private void Update() { PlayNext(); } public bool EnqueueAnimation(QueueActionEvent @event) { if (namedAnimancer == null) return false; _animationQueue.Enqueue(new AnimationQueue { AnimationName = @event.AnimationName, IsForceStopPrevious = @event.IsForceStopPrevious }); _animationList = _animationQueue.Select(q => q.AnimationName).ToList(); if (!isHit) { queueActionEvent = @event; isHit = @event.IsHitAnim; } return true; } private void PlayNext() { if (_animationQueue.Count == 0) { return; } // if (_currentState == null) // { // _animationQueue.Dequeue(); // return; // } //peek next if IsForceStopPrevious is true, force end if (_animationQueue.Peek().IsForceStopPrevious) { Debug.Log($" InternalPlayAnimation PlayNext: Force Stop Previous"); _currentState.Stop(); _currentState = null; } if (_currentState != null && _currentState.NormalizedTime < 1f) return; if (isHit)// have it relative to check _currentState == null? { ApplyDamage(); } var animationQueue = _animationQueue.Dequeue(); _animationList = _animationQueue.Select(q => q.AnimationName).ToList(); InternalPlayAnimation(animationQueue.AnimationName); } void ApplyDamage() { if (queueActionEvent == null) return; isHit = false; queueActionEvent.SetFlag(true, queueActionEvent.AttackEvent); queueActionEvent = null; } private void OnDestroy() { EventBus.UnsubscribeChannel(_playerInfo.cid, PlayActionEventHandler); EventBus.UnsubscribeChannelClass(_playerInfo.cid, QueueActionEventHandler); EventBus.UnsubscribeChannel(_playerInfo.cid, ClearComActFlagAllRankNodesEventHandler); // EventBus.UnsubscribeAllInChannel(_playerInfo.cid); } public bool IsAnimationExist(string animationName) { return namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false; } private string _currentAnimationName; /// /// play an animation with name /// /// /// /// private void InternalPlayAnimation(string animationName, float duration = FadeTime, FadeMode fadeMode = FadeMode) { //Debug.Log($"InternalPlayAnimation: animationName FUllNAME={animationName}"); string fullName = animationName; string removeShapeName = animationName; string removeFlyName = animationName; if(animationName.Contains("_")) { int underscoreIndex = animationName.IndexOf('_'); removeShapeName = animationName.Substring(underscoreIndex + 1); } bool isState = namedAnimancer.States.TryGet(removeShapeName, out var existingState) ? true : false; if (isState) { _currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode); _currentAnimationName = removeShapeName; //Debug.Log($"InternalPlayAnimation: removeShapeName 1 TriggerName={removeShapeName}"); return; } bool isState2 = namedAnimancer.States.TryGet(fullName, out var existingState2) ? true : false; if (isState2) { _currentState = namedAnimancer.TryPlay(fullName, duration / 1000, fadeMode); _currentAnimationName = fullName; return; } string fullName2 = fullName; //if contain 空拳 change it to 通用 apply to full name and removeShapeName if (fullName2.Contains("空拳")) { fullName2 = fullName2.Replace("空拳", "通用"); removeShapeName = removeShapeName.Replace("空拳", "通用"); } bool isState3 = namedAnimancer.States.TryGet(removeShapeName, out var existingState3) ? true : false; if (isState3) { _currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode); _currentAnimationName = removeShapeName; return; } bool isState4 = namedAnimancer.States.TryGet(fullName2, out var existingState4) ? true : false; if (isState4) { _currentState = namedAnimancer.TryPlay(fullName2, duration / 1000, fadeMode); _currentAnimationName = fullName2; return; } int index = removeShapeName.IndexOf("空中"); if (index >= 0 && removeShapeName.Length >= index + 4) { removeFlyName = removeShapeName.Remove(index + 2, 2); } else { removeFlyName = removeShapeName; } bool isState5 = namedAnimancer.States.TryGet(removeFlyName, out var existingState5) ? true : false; if (isState5) { _currentState = namedAnimancer.TryPlay(removeFlyName, duration / 1000, fadeMode); _currentAnimationName = removeFlyName; return; } if (fullName2.Contains("_通用")) { fullName2 = fullName2.Replace("_通用", ""); removeShapeName = removeShapeName.Replace("_通用", ""); removeFlyName = removeFlyName.Replace("_通用", ""); } bool isState6 = namedAnimancer.States.TryGet(fullName2, out var existingState6) ? true : false; if (isState6) { _currentState = namedAnimancer.TryPlay(fullName2, duration / 1000, fadeMode); _currentAnimationName = fullName2; return; } bool isState8 = namedAnimancer.States.TryGet(removeShapeName, out var existingState8) ? true : false; if (isState8) { _currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode); _currentAnimationName = removeShapeName; return; } bool isState7 = namedAnimancer.States.TryGet(removeFlyName, out var existingState7) ? true : false; if (isState7) { _currentState = namedAnimancer.TryPlay(removeFlyName, duration / 1000, fadeMode); _currentAnimationName = removeFlyName; return; } BMLogger.LogError($"Null name animation: {fullName}"); } /// /// Refresh the namedAnimancer reference when the model changes (e.g., shape change) /// 当模型更改时(例如形状更改)刷新namedAnimancer引用 /// /// The root GameObject of the model to search for NamedAnimancerComponent / 要搜索NamedAnimancerComponent的模型根GameObject public void RefreshNamedAnimancer(GameObject modelRoot = null) { if (modelRoot != null) { // Search specifically within the model GameObject's hierarchy // 在模型GameObject的层次结构中搜索 namedAnimancer = modelRoot.GetComponentInChildren(); } else { // Fallback to searching from this component's hierarchy // 回退到从此组件的层次结构搜索 namedAnimancer = GetComponentInChildren(); } if (namedAnimancer == null) { BMLogger.LogWarning($"PlayerVisual: RefreshNamedAnimancer - namedAnimancer == null after refresh (modelRoot: {modelRoot?.name ?? "null"})"); } else { BMLogger.Log($"PlayerVisual: RefreshNamedAnimancer - Successfully refreshed namedAnimancer from model: {modelRoot?.name ?? "default"}"); } } } }