// WL789_Diag.cs — OnAnimatorMove 호출 여부 · CanAttackStep 판정 · Animator 위치 진단 (Play 전용) // unity command run_script --file AgentScripts/WL789_Diag.cs --entry WL789_Diag.Run --args '[6.0]' // unity command run_script --file AgentScripts/WL789_Diag.cs --entry WL789_Diag.Report using System.Collections; using System.Linq; using System.Text; using UnityEngine; public static class WL789_Diag { const string Out = "Screenshots_WL/combat5/p_wl789_diag.txt"; class RmProbe : MonoBehaviour { public int calls; public float rawAccum; public int nonZero; void OnAnimatorMove() { calls++; var a = GetComponent(); if (a != null) { var d = a.deltaPosition; d.y = 0f; rawAccum += d.magnitude; if (d.sqrMagnitude > 1e-10f) nonZero++; } } } class Host : MonoBehaviour { public IEnumerator Co(float seconds) { var sb = new StringBuilder(); var me = Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None).FirstOrDefault() as PCActor; if (me == null) { Done(sb.AppendLine("PCActor none (MyActor 가 PCActor 가 아님)")); yield break; } var anim = me.m_animation; sb.AppendLine("animator=" + (anim != null ? anim.name : "null") + " sameGO=" + (anim != null && anim.gameObject == me.gameObject) + " applyRootMotion=" + (anim != null ? anim.applyRootMotion.ToString() : "?") + " comps=" + (anim != null ? string.Join(",", anim.GetComponents().Select(c => c.GetType().Name)) : "-")); var probe = anim.gameObject.AddComponent(); int canTrue = 0, frames = 0; string anims = ""; string lastA = ""; float t0 = Time.time; while (Time.time - t0 < seconds) { frames++; if (me.WL_CanAttackStep()) canTrue++; string a = me.Get_CurAnim().ToString(); if (a != lastA) { anims += a + "(" + (Time.time - t0).ToString("F1") + ") "; lastA = a; } yield return null; } sb.AppendLine("frames=" + frames + " canAttackStepTrue=" + canTrue + " rmProbe.calls=" + probe.calls + " nonZeroDelta=" + probe.nonZero + " rawAccum=" + probe.rawAccum.ToString("F2") + " m"); sb.AppendLine("curAnim seq: " + anims); sb.AppendLine("AttackRootMotion apply=" + WL.Combat.AttackRootMotion.ApplyCount + " raw=" + WL.Combat.AttackRootMotion.RawAccum.ToString("F2")); Destroy(probe); Done(sb); } void Done(StringBuilder sb) { System.IO.File.WriteAllText(Out, sb.ToString()); Destroy(gameObject); } } public static object Run(float seconds) { if (!Application.isPlaying) return "not playing"; var h = new GameObject("__wl789diag").AddComponent(); h.StartCoroutine(h.Co(seconds)); return "started"; } public static object Report() { return System.IO.File.Exists(Out) ? System.IO.File.ReadAllText(Out) : "(none)"; } /// AttackRootMotion 의 private 필드와 각 게이트 판정값을 리플렉션으로 읽는다. public static object Fields() { if (!Application.isPlaying) return "not playing"; var me = Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None).FirstOrDefault() as PCActor; if (me == null) return "PCActor none"; var rm = me.m_animation != null ? me.m_animation.GetComponent() : null; if (rm == null) return "AttackRootMotion none"; var bf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; var t = typeof(WL.Combat.AttackRootMotion); var pc = t.GetField("_pc", bf).GetValue(rm) as PCActor; var an = t.GetField("_anim", bf).GetValue(rm) as Animator; var ag = t.GetField("_agent", bf).GetValue(rm) as UnityEngine.AI.NavMeshAgent; var cfg = t.GetField("_cfg", bf).GetValue(rm) as WL.Combat.WLCombatMotionSettings; var sb = new StringBuilder(); sb.AppendLine("_pc=" + (pc != null ? pc.name : "NULL") + " _anim=" + (an != null ? an.name : "NULL") + " _agent=" + (ag != null ? ag.name + " enabled=" + ag.enabled + " onMesh=" + ag.isOnNavMesh : "NULL") + " _cfg=" + (cfg != null ? cfg.attackMoveMode.ToString() : "NULL")); sb.AppendLine("canAttackStep(now)=" + me.WL_CanAttackStep() + " dashing=" + WL.Combat.DashDriver.IsDashingNow(me) + " curAnim=" + me.Get_CurAnim() + " enabled=" + rm.enabled + " isActive=" + rm.isActiveAndEnabled); sb.AppendLine("statics apply=" + WL.Combat.AttackRootMotion.ApplyCount + " raw=" + WL.Combat.AttackRootMotion.RawAccum); return sb.ToString(); } class ClipDeltaProbe : MonoBehaviour { public System.Collections.Generic.Dictionary sum = new System.Collections.Generic.Dictionary(); public System.Collections.Generic.Dictionary frames = new System.Collections.Generic.Dictionary(); public Vector3 posAccum; public float maxDelta; void OnAnimatorMove() { var a = GetComponent(); if (a == null) return; string clip = "(none)"; var ci = a.GetCurrentAnimatorClipInfo(0); if (ci.Length > 0) clip = ci[0].clip.name; var d = a.deltaPosition; d.y = 0f; if (!sum.ContainsKey(clip)) { sum[clip] = 0f; frames[clip] = 0; } sum[clip] += d.magnitude; frames[clip]++; maxDelta = Mathf.Max(maxDelta, d.magnitude); } } /// applyRootMotion 을 지정 상태로 두고 클립별 deltaPosition 합을 잰다(attackMoveMode 는 FixedStep 으로 잠시 바꿔 AttackRootMotion 이 플래그를 건드리지 않게). public static object ClipDelta(int flagOn, float seconds) { if (!Application.isPlaying) return "not playing"; var me = Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None).FirstOrDefault() as PCActor; if (me == null || me.m_animation == null) return "none"; var cfg = WL.Combat.WLCombatMotionSettings.Instance; if (cfg != null) cfg.attackMoveMode = WL.Combat.AttackMoveMode.FixedStep; me.m_animation.applyRootMotion = flagOn != 0; var p = me.m_animation.gameObject.AddComponent(); var h = new GameObject("__wl789cd").AddComponent(); h.p = p; h.seconds = seconds; h.anim = me.m_animation; h.StartCoroutine(h.Co()); return "started flag=" + me.m_animation.applyRootMotion + " mode=" + (cfg != null ? cfg.attackMoveMode.ToString() : "?"); } class CdHost : MonoBehaviour { public ClipDeltaProbe p; public float seconds; public Animator anim; public IEnumerator Co() { yield return new WaitForSeconds(seconds); var sb = new StringBuilder("== 클립별 deltaPosition 합 (flag=" + anim.applyRootMotion + ") maxDelta=" + p.maxDelta.ToString("F3") + " ==\n"); foreach (var k in p.sum.Keys) sb.AppendLine(" " + k + " frames=" + p.frames[k] + " sum=" + p.sum[k].ToString("F3") + " m"); System.IO.File.WriteAllText("Screenshots_WL/combat5/p_wl789_clipdelta.txt", sb.ToString()); Destroy(p); Destroy(gameObject); } } public static object ClipDeltaReport() { var f = "Screenshots_WL/combat5/p_wl789_clipdelta.txt"; return System.IO.File.Exists(f) ? System.IO.File.ReadAllText(f) : "(none)"; } /// applyRootMotion 을 끄고도 deltaPosition 이 계산되는지 본다(끄면 Unity 가 transform 을 직접 옮기지 않는다). public static object RootFlag(int on) { if (!Application.isPlaying) return "not playing"; var me = Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None).FirstOrDefault() as PCActor; if (me == null || me.m_animation == null) return "none"; me.m_animation.applyRootMotion = on != 0; return "applyRootMotion=" + me.m_animation.applyRootMotion; } }