Project_WL/AgentScripts/WL789_Diag.cs

135 lines
8.1 KiB
C#
Raw Normal View History

WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// 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<Animator>();
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<MyActor>(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<MonoBehaviour>().Select(c => c.GetType().Name)) : "-"));
var probe = anim.gameObject.AddComponent<RmProbe>();
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<Host>();
h.StartCoroutine(h.Co(seconds));
return "started";
}
public static object Report() { return System.IO.File.Exists(Out) ? System.IO.File.ReadAllText(Out) : "(none)"; }
/// <summary>AttackRootMotion 의 private 필드와 각 게이트 판정값을 리플렉션으로 읽는다.</summary>
public static object Fields()
{
if (!Application.isPlaying) return "not playing";
var me = Object.FindObjectsByType<MyActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None).FirstOrDefault() as PCActor;
if (me == null) return "PCActor none";
var rm = me.m_animation != null ? me.m_animation.GetComponent<WL.Combat.AttackRootMotion>() : 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<string, float> sum = new System.Collections.Generic.Dictionary<string, float>();
public System.Collections.Generic.Dictionary<string, int> frames = new System.Collections.Generic.Dictionary<string, int>();
public Vector3 posAccum; public float maxDelta;
void OnAnimatorMove()
{
var a = GetComponent<Animator>(); 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);
}
}
/// <summary>applyRootMotion 을 지정 상태로 두고 클립별 deltaPosition 합을 잰다(attackMoveMode 는 FixedStep 으로 잠시 바꿔 AttackRootMotion 이 플래그를 건드리지 않게).</summary>
public static object ClipDelta(int flagOn, float seconds)
{
if (!Application.isPlaying) return "not playing";
var me = Object.FindObjectsByType<MyActor>(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<ClipDeltaProbe>();
var h = new GameObject("__wl789cd").AddComponent<CdHost>(); 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)"; }
/// <summary>applyRootMotion 을 끄고도 deltaPosition 이 계산되는지 본다(끄면 Unity 가 transform 을 직접 옮기지 않는다).</summary>
public static object RootFlag(int on)
{
if (!Application.isPlaying) return "not playing";
var me = Object.FindObjectsByType<MyActor>(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;
}
}