// WL922_Probe.cs — PD 지시 #922 시험(Play 중 · 섬) // PoseClip(fbxPath, timesCsv, outPrefix, side) : FBX 안 첫 애니메이션 클립을 PC 에 입혀(PlayableGraph · 원본 애니메이터 무수정) 시각(초)마다 옆에서 찍는다 — 클립 길이 · 이벤트 없음 using System.Text; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; public static class WL922_Probe { const string Key = "wl922log"; static StringBuilder Buf() { var sb = System.AppDomain.CurrentDomain.GetData(Key) as StringBuilder; if (sb == null) { sb = new StringBuilder(); System.AppDomain.CurrentDomain.SetData(Key, sb); } return sb; } public static object Log() { return Buf().ToString(); } static float s_t0; static StringBuilder s_sb; static void OnStage(in WL.Combat.Core.ComboStageEvent e) { if (s_sb != null) s_sb.AppendFormat("{0:F2} 단계 {1} · 클립 {2}", Time.time - s_t0, e.stage, e.clip).Append('\n'); } /// #922 속사 모으기 중 이동 — Q 발동 뒤 moveAt 초부터 moveSec 초 동안 이동 입력 · PC 이동 거리(모으기 동안 / 그 뒤) · 이동 막힘 수 · 속사 발 수. public static object RapidMove(float moveAt, float moveSec, float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; var p0 = pc.Get_position(); bool moving = false, moved = false; Vector3 pc0 = Vector3.zero; int mb0 = WL.Combat.Weapon.WLSkillCancel.MoveBlocked, s0 = WL.Combat.Weapon.WLRapidShot.Shots, ab0 = WL.Combat.Weapon.WLRapidShot.Aborted; var dir = Quaternion.Euler(0f, 90f, 0f) * pc.transform.forward; dir.y = 0f; dir.Normalize(); string res = WL.Combat.Weapon.WLSkillHotkey.Fire(1); UnityEngine.Events.UnityAction act = null; float chargeEndDist = -1f; act = () => { float t = Time.time - t0; if (!moved && t >= moveAt) { if (!moving) { pc.Set_AutoBraking(false); pc.Set_JoystickStatus(1); moving = true; sb.AppendFormat("{0:F2} ▶ 이동 입력 · 상태 {1}", t, pc.Get_CurAnim()).Append('\n'); } pc.Run_byJoystick(dir); if (t >= moveAt + moveSec) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); moving = false; moved = true; sb.AppendFormat("{0:F2} ■ 이동 끝 · 상태 {1} · 이동 {2:F2} m", t, pc.Get_CurAnim(), Flat(pc.Get_position() - p0)).Append('\n'); } } if (chargeEndDist < 0f && WL.Combat.Weapon.WLRapidShot.Shots > s0) { chargeEndDist = Flat(pc.Get_position() - p0); sb.AppendFormat("{0:F2} 첫 발(모으기 끝) · 그때까지 이동 {1:F2} m", t, chargeEndDist).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; if (moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); } sb.AppendFormat("끝 · 이동 막힘 +{0} · 속사 발 +{1} · 끊김 +{2} · 총 이동 {3:F2} m · {4}", WL.Combat.Weapon.WLSkillCancel.MoveBlocked - mb0, WL.Combat.Weapon.WLRapidShot.Shots - s0, WL.Combat.Weapon.WLRapidShot.Aborted - ab0, Flat(pc.Get_position() - p0), res).Append('\n'); } }; Application.onBeforeRender += act; return "속사 + 이동 시험"; } static float Flat(Vector3 v) { v.y = 0f; return v.magnitude; } static int s_wantStage; static float s_pauseAt = -1f, s_delay; static void OnStagePause(in WL.Combat.Core.ComboStageEvent e) { if (e.stage == s_wantStage && s_pauseAt < 0f) s_pauseAt = Time.time + s_delay; } /// 콤보 단계 stage 가 시작되고 delay 초 뒤 일시 정지(호 이펙트 캡처용). public static object PauseStage(int stage, float delay, float maxSeconds) { var sb = Buf(); sb.Length = 0; s_wantStage = stage; s_delay = delay; s_pauseAt = -1f; float t0 = Time.time; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStagePause); WL.Combat.Core.CombatEvents.ComboStage.Add(OnStagePause); UnityEngine.Events.UnityAction act = null; act = () => { if ((s_pauseAt > 0f && Time.time >= s_pauseAt) || Time.time - t0 > maxSeconds) { Application.onBeforeRender -= act; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStagePause); if (s_pauseAt > 0f) UnityEditor.EditorApplication.isPaused = true; sb.Append(s_pauseAt > 0f ? "■ 일시 정지 단계 " + stage : "못 찾음").Append(" · 호 기울임 ").Append(WL.Combat.WeaponTrailDriver.ArcTilted).Append('\n'); } }; Application.onBeforeRender += act; return "단계 " + stage + " 대기"; } /// 지면 이펙트(WLImpactFx.GroundShown)가 늘면 delay 초 뒤 일시 정지 · 그때 칼끝(대검 소켓 그림 끝) 위치와 이펙트 위치(LastGround) 기록. public static object PauseGround(float delay, float maxSeconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; float t0 = Time.time; int g0 = WL.Combat.Impact.WLImpactFx.GroundShown; float at = -1f; UnityEngine.Events.UnityAction act = null; act = () => { if (at < 0f && WL.Combat.Impact.WLImpactFx.GroundShown > g0) at = Time.time + delay; if ((at > 0f && Time.time >= at) || Time.time - t0 > maxSeconds) { Application.onBeforeRender -= act; if (at > 0f) UnityEditor.EditorApplication.isPaused = true; Bounds b = new Bounds(); bool has = false; if (pc.tfs_weapon != null) foreach (var s in pc.tfs_weapon) if (s != null && s.gameObject.activeInHierarchy) foreach (var r in s.GetComponentsInChildren(false)) { if (!r.enabled) continue; if (!has) { b = r.bounds; has = true; } else b.Encapsulate(r.bounds); } var cam = Camera.main; var vp = cam.WorldToViewportPoint(pc.Get_Center_position()); sb.AppendFormat("{0} · {1} · 무기 그림 {2}~{3} · PC {4} · PC 화면 {5:F3},{6:F3}", at > 0f ? "■ 일시 정지" : "못 찾음", WL.Combat.Impact.WLImpactFx.LastGround, has ? b.min.ToString("F2") : "-", has ? b.max.ToString("F2") : "-", pc.Get_position().ToString("F2"), vp.x, 1f - vp.y).Append('\n'); } }; Application.onBeforeRender += act; return "지면 이펙트 대기"; } /// 대검 기본 공격 관찰 — 콤보 단계 · 클립 순서 · 지면 충격파(4타) 로그 · 그 순간 칼끝 위치. public static object Combo(float seconds) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; var sb = Buf(); sb.Length = 0; s_sb = sb; s_t0 = Time.time; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStage); WL.Combat.Core.CombatEvents.ComboStage.Add(OnStage); int g0 = WL.Combat.Impact.WLImpactFx.GroundShown; string lg = WL.Combat.Impact.WLImpactFx.LastGround; UnityEngine.Events.UnityAction act = null; act = () => { float t = Time.time - s_t0; if (WL.Combat.Impact.WLImpactFx.GroundShown != g0 || WL.Combat.Impact.WLImpactFx.LastGround != lg) { g0 = WL.Combat.Impact.WLImpactFx.GroundShown; lg = WL.Combat.Impact.WLImpactFx.LastGround; sb.AppendFormat("{0:F2} 지면 이펙트 #{1} · {2} · PC {3}", t, g0, lg, pc.Get_position().ToString("F2")).Append('\n'); } if (t > seconds) { Application.onBeforeRender -= act; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStage); s_sb = null; } }; Application.onBeforeRender += act; return "대검 콤보 관찰 " + seconds + " 초"; } public static object PoseClip(string fbxPath, string timesCsv, string outPrefix, float side) { var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음"; AnimationClip clip = null; string want = null; int hash = fbxPath.IndexOf('#'); if (hash > 0) { want = fbxPath.Substring(hash + 1); fbxPath = fbxPath.Substring(0, hash); } // 경로#클립이름 foreach (var o in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(fbxPath)) { var c = o as AnimationClip; if (c != null && !c.name.StartsWith("__preview__") && (want == null || c.name == want)) { clip = c; break; } } if (clip == null) return "클립 없음 " + fbxPath; var an = pc.m_animation; var ci = System.Globalization.CultureInfo.InvariantCulture; var graph = PlayableGraph.Create("~WL922_Pose"); graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); var output = AnimationPlayableOutput.Create(graph, "pose", an); var cp = AnimationClipPlayable.Create(graph, clip); cp.SetApplyFootIK(false); output.SetSourcePlayable(cp); var go = new GameObject("~WL922_Cam"); var cam = go.AddComponent(); cam.enabled = false; cam.fieldOfView = 35f; cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.12f, 0.14f, 0.18f, 1f); int ui = LayerMask.NameToLayer("UI"); if (ui >= 0) cam.cullingMask &= ~(1 << ui); // 시험 더미는 잠깐 멀리 옮긴다(무기는 다른 층이라 층으로 거르지 않음) var d = WL.IslandMob.WLTrailTest.Dummy; if (d != null) { d.Stop(true); d.Set_Warp(pc.Get_position() - pc.transform.forward * 25f); } var center = pc.Get_position() + Vector3.up * 0.7f + pc.transform.forward * 0.4f; go.transform.position = center + pc.transform.right * side + Vector3.up * 0.5f; go.transform.LookAt(center); if (Mathf.Approximately(side, 0f) && Camera.main != null) // side 0 = 게임 카메라 각도에서 확대 { var mc = Camera.main; go.transform.rotation = mc.transform.rotation; cam.fieldOfView = 12f; go.transform.position = center - mc.transform.forward * Vector3.Distance(mc.transform.position, center); } var sb = new StringBuilder(clip.name + " " + clip.length.ToString("F2") + "s · "); float speed0 = an.speed; try { foreach (var s in timesCsv.Split(',')) { float t = float.Parse(s, ci) * clip.length; // 정규화 시각 cp.SetTime(t); graph.Evaluate(0f); var rt = new RenderTexture(500, 500, 24); cam.targetTexture = rt; cam.Render(); var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(500, 500, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, 500, 500), 0, 0); tex.Apply(); RenderTexture.active = prev; System.IO.File.WriteAllBytes(outPrefix + "_" + s + ".png", tex.EncodeToPNG()); Object.Destroy(tex); cam.targetTexture = null; Object.Destroy(rt); } } finally { graph.Destroy(); Object.Destroy(go); an.speed = speed0 <= 0f ? 1f : speed0; pc.Play_Idle(); } foreach (var e in clip.events) sb.Append(e.functionName).Append('@').Append((e.time / clip.length).ToString("F2")).Append(' '); return sb.ToString(); } }