From d2aa2c8001603128e527bec468a83deb4eafc7f2 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Wed, 20 May 2026 15:16:24 +0700 Subject: [PATCH] FIx bug animation not stop. add more QOL for test animation scene. add AnimationSceneSkillTest.md to show how to use --- .../AssetGroups/gfx.asset | 25 + Assets/Jianghu/Meshes/GFX_Ring.fbx | Bin 0 -> 57984 bytes Assets/Jianghu/Meshes/GFX_Ring.fbx.meta | 107 + .../gfx/人物/技能/妖兽/化血成魔释放.prefab | 29139 +++++++++++++++ .../人物/技能/妖兽/化血成魔释放.prefab.meta | 7 + .../gfx/人物/技能/妖兽/化血成魔预备.prefab | 29237 ++++++++++++++++ .../人物/技能/妖兽/化血成魔预备.prefab.meta | 7 + .../gfx/人物/技能/妖兽/化血成魔预备1.prefab | 14503 ++++++++ .../人物/技能/妖兽/化血成魔预备1.prefab.meta | 7 + .../人物/技能/妖兽/化血成魔预备施放起.prefab | 29237 ++++++++++++++++ .../技能/妖兽/化血成魔预备施放起.prefab.meta | 7 + .../人物/技能/妖兽/化血成魔预备施放起1.prefab | 14503 ++++++++ .../技能/妖兽/化血成魔预备施放起1.prefab.meta | 7 + .../Art/Materials/GFX/化血成魔释放.meta | 8 + .../Art/Materials/GFX/化血成魔释放/zsj7.mat | 209 + .../Materials/GFX/化血成魔释放/zsj7.mat.meta | 8 + .../GFX/化血成魔释放/天地无级内圈.mat | 209 + .../GFX/化血成魔释放/天地无级内圈.mat.meta | 8 + .../Art/Materials/GFX/化血成魔预备.meta | 8 + .../Materials/GFX/化血成魔预备/circle027.mat | 209 + .../GFX/化血成魔预备/circle027.mat.meta | 8 + .../GFX/化血成魔预备/purpletelep.mat | 209 + .../GFX/化血成魔预备/purpletelep.mat.meta | 8 + .../Art/Textures/gfx/circle027.dds | Bin 0 -> 2872 bytes .../Art/Textures/gfx/circle027.dds.meta | 21 + .../Art/Textures/gfx/purpletelep.dds | Bin 0 -> 11064 bytes .../Art/Textures/gfx/purpletelep.dds.meta | 21 + .../ModelRenderer/Art/Textures/gfx/zsj7.dds | Bin 0 -> 11064 bytes .../Art/Textures/gfx/zsj7.dds.meta | 21 + Assets/PerfectWorld/Scene/AnimationTest.unity | 4 +- .../Scripts/Addressable/AddressableManager.cs | 24 +- .../PerfectWorld/Scripts/AnimTestScene.meta | 8 + .../AnimScenePlayerBootstrap.cs | 1 - .../AnimScenePlayerBootstrap.cs.meta | 0 .../Scripts/AnimTestScene/CopyTextButton.cs | 28 + .../AnimTestScene/CopyTextButton.cs.meta | 2 + .../AnimTestScene/LogPanelAnimeScene.cs | 217 + .../AnimTestScene/LogPanelAnimeScene.cs.meta | 2 + .../Scripts/Debug/SkillTriggerPanel.cs | 14 +- Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 148 + Assets/PerfectWorld/Scripts/NPC/CECModel.cs | 3 +- .../Scripts/_Doc/AnimationSceneEditor.md | 4 +- .../Scripts/_Doc/AnimationSceneSetup.md | 2 + .../Scripts/_Doc/AnimationSceneSkillTest.md | 188 + Assets/Scripts/PlayerVisual.cs | 5 +- 45 files changed, 118360 insertions(+), 23 deletions(-) create mode 100644 Assets/Jianghu/Meshes/GFX_Ring.fbx create mode 100644 Assets/Jianghu/Meshes/GFX_Ring.fbx.meta create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔释放.prefab create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔释放.prefab.meta create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备.prefab create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备.prefab.meta create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备1.prefab create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备1.prefab.meta create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备施放起.prefab create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备施放起.prefab.meta create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备施放起1.prefab create mode 100644 Assets/ModelRenderer/Art/Gfx/gfx/人物/技能/妖兽/化血成魔预备施放起1.prefab.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔释放.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔释放/zsj7.mat create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔释放/zsj7.mat.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔释放/天地无级内圈.mat create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔释放/天地无级内圈.mat.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔预备.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔预备/circle027.mat create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔预备/circle027.mat.meta create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔预备/purpletelep.mat create mode 100644 Assets/ModelRenderer/Art/Materials/GFX/化血成魔预备/purpletelep.mat.meta create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/circle027.dds create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/circle027.dds.meta create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/purpletelep.dds create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/purpletelep.dds.meta create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/zsj7.dds create mode 100644 Assets/ModelRenderer/Art/Textures/gfx/zsj7.dds.meta create mode 100644 Assets/PerfectWorld/Scripts/AnimTestScene.meta rename Assets/PerfectWorld/Scripts/{ => AnimTestScene}/AnimScenePlayerBootstrap.cs (99%) rename Assets/PerfectWorld/Scripts/{ => AnimTestScene}/AnimScenePlayerBootstrap.cs.meta (100%) create mode 100644 Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs create mode 100644 Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs create mode 100644 Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSkillTest.md diff --git a/Assets/AddressableAssetsData/AssetGroups/gfx.asset b/Assets/AddressableAssetsData/AssetGroups/gfx.asset index 17bf710b31..7eaebb4b65 100644 --- a/Assets/AddressableAssetsData/AssetGroups/gfx.asset +++ b/Assets/AddressableAssetsData/AssetGroups/gfx.asset @@ -505,6 +505,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 537aaa290c1d9b540bbd480a617d9929 + m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u5996\u517D/\u5316\u8840\u6210\u9B54\u9884\u5907.gfx" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 539710a69297f704e9a0d1c1cdda6390 m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u6B66\u4FA0/\u4E91\u9F99\u4E5D\u73B01.gfx" m_ReadOnly: 0 @@ -660,6 +665,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 6dad60b1e8f9feb4bbbb7a09443d6f58 + m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u5996\u517D/\u5316\u8840\u6210\u9B54\u9884\u5907\u65BD\u653E\u8D77.gfx" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 6e10cdd12acec704bbe95ee569e4b41b m_Address: "gfx/\u7B56\u5212\u8054\u5165/\u4EBA\u7269\u6280\u80FD/\u51FB\u4E2D/\u7FBD\u7BAD\u51FB\u4E2D.gfx" m_ReadOnly: 0 @@ -830,6 +840,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 90589782f70dd234b970ee3dfd8ac37a + m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u5996\u517D/\u5316\u8840\u6210\u9B54\u9884\u59071.gfx" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 90b73df583cd78c458a92d5c3d55a136 m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u6CD5\u5E08/\u6C99\u9677\u9884\u59072.gfx" m_ReadOnly: 0 @@ -1110,6 +1125,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: c1ba74f0f1bae7f4eb8a96fc7319ebc1 + m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u5996\u517D/\u5316\u8840\u6210\u9B54\u91CA\u653E.gfx" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: c2bfba6b27255834b805caffd1e7db8b m_Address: "gfx/\u7B56\u5212\u8054\u5165/\u4EBA\u7269\u6280\u80FD/\u51FB\u4E2D/\u5BF8\u529B\u51FB\u4E2D.gfx" m_ReadOnly: 0 @@ -1380,6 +1400,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: fa805bed635aea543bb135ef5456bb6d + m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u5996\u517D/\u5316\u8840\u6210\u9B54\u9884\u5907\u65BD\u653E\u8D771.gfx" + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: fa9f8176eb7b40c428689a5a3f528931 m_Address: "gfx/\u4EBA\u7269/\u6280\u80FD/\u6B66\u4FA0/\u9F99\u73B0\u9884\u5907.gfx" m_ReadOnly: 0 diff --git a/Assets/Jianghu/Meshes/GFX_Ring.fbx b/Assets/Jianghu/Meshes/GFX_Ring.fbx new file mode 100644 index 0000000000000000000000000000000000000000..fd4e454d96bacb1facf9c33c399c1257ab793775 GIT binary patch literal 57984 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 N6GW6%-F!0``T+-aHI4uP literal 0 HcmV?d00001 diff --git a/Assets/ModelRenderer/Art/Textures/gfx/circle027.dds.meta b/Assets/ModelRenderer/Art/Textures/gfx/circle027.dds.meta new file mode 100644 index 0000000000..0607e18c8b --- /dev/null +++ b/Assets/ModelRenderer/Art/Textures/gfx/circle027.dds.meta @@ -0,0 +1,21 @@ +fileFormatVersion: 2 +guid: 34c57316d248ac544b9f88d1c7f678ce +IHVImageFormatImporter: + externalObjects: {} + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + isReadable: 0 + sRGBTexture: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + ignoreMipmapLimit: 0 + mipmapLimitGroupName: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ModelRenderer/Art/Textures/gfx/purpletelep.dds b/Assets/ModelRenderer/Art/Textures/gfx/purpletelep.dds new file mode 100644 index 0000000000000000000000000000000000000000..7e1a099a0b82066eb6b82a0d461871c4086338d8 GIT binary patch literal 11064 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 literal 0 HcmV?d00001 diff --git a/Assets/ModelRenderer/Art/Textures/gfx/zsj7.dds.meta b/Assets/ModelRenderer/Art/Textures/gfx/zsj7.dds.meta new file mode 100644 index 0000000000..b481d5330b --- /dev/null +++ b/Assets/ModelRenderer/Art/Textures/gfx/zsj7.dds.meta @@ -0,0 +1,21 @@ +fileFormatVersion: 2 +guid: 495b63cf335ed554bb329f12cfc4aab5 +IHVImageFormatImporter: + externalObjects: {} + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + isReadable: 0 + sRGBTexture: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + ignoreMipmapLimit: 0 + mipmapLimitGroupName: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scene/AnimationTest.unity b/Assets/PerfectWorld/Scene/AnimationTest.unity index ce2ed17328..5855602aaf 100644 --- a/Assets/PerfectWorld/Scene/AnimationTest.unity +++ b/Assets/PerfectWorld/Scene/AnimationTest.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11f16ce7ddd16e5c3de941b56358deb495efb2d5d4783c8d59c75aab0dad1975 -size 92966 +oid sha256:4370d12d63b3f482d2c5f38066604b3ba3cd462fa451d6ec7fe2f81b43614f82 +size 124399 diff --git a/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs b/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs index 394eb1ccb8..4e3a45eb2a 100644 --- a/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs +++ b/Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs @@ -493,25 +493,27 @@ namespace BrewMonster.Scripts /// True if the key exists, false otherwise. public static bool KeyExists(object key, System.Type type = null) { + string keyStr = key?.ToString(); + if (string.IsNullOrEmpty(keyStr)) + return false; + #if UNITY_EDITOR - return !Instance._invalidAssetPaths.Contains(key.ToString()); - #else - // Iterate through all loaded locators (catalogs) + // Failed loads are cached; still verify against catalog (do not assume unknown keys exist). + if (Instance != null && Instance._invalidAssetPaths.Contains(keyStr)) + return false; + #endif + foreach (IResourceLocator locator in Addressables.ResourceLocators) { - // If the locator finds the key, it returns true - if (locator.Locate(key, type, out var locations)) + if (locator.Locate(key, type, out var locations) + && locations != null + && locations.Count > 0) { - // Double check that it actually yielded at least one location - if (locations != null && locations.Count > 0) - { - return true; - } + return true; } } return false; - #endif } #endregion diff --git a/Assets/PerfectWorld/Scripts/AnimTestScene.meta b/Assets/PerfectWorld/Scripts/AnimTestScene.meta new file mode 100644 index 0000000000..14a811186e --- /dev/null +++ b/Assets/PerfectWorld/Scripts/AnimTestScene.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c5c28e526fd689b4ba33afac281ab524 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/AnimScenePlayerBootstrap.cs b/Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs similarity index 99% rename from Assets/PerfectWorld/Scripts/AnimScenePlayerBootstrap.cs rename to Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs index 2c5107a173..f2733ff214 100644 --- a/Assets/PerfectWorld/Scripts/AnimScenePlayerBootstrap.cs +++ b/Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs @@ -38,7 +38,6 @@ namespace PerfectWorld.Scripts [Range(0, 10)] private int activeCameraPos; - [Header("Initial Role")] [SerializeField] private Profession profession = Profession.PROF_WARRIOR; diff --git a/Assets/PerfectWorld/Scripts/AnimScenePlayerBootstrap.cs.meta b/Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs.meta similarity index 100% rename from Assets/PerfectWorld/Scripts/AnimScenePlayerBootstrap.cs.meta rename to Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs.meta diff --git a/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs b/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs new file mode 100644 index 0000000000..ad32668559 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs @@ -0,0 +1,28 @@ +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +public class CopyTextButton : MonoBehaviour +{ + [SerializeField] private TMP_Text textToCopy; + [SerializeField] private TMP_Text labelText; + [SerializeField] Button button; + public void Awake() + { + button.onClick.AddListener(CopyText); + } + public void CopyText() + { + GUIUtility.systemCopyBuffer = textToCopy.text; + } + public void SetText(string labeltext, string text, bool? inAddressables = null) + { + labelText.text = labeltext; + textToCopy.text = text; + if (inAddressables == null) + labelText.color = Color.white; + else + labelText.color = inAddressables.Value ? Color.green : Color.red; + } + +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs.meta b/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs.meta new file mode 100644 index 0000000000..ec77b88574 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/AnimTestScene/CopyTextButton.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 475fda581c4d66c49a0bee08cfab75fe \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs b/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs new file mode 100644 index 0000000000..4f234bcfa8 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; + +using BrewMonster.Scripts; +using BrewMonster.Scripts.Skills; + +using ModelViewer.Common; + +using UnityEngine; + +namespace BrewMonster +{ + public class LogPanelAnimeScene : MonoBehaviour + { + [SerializeField] private CopyTextButton copyTextButtonPrefab; + + [SerializeField] private List copyTextButtons; + + [SerializeField] private Transform copyTextButtonsParent; + + private int currentIndex = 0; + + public void Awake() + { + copyTextButtons = new List(); + currentIndex = 0; + Reset(); + } + + public void Reset() + { + currentIndex = 0; + foreach (var copyText in copyTextButtons) + { + copyText.gameObject.SetActive(false); + } + } + + public void AddCopyTextButton(string labeltext, string text, bool? inAddressables = null) + { + if (currentIndex >= copyTextButtons.Count) + { + var copyTextButton = Instantiate(copyTextButtonPrefab, copyTextButtonsParent); + copyTextButton.gameObject.SetActive(true); + copyTextButton.SetText(labeltext, text, inAddressables); + copyTextButtons.Add(copyTextButton); + } + else + { + copyTextButtons[currentIndex].gameObject.SetActive(true); + copyTextButtons[currentIndex].SetText(labeltext, text, inAddressables); + } + + currentIndex++; + } + + /// + /// Display name: last path segment only, no gfx/ prefix, no .gfx suffix. + /// 显示名:仅最后一段,无 gfx/ 前缀、无 .gfx 后缀。 + /// + public static string GfxBasename(string path) + { + if (string.IsNullOrWhiteSpace(path)) + return null; + + path = AFile.NormalizePath(path, true); + if (path.StartsWith("gfx/", StringComparison.OrdinalIgnoreCase)) + path = path.Substring(4); + + int slash = path.LastIndexOf('/'); + string file = slash >= 0 ? path.Substring(slash + 1) : path; + if (file.EndsWith(".gfx", StringComparison.OrdinalIgnoreCase)) + file = file.Substring(0, file.Length - 4); + + return string.IsNullOrEmpty(file) ? null : file; + } + + /// + /// Candidate Addressables keys (runtime CECModel path + catalog .gfx tail). + /// + static IEnumerable GfxAddressableKeyCandidates(string rawPath) + { + if (string.IsNullOrWhiteSpace(rawPath)) + yield break; + + string path = AFile.NormalizePath(rawPath, true); + string runtimeKey = path.StartsWith("gfx/", StringComparison.OrdinalIgnoreCase) + ? path + : "gfx/" + path.TrimStart('/'); + + yield return runtimeKey; + + if (!runtimeKey.EndsWith(".gfx", StringComparison.OrdinalIgnoreCase)) + yield return runtimeKey + ".gfx"; + } + + public static bool GfxAddressableExists(string rawPath) + { + var mgr = AddressableManager.Instance; + if (mgr == null || !mgr.IsInitialized()) + return false; + + foreach (string key in GfxAddressableKeyCandidates(rawPath)) + { + if (AddressableManager.KeyExists(key, typeof(GameObject))) + return true; + } + + return false; + } + + /// + /// Has path → show row. Green if Addressables has key, red if not. No path → skip. + /// 有路径则显示:Addressables 有则绿,无则红;无路径不显示。 + /// + void AddGfxPathRow(string label, string rawPath) + { + if (string.IsNullOrWhiteSpace(rawPath)) + return; + + string displayText = GfxBasename(rawPath); + if (string.IsNullOrEmpty(displayText)) + return; + + AddCopyTextButton(label, displayText, GfxAddressableExists(rawPath)); + } + + public void LogSkillCastGfx(CECHostPlayer player, int skillId) + { + AddGfxEventsFromComAct(player?.GetSkillCastComAct(skillId), "Cast"); + } + + public void LogPlayAttackEffectGfx(CECHostPlayer player, int skillId, int section = 0) + { + if (player == null) + return; + + AddGfxEventsFromComAct(player.GetSkillAttackComActRise(skillId, section), "Attack"); + AddGfxEventsFromComAct(player.GetSkillAttackComActFall(skillId, section), "Attack"); + LogSkillStubFlyHitGfx(skillId, section); + } + + public void LogAttackEventGfx(CECAttackEvent ev) + { + if (ev == null || ev.m_idSkill == 0) + return; + + LogSkillStubFlyHitGfx(ev.m_idSkill, ev.m_nSkillSection); + } + + public void AddGfxEventsFromComAct(A3DCombinedAction comAct, string label) + { + if (string.IsNullOrEmpty(label) || comAct?.m_EventInfoLst == null) + return; + + var seen = new HashSet(); + foreach (var ev in comAct.m_EventInfoLst) + { + if (ev is not GFX_INFO gfx || gfx.m_strFilePaths == null) + continue; + + foreach (string path in gfx.m_strFilePaths) + { + if (string.IsNullOrWhiteSpace(path)) + continue; + + string displayText = GfxBasename(path); + if (string.IsNullOrEmpty(displayText) || !seen.Add(displayText)) + continue; + + AddCopyTextButton(label, displayText, GfxAddressableExists(path)); + } + } + } + + void LogSkillStubFlyHitGfx(int skillId, int section) + { + string fly = null; + string hitGrnd = null; + string hit = null; + + var atkMan = CECAttacksMan.Instance; + if (section > 0 && atkMan?.m_pMultiSkillGfxComposerMan != null) + { + var composer = atkMan.m_pMultiSkillGfxComposerMan.GetSkillGfxComposer(skillId, section); + if (composer != null) + { + fly = composer.flyGfxName; + hit = composer.hitGfxName; + hitGrnd = composer.hitGrdGfxName; + } + } + else + { + var composer = atkMan?.GetSkillGfxComposerMan()?.GetSkillGfxComposer(skillId); + if (composer != null) + { + fly = composer.flyGfxName; + hit = composer.hitGfxName; + hitGrnd = composer.hitGrdGfxName; + } + } + + if (string.IsNullOrEmpty(fly) && string.IsNullOrEmpty(hit) && string.IsNullOrEmpty(hitGrnd)) + { + var paths = ElementSkill.GetAllGFX((uint)skillId); + fly = paths.Item1; + hitGrnd = paths.Item2; + hit = paths.Item3; + } + + AddGfxPathRow("Fly", fly); + AddGfxPathRow("Hit", hit); + AddGfxPathRow("HitGrnd", hitGrnd); + } + } +} diff --git a/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs.meta b/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs.meta new file mode 100644 index 0000000000..be4bcac485 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4c02249851b66b948809cc1a2a1a9f13 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs b/Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs index 097aab9c2c..07a48ea90e 100644 --- a/Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs +++ b/Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs @@ -63,6 +63,9 @@ namespace BrewMonster [SerializeField] private bool isEvilSkillPath; [SerializeField] private int skillCatalogLevel = 1; + + [SerializeField] + private LogPanelAnimeScene logPanelAnimeScene; /// /// World position of the draggable target marker, read by CECSkillGfxMan.get_pos_by_id. @@ -455,18 +458,21 @@ namespace BrewMonster { if (player == null || skill == null) return; - + logPanelAnimeScene.Reset(); int skillId = skill.GetSkillID(); + logPanelAnimeScene.AddCopyTextButton("ID", skillId.ToString()); + + logPanelAnimeScene.LogSkillCastGfx(player, skillId); + logPanelAnimeScene.LogPlayAttackEffectGfx(player, skillId, 0); // Self-cast skills target the host; all others target the draggable marker. // 自身施法技能以主角为目标;其余技能以可拖动标记为目标。 int targetId = ResolveLocalCastTargetId(skill); - BMLogger.Log($"[SkillTriggerPanel] LocalCastSkill: skillID={skillId}, targetId={targetId}, hostCid={player.GetCharacterID()}"); - // 1. Trigger cast animation (PlaySkillCastAction resolves action name internally) // 触发施法动画(PlaySkillCastAction 内部解析动作名) bool animOk = player.PlaySkillCastAction(skillId); + if (!animOk) Debug.LogWarning($"[SkillTriggerPanel] PlaySkillCastAction returned false for skill {skillId} — animation may not play."); @@ -521,7 +527,7 @@ namespace BrewMonster ElementSkill.GetType((uint)skillId) == 2) { pAttack = CECAttacksMan.Instance.AddSkillAttack( - hostPlayer.GetPlayerInfo().cid, hostPlayer.GetPlayerInfo().cid, targetMarker.GetNPCID(), hostPlayer.GetWeaponID(), skillId, 0, 0x0200, 0); + hostPlayer.GetPlayerInfo().cid, hostPlayer.GetPlayerInfo().cid, hostPlayer.GetPlayerInfo().cid, hostPlayer.GetWeaponID(), skillId, 0, 0x0200, 0); } else { diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index 0a24e3ea40..8911b0e9de 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -22,6 +22,7 @@ using DG.Tweening.Plugins; using PerfectWorld.Scripts.Managers; using BrewMonster.Scripts.ECModel; using Animancer; +using ModelViewer.Common; namespace BrewMonster { public abstract partial class CECPlayer : CECObject, ITickable @@ -2518,6 +2519,153 @@ namespace BrewMonster //UpdateWeaponHangerPosBySkillAction(idSkill);// ¸ù¾Ý¼¼Äܲ¥·ÅµÄ¶¯×÷£¬¸ü¸ÄÎäÆ÷Ðü¹ÒλÖã¨Ö»Äܼì²é²»ÅŶӶ¯×÷£© return true; } + + /// + /// Resolves cast (吟唱) combined-action name for anim-test / debug. Does not call or alter . + /// 解析吟唱组合动作名(动画测试用),不调用、不改变 PlaySkillCastAction。use for test animtion. + /// + public bool TryBuildSkillCastActionName(int idSkill, out string szAct) + { + szAct = string.Empty; + int weapon_type = GetShowingWeaponType(); + int moveEnv = GetMoveEnv(); + + if (!_default_skill_actions.TryGetValue((uint)idSkill, out PLAYER_ACTION_INFO_CONFIG data) || data.action_prefix == null || data.action_prefix.Length == 0 || data.action_prefix[0] == 0) + { + if (ElementSkill.GetCommonCoolDown((uint)idSkill) > 1 << 4) + { + data = m_PlayerActions[(int)PLAYER_ACTION_TYPE.ACT_USING_TARGET_ITEM].data; + + if (data.action_prefix == null || data.action_prefix.Length == 0 || data.action_prefix[0] == 0) + { + return false; + } + } + else + { + return false; + } + } + if (moveEnv == (int)MoveEnvironment.MOVEENV_GROUND) + { + szAct = EC_Utility.BuildActionName(data, weapon_type, "_吟唱_"); + } + else + { + string szAirCastMid = + ((m_wingType == enumWingType.WINGTYPE_WING && IsFlying()) || + GetProfession() == (int)PROFESSION.PROF_ANGEL || + GetProfession() == (int)PROFESSION.PROF_ARCHOR || + GetProfession() == (int)PROFESSION.PROF_MONK || + GetProfession() == (int)PROFESSION.PROF_GHOST) + ? "_空中翅膀_吟唱_" // Air cast with wings / 空中翅膀吟唱 + : "_空中飞剑_吟唱_"; // Air cast with flysword / 空中飞剑吟唱 + szAct = EC_Utility.BuildActionName(data, weapon_type, szAirCastMid); + } + + return !string.IsNullOrEmpty(szAct); + } + + /// + /// Combined action for skill cast GFX lookup (anim-test). / 技能吟唱组合动作(动画测试 GFX 查询)。 + /// + public A3DCombinedAction GetSkillCastComAct(int idSkill) + { + if (!TryBuildSkillCastActionName(idSkill, out string szAct) || m_pPlayerCECModel == null) + return null; + return m_pPlayerCECModel.GetComActByName(szAct); + } + + /// + /// Resolves skill attack (施放起/落) combined-action names for anim-test GFX lookup. + /// 解析技能施放组合动作名(动画测试 GFX 查询),不播放动作。 + /// + public bool TryBuildSkillAttackActionNames(int idSkill, int nSection, out string riseName, out string fallName) + { + riseName = string.Empty; + fallName = string.Empty; + + int weapon_type = GetShowingWeaponType(); + + if (!_default_skill_actions.TryGetValue((uint)idSkill, out PLAYER_ACTION_INFO_CONFIG data) || + data.action_prefix == null || data.action_prefix.Length == 0 || data.action_prefix[0] == 0) + { + if (ElementSkill.GetCommonCoolDown((uint)idSkill) > 1 << 4) + { + PLAYER_ACTION_INFO_CONFIG? data2 = m_PlayerActions[(int)PLAYER_ACTION_TYPE.ACT_USING_TARGET_ITEM].data; + if (data2 == null || data2.Value.action_prefix == null || data2.Value.action_prefix.Length == 0 || + data2.Value.action_prefix[0] == 0) + return false; + data = data2.Value; + } + else + return false; + } + + if (GetMoveEnv() == (int)MoveEnvironment.MOVEENV_GROUND) + { + riseName = EC_Utility.BuildActionName(data, weapon_type, "_施放起_"); + fallName = EC_Utility.BuildActionName(data, weapon_type, "_施放落_"); + } + else + { + string szActionMiddleName; + if ((m_wingType == enumWingType.WINGTYPE_WING && IsFlying()) || + (m_iProfession == (int)PROFESSION.PROF_ANGEL) || + (m_iProfession == (int)PROFESSION.PROF_ARCHOR) || + (m_iProfession == (int)PROFESSION.PROF_MONK) || + (m_iProfession == (int)PROFESSION.PROF_GHOST)) + { + szActionMiddleName = "_空中翅膀"; + } + else + { + szActionMiddleName = "_空中飞剑"; + } + + szActionMiddleName += "_施放起_"; + riseName = EC_Utility.BuildActionName(data, weapon_type, szActionMiddleName); + fallName = + $"{data.ActionPrefix}_{szActionMiddleName}_施法行_{data.action_weapon_suffix[weapon_type].Suffix}"; + } + + if (nSection != 0) + { + var pAtkMan = CECAttacksMan.Instance; + if (pAtkMan != null && pAtkMan.GetSkillSectionActionSuffix(idSkill, nSection, out string suffix)) + { + riseName += "_" + suffix; + fallName += "_" + suffix; + } + } + + return !string.IsNullOrEmpty(riseName); + } + + /// + /// Combined action for skill attack rise (施放起) GFX lookup (anim-test). + /// 技能施放起组合动作(动画测试 GFX 查询)。 + /// + public A3DCombinedAction GetSkillAttackComActRise(int idSkill, int nSection = 0) + { + if (!TryBuildSkillAttackActionNames(idSkill, nSection, out string riseName, out _) || + m_pPlayerCECModel == null) + return null; + return m_pPlayerCECModel.GetComActByName(riseName); + } + + /// + /// Combined action for skill attack fall (施放落) GFX lookup (anim-test). + /// 技能施放落组合动作(动画测试 GFX 查询)。 + /// + public A3DCombinedAction GetSkillAttackComActFall(int idSkill, int nSection = 0) + { + if (!TryBuildSkillAttackActionNames(idSkill, nSection, out _, out string fallName) || + m_pPlayerCECModel == null) + return null; + return m_pPlayerCECModel.GetComActByName(fallName); + } + public bool OnDamaged(int skill) { var atkMan = CECAttacksMan.Instance; diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs index e797a8c2ca..7722716b2e 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -501,7 +501,6 @@ public class A3DCombActDynData RemoveAllActiveFx(); if (ActiveAttackEvent != null) { - Debug.Log($"[Nam Debug]Stop: ActiveAttackEvent.m_bSignaled = true, Action name: {m_pAct.m_strName}"); ActiveAttackEvent.m_bSignaled = true; } @@ -1553,7 +1552,7 @@ public class CECModel { foreach (var ca in m_ChannelActs) ca?.Release(); - //EventBus.PublishChannel(m_nId, new ClearComActFlagAllRankNodesEvent(bSignalCurrent)); + EventBus.PublishChannel(m_nId, new ClearComActFlagAllRankNodesEvent(bSignalCurrent)); return false; } diff --git a/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneEditor.md b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneEditor.md index cf28618b6d..e5b65c392b 100644 --- a/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneEditor.md +++ b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneEditor.md @@ -2,7 +2,9 @@ This document tracks **Editor-only tooling** used with the runtime animation test workflow described in **`AnimationSceneSetup.md`**. -**Scene docs (runtime hierarchy, managers, APIs):** see `Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md`. +**Scene docs (runtime hierarchy, managers, APIs):** see [`AnimationSceneSetup.md`](AnimationSceneSetup.md). + +**Play Mode skill test (scene UI + SkillPanel):** see [`AnimationSceneSkillTest.md`](AnimationSceneSkillTest.md). **How to extend this file:** whenever you add or plan a new piece of Editor UI / automation for the animation scene, append a **`### Feature`** block under **[Feature log](#feature-log)** using the **[Feature template](#feature-template)** below. Keep one feature per numbered section so history stays searchable. diff --git a/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md index d3fce9bbf5..8b42955c68 100644 --- a/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md +++ b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSetup.md @@ -3,6 +3,8 @@ A standalone scene for testing character animations, skill VFX, weapon logic, and shape-change without any server connection. All triggers are issued via local commands. +**Skill test workflow (AnimatedModifier / LogPanel / SkillPanel):** see [`AnimationSceneSkillTest.md`](AnimationSceneSkillTest.md). + --- ## Related Source Files diff --git a/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSkillTest.md b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSkillTest.md new file mode 100644 index 0000000000..984a9850fc --- /dev/null +++ b/Assets/PerfectWorld/Scripts/_Doc/AnimationSceneSkillTest.md @@ -0,0 +1,188 @@ +# Animation Test Scene — Skill Animation Workflow + +**Scene:** [`Assets/PerfectWorld/Scene/AnimationTest.unity`](Assets/PerfectWorld/Scene/AnimationTest.unity) + +Use **AnimatedModifier**, **LogPanel**, and **SkillPanel** to load a character, pick a skill, and verify **cast + attack animations** and **GFX path coverage** without a server. + +**See also:** [`AnimationSceneSetup.md`](AnimationSceneSetup.md) (runtime APIs), [`AnimationSceneEditor.md`](AnimationSceneEditor.md) (bootstrap inspector tools). + +--- + +## Scene objects + +| Hierarchy name | Component | Script | +|---|---|---| +| **AnimatedModifier** | `AnimScenePlayerBootstrap` | `Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs` | +| **LogPanel** | `LogPanelAnimeScene` | `Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs` | +| **SkillPanel** | `SkillTriggerPanel` | `Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs` | + +--- + +## Prerequisites + +The scene must include the managers from `AnimationSceneSetup.md` (§9–11): + +- `AutoInitializer` / `ElementDataManProvider` +- `NPCManager`, `CECAttacksMan`, `EC_ManMessageMono` +- **CECHostPlayer** (referenced by AnimatedModifier and SkillPanel) +- UI init so `CECUIManager` / `CECGameUIMan` is ready (skill icons) + +**AnimatedModifier** must not be on the same GameObject as `CECHostPlayer` if **Replace host on role switch** is enabled. + +--- + +## Inspector wiring + +### AnimatedModifier (`AnimScenePlayerBootstrap`) + +| Field | Assign | +|---|---| +| **Player** | Scene `CECHostPlayer` | +| **Camera Object** | Main camera | +| **Camera Poses** | Preset transforms for orbit views | +| **Initial Role** | Default profession + gender on Play | +| **Weapon By Action Type** | 15 slots (index = `action_type` / `m_uAttackType`) | +| **Active Weapon Action Type Index** | Slot applied after each load | +| **Anim Scene Avoid Fashion Weapon Config Path** | On (recommended) | +| **Bind Player Visual After Model Load** | On (required for `PlayActionEvent` / Animancer) | +| **Is Evil Skill Path** | God vs evil catalog (same as `CDlgSkillSubList`) | +| **Skill Catalog Level** | Level for `CECHostSkillModel` injection (default `1`) | + +**Scene UI (camera):** + +| Button | Typical `OnClick()` target | +|---|---| +| **NextView** | `AnimatedModifier` → `AnimScenePlayerBootstrap.ChangeNextCamera()` | +| **PreviosView** | `AnimatedModifier` → `AnimScenePlayerBootstrap.ChangePreviosCamera()` | + +These cycle **Camera Poses** and reparent **Camera Object** (wraps at ends of the list). + +**Play Mode inspector** (select AnimatedModifier): **Switch model**, **Play action**, **Re-apply active weapon slot**. + +### SkillPanel (`SkillTriggerPanel`) + +| Field | Assign | +|---|---| +| **Player** | Same `CECHostPlayer` as AnimatedModifier | +| **Target Marker** | `CECMonsterTest` in front of the player | +| **Skill Grid Container** | `RectTransform` + `GridLayoutGroup` | +| **Skill Button Prefab** | Button + Image (icon) + Text | +| **Filter By Restrictions** | Off = full catalog; On = weapon/form/env filter | +| **Is Evil Skill Path** | Align with AnimatedModifier | +| **Skill Catalog Level** | Align with AnimatedModifier | +| **Log Panel Anime Scene** | **LogPanel** component | + +### LogPanel (`LogPanelAnimeScene`) + +| Field | Assign | +|---|---| +| **Copy Text Button Prefab** | `CopyTextButton` row | +| **Copy Text Buttons Parent** | Layout parent for rows | + +Filled automatically when you click a skill on SkillPanel. + +--- + +## How to test a skill animation + +### 1. Enter Play Mode + +1. Open `Assets/PerfectWorld/Scene/AnimationTest.unity`. +2. Press **Play**. +3. Filter Console: `[AnimSceneBootstrap]`. + +Expected logs: element data ready → `SetPlayerModel` done → `CECHostSkillModel initialized` → `[SkillTriggerPanel] Refreshed: N buttons added`. + +### 2. Character, weapon, camera + +- **Role:** AnimatedModifier **Initial Role**, or Play Mode **Switch model** on AnimatedModifier. +- **Weapon:** set **Active Weapon Action Type Index** and prefabs in **Weapon By Action Type**; **Re-apply active weapon slot** in Play Mode if you change the index. +- **Camera:** **NextView** / **PreviosView** to step through **Camera Poses**. + +Skill clip names depend on weapon suffix (`{prefix}_{weapon_suffix}_吟唱_`, `_施放起_`, `_施放落_`). + +### 3. Skill list filter (optional) + +On SkillPanel, **Filter By Restrictions**: + +- **Off** — full profession catalog (bootstrap default after load). +- **On** — only skills passing weapon/form/move-env (MP/AP faked unlimited). + +Context menu: **Debug: Refresh From Skill Model**, **Debug: Inject All Config Skills**. + +### 4. Target marker + +Place **Target Marker** (`CECMonsterTest`) ~5 units in front of the player for non-self skills. Self-target skills use the host id. + +### 5. Click a skill (SkillPanel) + +`LocalCastSkill` does the following: + +1. **LogPanel** — `Reset()`, skill **ID**, then GFX rows: + - **Cast** — from cast combined action + - **Attack** — rise/fall combined actions + - **Fly** / **Hit** / **HitGrnd** — composer or `ElementSkill.GetAllGFX` +2. **Cast animation** — `PlaySkillCastAction(skillId)` immediately. +3. **Attack animation** — after ~**1 s**, `CECAttacksMan.AddSkillAttack` + `PlaySkillAttackAction`. + +Click a LogPanel row to copy the GFX basename (green = Addressables key exists, red = missing). + +### 6. Raw actions (optional) + +AnimatedModifier Play Mode inspector: **Stand (0)**, **Run (3)**, or custom action index + **Play action** (not full skill chains). + +--- + +## VFX note (commented composer path) + +In `SkillTriggerPanel.LocalCastSkill`, the direct **`composerMan.Play(...)`** block is **commented out**. VFX in this scene is driven mainly by: + +- Combined-action GFX events logged on **LogPanel** +- The **delayed** `AddSkillAttack` / `PlaySkillAttackAction` path (attack event + `CECAttacksMan` tick) + +You may see **animations** without all **fly/hit GFX** playing until those events run. Re-enabling `composerMan.Play` would be a separate local-VFX experiment; the current workflow matches “animation-first” debugging. + +--- + +## Data flow + +```mermaid +sequenceDiagram + participant User + participant SkillPanel + participant LogPanel + participant Host as CECHostPlayer + participant Attacks as CECAttacksMan + + User->>SkillPanel: Click skill button + SkillPanel->>LogPanel: Reset + cast/attack/fly/hit GFX rows + SkillPanel->>Host: PlaySkillCastAction(skillId) + Note over SkillPanel: Wait ~1s + SkillPanel->>Attacks: AddSkillAttack(...) + SkillPanel->>Host: PlaySkillAttackAction(skillId, ...) +``` + +--- + +## Troubleshooting + +| Symptom | Check | +|---|---| +| No model | `NPCManager`, Addressables, `[AnimSceneBootstrap]`; **Player** on AnimatedModifier | +| No skill motion | **Bind Player Visual After Model Load**; Animancer + `CombinedActionSO` on `.ecm` | +| `PlaySkillCastAction returned false` | Skill not in action map; wrong weapon suffix | +| Empty skill grid | Wait for bootstrap; **Inject All Config Skills**; evil/god path vs profession | +| GFX at origin | **Target Marker** assigned and positioned | +| LogPanel all red | Register GFX in Addressables (`gfx/...`) | +| Camera buttons dead | **NextView** / **PreviosView** wired to `ChangeNextCamera` / `ChangePreviosCamera` on AnimatedModifier | + +**Console:** `[AnimSceneBootstrap]`, `[SkillTriggerPanel]` + +--- + +## Script references + +- `Assets/PerfectWorld/Scripts/AnimTestScene/AnimScenePlayerBootstrap.cs` +- `Assets/PerfectWorld/Scripts/AnimTestScene/LogPanelAnimeScene.cs` +- `Assets/PerfectWorld/Scripts/Debug/SkillTriggerPanel.cs` +- `Assets/PerfectWorld/Scripts/Editor/AnimScenePlayerBootstrapEditor.cs` diff --git a/Assets/Scripts/PlayerVisual.cs b/Assets/Scripts/PlayerVisual.cs index f0f7bed33b..c692b101c2 100644 --- a/Assets/Scripts/PlayerVisual.cs +++ b/Assets/Scripts/PlayerVisual.cs @@ -33,11 +33,13 @@ namespace BrewMonster private const float FadeTime = 100; private const FadeMode FadeMode = Animancer.FadeMode.FixedDuration; private string previousAnimationName; + private string lastQueueAnimationName; private void PlayActionEventHandler(PlayActionEvent @event) { //prevent enqueue the same loop animation bool loopcheck = @event.IsLoop == true && previousAnimationName == @event.AnimationName; - if(loopcheck) + bool queuecheck = lastQueueAnimationName == @event.AnimationName; + if(loopcheck || queuecheck) { return; } @@ -162,7 +164,6 @@ namespace BrewMonster } private void PlayNext() { - if (_animationQueue.Count == 0) { return;