From 936dbb7839adac85b73dfe9e7e1cec880918fd23 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Wed, 6 May 2026 17:46:38 +0700 Subject: [PATCH] Change animation logic to using channer act --- Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 22 +- Assets/PerfectWorld/Scripts/NPC/CECModel.cs | 447 ++++++++++++++---- Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs | 7 +- Assets/Scripts/PlayerVisual.cs | 15 +- 4 files changed, 401 insertions(+), 90 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index c2b9ba5fc1..8043d711de 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -24,7 +24,7 @@ using BrewMonster.Scripts.ECModel; namespace BrewMonster { - public abstract partial class CECPlayer : CECObject + public abstract partial class CECPlayer : CECObject, ITickable { [SerializeField] protected Transform parentModel; [SerializeField] protected TextMeshProUGUI txtName; @@ -360,6 +360,21 @@ namespace BrewMonster m_iShape = 0; m_aEquips = new int[(int)IndexOfIteminEquipmentInventory.SIZE_ALL_EQUIPIVTR]; queueActionEvent = new QueueActionEvent("", null, false, null, 200); + TickInvoker.Instance.RegisterTickable(this); + } + + protected virtual void OnDestroy() + { + TickInvoker.Instance.UnregisterTickable(this); + } + + public override bool Tick(uint dwDeltaTime) + { + if(m_pPlayerCECModel != null) + { + m_pPlayerCECModel.Tick(dwDeltaTime); + } + return true; } /// This function will get the coressponding model player for the player based on the profession and gender @@ -1336,6 +1351,11 @@ namespace BrewMonster } return PlayActionWithConfig(iAction, m_PlayerActions[iAction], bRestart, iTransTime, bQueue); } + + + const int COMACT_FLAG_MODE_NONE = 0; + const int COMACT_FLAG_MODE_ONCE = 1; + const int COMACT_FLAG_MODE_ONCE_IGNOREGFX = 2; const int COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX = 3; private bool PlayActionWithConfig(int iAction, in PLAYER_ACTION actionConfig, bool bRestart = true, int iTransTime = 200, bool bQueue = false) diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs index f3db65e1b6..b341406efa 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -13,6 +13,7 @@ using ECModelHookMap = System.Collections.Generic.Dictionary; using ActQueue = System.Collections.Generic.List; using BrewMonster; using BrewMonster.Scripts.ECModel; +using Cysharp.Threading.Tasks; using Unity.VisualScripting.FullSerializer; public enum ECMScript { @@ -54,13 +55,243 @@ public enum ActionChannel }; public class A3DCombActDynData { - public A3DCombinedAction combinedAction; - public int nChannel; - public int m_dwUserData; - public bool IsAllActionFinished => m_EventNames.Count == 0; - public List m_EventNames = new List(); + protected A3DCombinedAction m_pAct; + protected CECModel m_pECModel; + protected int m_dwTimeSpan; + protected float m_fWeight; + protected int m_nTransTime; + protected int m_dwUserData; + protected bool m_bAbsTrack; + protected bool m_bNoFx; + //ALISTPOSITION m_CurEventPos; + //const ChildActInfo* m_pParentInfo; + //CECModel* m_pParentModel; + protected int m_dwDeltaTime; + protected bool m_bStopPrevAct; + //important + //FxBindBaseList m_ActFxArray; + protected int m_nChannel; + + protected int m_dwEventMask; + //ActDynArray m_arrActLoopNum; + protected int m_nCurActIndex; + protected int m_dwDynComActSpan; + protected bool m_bIsInfiniteComAct; + + //important + //EventInfoMap m_mapDynAddedInfo; + + /// Attack event tied to this combined action, if any. / 与此组合动作绑定的攻击事件(若有) + /// this is alternative for m_IdCaster to m_IsAttackAct in c++ + /// + public CECAttackEvent ActiveAttackEvent; + + public bool m_bResetPSWhenActionStop; + public bool m_bSetSpeedWhenActStart; + public float m_fModelScale; + + + public List m_ActionNames = new List(); + public List m_SFXNames = new List(); + public bool IsActionStopped() { return m_ActionNames.Count == 0; } + /// True when there is no attack or damage has been applied. / 无攻击事件或已造成伤害则为 true + public bool IsAllEventFinished() { return (ActiveAttackEvent == null || ActiveAttackEvent.m_bDoDamaged); } + /// Animations finished and attack-event phase finished. / 动画与攻击事件阶段均结束 + public bool IsAllFinished() + { + if(IsActionStopped() && IsAllEventFinished()) + { + return true; + } + return false; + } + public A3DCombActDynData(A3DCombinedAction pAct, CECModel pECModel, CECAttackEvent attackEvent) + { + m_pAct = pAct; + m_pECModel = pECModel; + // m_nCurLoop = 0; + // m_nCurActLoop = 0; + m_dwTimeSpan = 0; + m_fWeight = 1.0f; + m_nTransTime = 200; + m_dwUserData = 0; + m_bAbsTrack = false; + m_bNoFx = false; + // m_CurEventPos = 0; + // m_pParentInfo = 0; + // m_pParentModel = 0; + m_bStopPrevAct = false; + m_dwDeltaTime = 0; + m_nChannel = 0; + m_dwEventMask = -1; + //use active attack event instead of idCaster to m_IsAttackAct + ActiveAttackEvent = attackEvent; + // m_idCaster = 0; + // m_idCastTarget = 0; + // m_ptFixed = 0; + // m_SerialId = 0; + // m_IsAttackAct = false; + m_nCurActIndex = -1; + m_dwDynComActSpan = 0; + m_bIsInfiniteComAct = false; + m_bResetPSWhenActionStop = false; + m_bSetSpeedWhenActStart = false; + m_fModelScale = 1.0f; + var actInfoList = pAct.m_ActLst; + // { + // ActInfoList& actInfoList = pAct->m_ActLst; + // ALISTPOSITION pos = actInfoList.GetHeadPosition(); + // while (pos) + // { + // PACTION_INFO pAct = actInfoList.GetNext(pos); + // int nDynLoopNum = pAct->CalcLoopNum(); + // m_arrActLoopNum.Add(ACTIONDYN_DATA(nDynLoopNum, pAct)); + // if (!m_bIsInfiniteComAct && nDynLoopNum < 0) m_bIsInfiniteComAct = true; + // } + // CalcDynComActSpan(); + // } + } + + /// + /// When true, publishes for Animancer, tracks , + /// and plays combined-action sound FX from data (unless suppresses audio). + /// / 为 true 时发布 PlayActionEvent、维护 m_EventNames,并按数据播放组合动作音效(bNoFx 时跳过音效)。 + /// + public void Play(int nChannel, float fWeight, int nTransTime, int dwEventMask, bool bRestart, bool bAbsTrack, bool bNoFx, + bool bForceStopPrevious = false, byte channelRank = 0) + { + m_nChannel = nChannel; + m_fWeight = fWeight; + m_nTransTime = nTransTime; + m_dwEventMask = dwEventMask; + m_bAbsTrack = bAbsTrack; + m_bNoFx = bNoFx; + + // if (bRestart) + // m_nCurLoop = 0; + + m_fModelScale = 1; //Vm_pECModel.GetAllRootBonesScale(); + + PublishVisual(bForceStopPrevious, channelRank); + if (!m_bNoFx) + TriggerSFxFromEventList(); + // Resume(); + // UpdateAct(0); + // #ifdef _ANGELICA21 + // UpdateEvent(0, 0); + // #endif + } + + /// + /// Notify visual layer and fire combined-action audio using data on this instance. + /// / 通知表现层并根据本实例数据触发组合动作音效。 + /// + void PublishVisual(bool bForceStopPrevious, byte channelRank) + { + if (m_pECModel == null || m_pAct == null) + return; + + var actionInfos = m_pAct.m_ActLst; + if (actionInfos != null && actionInfos.Count > 0 && !string.IsNullOrEmpty(actionInfos[0].m_strName)) + { + ChannelAct channelAct = m_pECModel.GetChannelAct(m_nChannel); + if (channelAct != null) + { + bool isLoop = m_pAct.m_nLoops == 1; + int ownerId = m_pECModel.GetId(); + EventBus.PublishChannel(ownerId, new PlayActionEvent(ref channelAct, actionInfos[0].m_strName, m_nTransTime, bForceStopPrevious, ActiveAttackEvent, isLoop, channelRank)); + m_ActionNames.Add(actionInfos[0].m_strName); + for(int i = 1; i < actionInfos.Count; i++) + { + EventBus.PublishChannelClass(ownerId, new QueueActionEvent(ref channelAct, actionInfos[i].m_strName, null, false, null, m_nTransTime, false, isLoop, channelRank)); + m_ActionNames.Add(actionInfos[i].m_strName); + } + } + } + } + + /// + /// Sound events embedded in combined action; skip gfx paths. / 组合动作内嵌音效事件;跳过 gfx 路径。 + /// + void TriggerSFxFromEventList() + { + if (m_bNoFx || m_pAct == null) + return; + + var eventInfoList = m_pAct.m_EventInfoLst; + if (eventInfoList == null || eventInfoList.Count == 0) + return; + + foreach (var eventInfo in eventInfoList) + { + if (eventInfo is FX_BASE_INFO sfx) + { + if (sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) + { + string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0], true); + if (soundpath.Contains("gfx")) + continue; + soundpath = soundpath.ToLower(); + m_SFXNames.Add(soundpath); + try + { + SFXManager.Instance + .PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); + } + finally + { + // remove AFTER sound finished + m_SFXNames.Remove(soundpath); + } + } + } + } + } + public void Stop(bool bStopAct, bool bForceStopFx = false ) + { + // FlushDamageInfo(); + + // ClearParentInfo(); + // m_nCurLoop = 0; + // m_nCurActLoop = 0; + + // RemoveAllActiveFx(bForceStopFx); + // A3DSkinModel* pSkinModel = m_pECModel->GetA3DSkinModel(); + + // m_pECModel->GetStaticData()->OnScriptEndAction(m_pECModel, m_nChannel, m_pAct->GetName()); + + // if (IsResetPSWhenActStop()) + // { + // m_pECModel->RemoveReplaceShader(); + // SetResetPSWhenActStop(false); + // } + + // if (m_pAct->GetResetMaterialScale()) + // m_pECModel->ResetMaterialScale(); + + // if (m_pAct->GetStopChildrenAct()) + // m_pECModel->StopChildrenAct(); + + // if (pSkinModel == NULL || !bStopAct) + // return; + + // A3DSMActionChannel* pChannel = pSkinModel->GetActionChannel(m_nChannel); + + // if (pChannel) + // pChannel->StopAction(m_pAct->GetRank(m_nChannel)); + } + public void SetUserData(int dwUserData) { m_dwUserData = dwUserData; } + public void SetTransTime(int nTransTime) { m_nTransTime = nTransTime; } + public void SetStopPrevAct(bool bStopPrevAct) { m_bStopPrevAct = bStopPrevAct; } + public void SetNoFxFlag(bool bNoFx) { m_bNoFx = bNoFx; } + public void SetSpeedWhenActStart(bool bResetSpeed) { m_bSetSpeedWhenActStart = bResetSpeed; } + public bool GetStopPrevAct() { return m_bStopPrevAct; } public int GetUserData() { return m_dwUserData; } + public int GetTransTime() { return m_nTransTime; } + public bool GetNoFxFlag() { return m_bNoFx; } + public bool IsSetSpeedWhenActStart() { return m_bSetSpeedWhenActStart; } + public A3DCombinedAction GetComAct() { return m_pAct; } } public class ChannelActNode { @@ -68,22 +299,22 @@ public class ChannelActNode public A3DCombActDynData m_pActive; public bool m_pActFlag; public int m_dwFlagMode; - public ActQueue m_ActQueue; + public ActQueue m_QueuedActs; public ChannelActNode() { m_Rank = 0; m_pActive = null; m_pActFlag = false; m_dwFlagMode = 0; - if(m_ActQueue == null) + if(m_QueuedActs == null) { - m_ActQueue = new ActQueue(); + m_QueuedActs = new ActQueue(); } - m_ActQueue.Clear(); + m_QueuedActs.Clear(); } public void RemoveQueuedActs() { - m_ActQueue.Clear(); + m_QueuedActs.Clear(); } } public class ChannelAct @@ -478,14 +709,27 @@ public class CECModel private int m_nId = 0; public GameObject m_pPlayerModel; private const uint COMACT_FLAG_MODE_NONE = 0; + private const uint COMACT_FLAG_MODE_ONCE_IGNOREGFX = 2; + private const uint COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX = 3; protected CECModelStaticData m_pMapModel = new CECModelStaticData(); public SkeletonBuilder m_skeletonBuilder; private Dictionary m_hookCache = new Dictionary(); private Dictionary m_hangerPositionCache = new Dictionary(); private Dictionary m_childModels = new Dictionary(); private Transform m_transform; + private bool m_bAbsTrack = false; public bool InheritParentId() => m_bInheritParentId; public void SetId(int nId) => m_nId = nId; + public int GetId() => m_nId; + + /// Channel playback container for combined actions. / 组合动作通道容器。 + public ChannelAct GetChannelAct(int nChannel) + { + if (nChannel < 0 || nChannel >= m_ChannelActs.Length) + return null; + return m_ChannelActs[nChannel]; + } + //16 is ChannelAct[] m_ChannelActs = InitChannelActs(); private static ChannelAct[] InitChannelActs() @@ -783,54 +1027,19 @@ public class CECModel { return false; } - var actionInfos = combinedAction.m_ActLst; - var isLoop = combinedAction.m_nLoops == 1; - var node = m_ChannelActs[nChannel].GetNodeByRank(0); - if (node == null) + var pNode = m_ChannelActs[nChannel].GetNodeByRank(0); + if (pNode == null) { - node = new ChannelActNode { m_Rank = 0 }; - m_ChannelActs[nChannel].m_RankNodes.Add(node); - } - var actData = new A3DCombActDynData - { - combinedAction = combinedAction, - nChannel = nChannel, - m_dwUserData = (int)dwUserData - }; - EventBus.PublishChannel(m_nId, new PlayActionEvent(ref m_ChannelActs[nChannel], actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent, isLoop, node.m_Rank)); - actData.m_EventNames.Add(actionInfos[0].m_strName); - - for(int i = 1; i < actionInfos.Count; i++) - { - EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop, node.m_Rank)); - actData.m_EventNames.Add(actionInfos[i].m_strName); + pNode = new ChannelActNode { m_Rank = 0 }; + m_ChannelActs[nChannel].m_RankNodes.Add(pNode); } + pNode.m_pActive = new A3DCombActDynData(combinedAction, this, attackEvent); + pNode.m_pActive.SetUserData((int)dwUserData); + pNode.m_pActive.Play(nChannel, fWeight, nTransTime, 0, bRestart, m_bAbsTrack, bNoFx, bForceStop,pNode.m_Rank); + m_bAbsTrack = false; + pNode.m_pActFlag = false; + pNode.m_dwFlagMode = 2;//COMACT_FLAG_MODE_ONCE_IGNOREGFX; - node.m_pActive = actData; - - //Todo: should move those event logic to channel act because their control the animation life span. - var eventInfoList = combinedAction.m_EventInfoLst; - if(eventInfoList != null && eventInfoList.Count > 0) - { - foreach(var eventInfo in eventInfoList) - { - //0 is sound event - if (eventInfo is FX_BASE_INFO sfx) - { - if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) - { - string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0],true); - //we need to determine sfx and gfx. now we dont have logic for this path - if(soundpath.Contains("gfx")) - { - continue; - } - soundpath = soundpath.ToLower(); - SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); - } - } - } - } return true; } public A3DCombinedAction GetComActByName(string szActName) @@ -847,28 +1056,28 @@ public class CECModel { return false; } - A3DCombinedAction combinedAction = GetComActByName(szActName); - if(combinedAction == null) - { + A3DCombinedAction pComAct = GetComActByName(szActName); + + if (pComAct == null) return false; - } - var actionInfos = combinedAction.m_ActLst; - var isLoop = combinedAction.m_nLoops == 1; - var node = new ChannelActNode { m_Rank = (byte)m_ChannelActs[nChannel].m_RankNodes.Count}; - m_ChannelActs[nChannel].m_RankNodes.Add(node); - var actData = new A3DCombActDynData - { - // combinedAction = combinedAction, // not needed now - nChannel = nChannel, - m_dwUserData = (int)dwUserData - }; - EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct, isLoop, node.m_Rank)); - for(int i = 1; i < actionInfos.Count; i++) - { - EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop, node.m_Rank)); - actData.m_EventNames.Add(actionInfos[i].m_strName); - } - node.m_pActive = actData; + + ChannelAct ca = m_ChannelActs[nChannel]; + ChannelActNode pNode = ca.GetNodeByRank(0); + + A3DCombActDynData pDynData = new A3DCombActDynData(pComAct, this, attackEvent); + pDynData.SetUserData((int)dwUserData); + pDynData.SetTransTime(nTransTime); + pDynData.SetStopPrevAct(bForceStopPrevAct); + pDynData.SetNoFxFlag(bNoFx); + pDynData.SetSpeedWhenActStart(bResetSpeed); + pNode.m_QueuedActs.Add(pDynData); + + // EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct, isLoop, node.m_Rank)); + // for(int i = 1; i < actionInfos.Count; i++) + // { + // EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop, node.m_Rank)); + // actData.m_EventNames.Add(actionInfos[i].m_strName); + // } //todo: should add sfx and gfx logic to channel act. currently we dont have logic for queue action. only available for play action. return true; } @@ -880,11 +1089,12 @@ public class CECModel return false; } - public int GetCurActionUserData() + public int GetCurActionUserData(int channel = 0) { var returnValue = -1; - if (m_ChannelActs[0].GetHighestRankNode() != null) - returnValue = m_ChannelActs[0].GetHighestRankNode().m_pActive.GetUserData(); + var nodeCh0 = m_ChannelActs[channel].GetHighestRankNode(); + if (nodeCh0 != null) + returnValue = nodeCh0.m_pActive.GetUserData(); return returnValue; } public bool QueueAction(CECNPC.INFO iNFO, string szActName, ref bool pNewActFlag, int nTransTime = 200, uint dwUserData = 0, bool bForceStopPrevAct = false, bool bCheckTailDup = false, bool bNoFx = false, bool bResetSpeed = false @@ -911,6 +1121,85 @@ public class CECModel AnimationName = animationName; } } + public bool Tick(uint dwDeltaTime) + { + UpdateChannelActs(dwDeltaTime); + return true; + } + void UpdateChannelActs(uint dwUpdateTime) + { + if (dwUpdateTime == 0) + return; + + for (int i = 0; i < (int)ActionChannel.ACTCHA_MAX; i++) + { + ChannelAct ca = m_ChannelActs[i]; + var rankNodes = ca.m_RankNodes; + for (int n = rankNodes.Count - 1; n >= 0; n--) + { + var node = rankNodes[n]; + bool bActFinished = false; + if (node.m_dwFlagMode == COMACT_FLAG_MODE_ONCE_IGNOREGFX || node.m_dwFlagMode == COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX) + bActFinished = node.m_pActive.IsActionStopped(); + else + bActFinished = node.m_pActive.IsAllFinished(); + + if (bActFinished) + { + if (node.m_dwFlagMode != COMACT_FLAG_MODE_NONE && node.m_pActFlag) + { + node.m_pActFlag = true; + + if (node.m_dwFlagMode != COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX) + node.m_dwFlagMode = (int)COMACT_FLAG_MODE_NONE; + + node.m_pActFlag = false; + } + if (node.m_QueuedActs.Count > 0) + { + A3DCombActDynData pNext = node.m_QueuedActs[0]; + node.m_QueuedActs.RemoveAt(0); + + if (pNext.GetStopPrevAct()) + { + node.m_pActive.Stop(true); + //StopChildrenAct(); + } + else + node.m_pActive.Stop(false); + + // script + //m_pMapModel->OnScriptPlayAction(this, i, pNext->GetComAct()->GetName()); + + // Èç¹ûQueueActionµÄʱºòÉèÖÃÁËResetSpeed£¬ÔòÕâÀォËÙ¶ÈÉèÖÃÖØÖã¬Ä¬ÈÏÇé¿öϸñê־Ϊfalse + //by2021 ÕâÀï¿ÉÐÞ¸Ä + if (pNext.IsSetSpeedWhenActStart()) + SetPlaySpeedByChannel(i, pNext.GetComAct()); + + node.m_pActive = null; + node.m_pActive = pNext; + node.m_pActive.Play(i, 1f, node.m_pActive.GetTransTime(), 0/**m_EventMasks[i]*/, true, m_bAbsTrack, pNext.GetNoFxFlag()); + //pNext.UpdateAct(dwUpdateTime); + m_bAbsTrack = false; + } + else + { + node.m_pActive.Stop(false); + rankNodes.RemoveAt(n); + } + } + // else + // node.m_pActive.UpdateAct(dwUpdateTime); + } + } + } + void SetPlaySpeedByChannel(int nChannel, A3DCombinedAction pComAct) + { + // if (m_nMainChannel == nChannel || 0 == nChannel) + // { + // m_fPlaySpeed = m_fDefPlaySpeed * pComAct.GetPlaySpeed(); + // } + } public void PlayGfx(string szPath, string szHook, float fScale, bool bFadeOut, A3DVECTOR3 vOffset, float fPitch, float fYaw, float fRot, bool bUseECMHook, uint dwFadeOutTime) { if (!bFadeOut) diff --git a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs index 894f70cc04..a7e2754672 100644 --- a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs +++ b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs @@ -27,8 +27,11 @@ public class NPCVisual : MonoBehaviour // BMLogger.LogMono(this, "HoangDev: namedAnimancerIsPlaying == null1: " + animationName); _currentState = namedAnimancer.TryPlay(animationName, fadeTime); if (isHit) - { - _currentState.Events.OnEnd = () => SetHitOnEnd(cECAttackEvent); + { + if(_currentState != null) + { + _currentState.Events.OnEnd = () => SetHitOnEnd(cECAttackEvent); + } } // if (_currentState != null) // BMLogger.LogMono(this, "HoangDev: _currentState != null1: " + _currentState.Clip.name); diff --git a/Assets/Scripts/PlayerVisual.cs b/Assets/Scripts/PlayerVisual.cs index e79b9b083e..b9b52a7665 100644 --- a/Assets/Scripts/PlayerVisual.cs +++ b/Assets/Scripts/PlayerVisual.cs @@ -59,7 +59,7 @@ namespace BrewMonster } previousAnimationName = @event.AnimationName; InternalPlayAnimation(@event.AnimationName, @event.ITransTime, FadeMode, @event.IsLoop); - ApplyAnimationEndCallbacks(@event.AttackEvent, @event.ChannelAct, @event.Rank); + ApplyAnimationEndCallbacks(@event.AttackEvent, @event.ChannelAct, @event.Rank, @event.AnimationName); } public void InitPlayerEventDoneHandler() { @@ -228,21 +228,20 @@ namespace BrewMonster _animationList = _animationQueue.Select(q => q.AnimationName).ToList(); previousAnimationName = animationQueue.AnimationName; InternalPlayAnimation(animationQueue.AnimationName, animationQueue.ITransTime, FadeMode, animationQueue.IsLoop); - ApplyAnimationEndCallbacks(animationQueue.AttackEvent, animationQueue.ChannelAct, animationQueue.Rank); + ApplyAnimationEndCallbacks(animationQueue.AttackEvent, animationQueue.ChannelAct, animationQueue.Rank, animationQueue.AnimationName); } - private void ApplyAnimationEndCallbacks(CECAttackEvent attackEvent, ChannelAct channelAct, int rank) + private void ApplyAnimationEndCallbacks(CECAttackEvent attackEvent, ChannelAct channelAct, int rank, string animationName) { if (_currentState == null) return; _currentState.Events.OnEnd = () => { if (attackEvent != null) attackEvent.m_bSignaled = true; - channelAct.GetNodeByRank((byte)rank).m_pActive.m_EventNames.Remove(_currentAnimationName); - if(channelAct.GetNodeByRank((byte)rank).m_pActive.IsAllActionFinished) - { - //channelAct.RemoveNodeByRank((byte)rank); - } + if (channelAct == null || string.IsNullOrEmpty(animationName)) + return; + var node = channelAct.GetNodeByRank((byte)rank); + node?.m_pActive?.m_ActionNames?.Remove(animationName); }; } void ApplyDamage()