// WL887_Probe.cs — PD 지시 #887 검 + 방패 · 방패 치기 이펙트 교체 시험(Play 중) // unity command run_script --file AgentScripts/WL887_Probe.cs --entry WL887_Probe.Weapons using System.Collections; using System.Text; using UnityEngine; public static class WL887_Probe { /// PC 직업 · 소켓별 무기(이름 · 월드 크기) · 지금 클립 · 이펙트 교체 카운터. public static object Weapons() { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var sb = new StringBuilder(); var mid = typeof(Actor).GetField("m_ID", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public).GetValue(pc); sb.Append("class=").Append(mid); var an = pc.m_animation; if (an != null) { var ci = an.GetCurrentAnimatorClipInfo(0); sb.Append(" clip=").Append(ci.Length > 0 ? ci[0].clip.name : "?").Append(" ctrl=").Append(an.runtimeAnimatorController != null ? an.runtimeAnimatorController.name : "-"); } float h = 0f; foreach (var r in pc.GetComponentsInChildren(true)) h = Mathf.Max(h, r.bounds.max.y - pc.transform.position.y); sb.Append(" 키=").Append(h.ToString("F2")); for (int i = 0; pc.tfs_weapon != null && i < pc.tfs_weapon.Length; i++) { var s = pc.tfs_weapon[i]; if (s == null || s.childCount == 0) continue; for (int c = 0; c < s.childCount; c++) { var w = s.GetChild(c); var b = new Bounds(w.position, Vector3.zero); bool has = false; foreach (var r in w.GetComponentsInChildren(true)) { if (!has) { b = r.bounds; has = true; } else b.Encapsulate(r.bounds); } sb.Append("\n 소켓").Append(i).Append(' ').Append(s.name).Append(" → ").Append(w.name).Append(" active=").Append(w.gameObject.activeInHierarchy) .Append(" 월드크기=").Append(b.size.ToString("F2")).Append(" lscale=").Append(w.localScale.ToString("F2")); } } sb.Append("\n vfx: swings=").Append(WL.Combat.Weapon.WLSkillVfx.Swings).Append(" hits=").Append(WL.Combat.Weapon.WLSkillVfx.Hits).Append(" hidden=").Append(WL.Combat.Weapon.WLSkillVfx.Hidden).Append(" errors=").Append(WL.Combat.Weapon.WLSkillVfx.Errors).Append(" last=").Append(WL.Combat.Weapon.WLSkillVfx.LastLog); sb.Append("\n classes: ").Append(WL.Combat.Weapon.WLWeaponClasses.LastLog); return sb.ToString(); } /// 임시 카메라로 PC 를 가까이 찍는다(게임 카메라 무접촉). yaws = "0|-90|180" (0 = 정면 · -90 = 왼쪽 = 방패 쪽) · 파일 = 접두 + yaw.png public static object SideShot(string prefix, string yaws, float dist, float height, int size) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var sb = new StringBuilder(); var center = pc.Get_Center_position(); var go = new GameObject("WL887_SideCam"); try { var cam = go.AddComponent(); cam.fieldOfView = 30f; cam.nearClipPlane = 0.05f; cam.farClipPlane = 60f; cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.32f, 0.36f, 0.42f); var rt = new RenderTexture(size, size, 24, RenderTextureFormat.ARGB32); cam.targetTexture = rt; foreach (var ys in yaws.Split('|')) { float yaw = float.Parse(ys); var dir = Quaternion.Euler(0f, yaw, 0f) * pc.transform.forward; cam.transform.position = center + dir * dist + Vector3.up * height; cam.transform.LookAt(center); cam.Render(); var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(size, size, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, size, size), 0, 0); tex.Apply(); RenderTexture.active = prev; System.IO.File.WriteAllBytes(prefix + ys + ".png", tex.EncodeToPNG()); Object.Destroy(tex); sb.Append("저장 yaw ").Append(ys).Append(' '); } cam.targetTexture = null; rt.Release(); Object.Destroy(rt); } finally { Object.Destroy(go); } return sb.ToString(); } /// 비교용 — 왼손 소켓 0 의 무기를 잠시 숨기고 path 프리팹을 붙인다(path 비우면 원래대로). 에디터 Play 전용. public static object SwapLH(string path) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var s = pc.tfs_weapon[0]; var test = s.Find("WL887_Test"); if (test != null) Object.Destroy(test.gameObject); for (int c = 0; c < s.childCount; c++) if (s.GetChild(c).name != "WL887_Test") s.GetChild(c).gameObject.SetActive(string.IsNullOrEmpty(path)); if (string.IsNullOrEmpty(path)) return "원래대로"; var p = UnityEditor.AssetDatabase.LoadAssetAtPath(path); if (p == null) return "없음 " + path; var w = Object.Instantiate(p, s, false); w.name = "WL887_Test"; w.transform.localPosition = Vector3.zero; w.transform.localRotation = Quaternion.identity; w.transform.localScale = Vector3.one; return "붙임 " + p.name; } /// 비교용 — 지금 컨트롤러 위에 클립 바꿔치기(AnimatorOverrideController)를 씌운다(파일 무수정). map = "원래클립=FBX경로:클립이름|…" · 비우면 원래 컨트롤러로. public static object TryClips(string map) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; var an = pc.m_animation; var cur = an.runtimeAnimatorController; var baseCtrl = cur is AnimatorOverrideController ? ((AnimatorOverrideController)cur).runtimeAnimatorController : cur; if (string.IsNullOrEmpty(map)) { an.runtimeAnimatorController = baseCtrl; return "원래대로 " + baseCtrl.name; } var ov = new AnimatorOverrideController(baseCtrl) { name = baseCtrl.name + "_WL887test" }; var sb = new StringBuilder(); foreach (var pair in map.Split('|')) { var kv = pair.Split('='); if (kv.Length != 2) continue; var src = kv[1].Split(':'); AnimationClip clip = null; foreach (var o in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(src[0])) { var c = o as AnimationClip; if (c != null && c.name == src[1]) { clip = c; break; } } if (clip == null) { sb.Append("클립 없음 ").Append(kv[1]).Append(' '); continue; } ov[kv[0]] = clip; sb.Append(kv[0]).Append("→").Append(clip.name).Append(' '); } an.runtimeAnimatorController = ov; return sb.ToString(); } /// 섬 조이스틱(SimpleInput 축) 입력을 seconds 동안 넣는다(달리기 캡처용). public static object Steer(float dx, float dz, float seconds) { var pc = MyValue.MyPC; if (pc == null) return "PC 없음"; pc.StartCoroutine(Co(dx, dz, seconds)); return "입력 " + seconds + "s"; } static IEnumerator Co(float dx, float dz, float seconds) { var h = new SimpleInput.AxisInput("Horizontal") { value = dx }; var v = new SimpleInput.AxisInput("Vertical") { value = dz }; h.StartTracking(); v.StartTracking(); float t = 0f; while (t < seconds) { t += Time.deltaTime; yield return null; } h.StopTracking(); v.StopTracking(); } }