// WL-814k 진단 ⑤ — 「진짜 Animator 로 구동」 경로가 에디트 모드에서 되는지 실측 // (applyRootMotion=false 인 게임과 똑같은 포즈를 얻을 수 있으면 그게 정답 경로) // run: unity command run_script --file AgentScripts/WL814k_Diag5.cs --entry WL814k_Diag5.Run using System; using System.IO; using System.Text; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; public static class WL814k_Diag5 { const string kTxt = "AgentScripts/WL814k_DIAG5.txt"; static string OutDir = @"E:\NerdNavis\nn_himminji\Screenshots_WL\WL814k_diag"; static StringBuilder s_sb; static void L(string s) { s_sb.AppendLine(s); Debug.Log("[WL814k] " + s); } public static void Run() { s_sb = new StringBuilder(); Directory.CreateDirectory(OutDir); L("WL-814k 진단 ⑤ · " + DateTime.Now.ToString("HH:mm:ss")); Test("Assets/Res_Addr/PC/Ai01.prefab", new[] { "idle", "run", "attack", "hit", "die" }); Test("Assets/Res_Addr/Mobs/Mob/Batty_A.prefab", new[] { "idle", "run", "attack", "hit", "die" }); File.WriteAllText(kTxt, s_sb.ToString(), new UTF8Encoding(false)); Debug.Log("[WL814k] → " + kTxt); } static void Test(string prefabPath, string[] states) { GameObject pivot = null, inst = null, camGo = null; RenderTexture rt = null; Texture2D tex = null; try { EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat; RenderSettings.ambientLight = new Color(0.212f, 0.227f, 0.259f, 1f); RenderSettings.fog = false; RenderSettings.skybox = null; var sun = new GameObject("Sun").AddComponent(); sun.type = LightType.Directional; sun.intensity = 1.2f; sun.shadows = LightShadows.None; sun.transform.rotation = Quaternion.Euler(26.4f, 238.6f, 0f); var src = AssetDatabase.LoadAssetAtPath(prefabPath); pivot = new GameObject("Pivot"); inst = (GameObject)PrefabUtility.InstantiatePrefab(src); PrefabUtility.UnpackPrefabInstance(inst, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); inst.transform.SetParent(pivot.transform, false); inst.transform.localPosition = Vector3.zero; inst.transform.localRotation = Quaternion.identity; foreach (var c in inst.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in inst.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in inst.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in inst.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c.gameObject); foreach (var mb in inst.GetComponentsInChildren(true)) if (mb != null) UnityEngine.Object.DestroyImmediate(mb); foreach (var sm in inst.GetComponentsInChildren(true)) if (sm != null) sm.updateWhenOffscreen = true; var anim = inst.GetComponent(); anim.applyRootMotion = false; anim.cullingMode = AnimatorCullingMode.AlwaysAnimate; anim.Rebind(); float wpp = 3.4f * 2f / 240f; int P = 192; camGo = new GameObject("Cam"); var cam = camGo.AddComponent(); cam.orthographic = true; cam.orthographicSize = P * wpp * 0.5f; cam.nearClipPlane = 0.01f; cam.farClipPlane = 200f; cam.clearFlags = CameraClearFlags.SolidColor; cam.allowHDR = false; cam.allowMSAA = false; camGo.transform.rotation = Quaternion.Euler(43.609f, 0f, 0f); camGo.transform.position = -camGo.transform.right * (wpp * 0.5f) - camGo.transform.up * (wpp * 0.5f) - camGo.transform.forward * 50f; rt = new RenderTexture(P, P, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); rt.filterMode = FilterMode.Point; rt.antiAliasing = 1; rt.Create(); tex = new Texture2D(P, P, TextureFormat.RGBA32, false, false); var black = new Color32[P * P]; var white = new Color32[P * P]; L(""); L("== " + prefabPath + " (anim.Play + anim.Update 경로) =="); foreach (var st in states) { int hash = Animator.StringToHash(st); if (!anim.HasState(0, hash)) { L(" 상태 " + st + " 없음"); continue; } for (int f = 0; f < 4; f++) { float nt = f / 4f; anim.Play(hash, 0, nt); anim.Update(0f); inst.transform.localPosition = Vector3.zero; inst.transform.localRotation = Quaternion.identity; string tag = st + "_f" + f; Box(cam, rt, tex, P, black, white, tag, (f == 0) ? OutDir + "\\play_" + Path.GetFileNameWithoutExtension(prefabPath) + "_" + tag + ".png" : null); } } } catch (Exception ex) { L("EXCEPTION " + ex); } finally { if (rt != null) { RenderTexture.active = null; rt.Release(); UnityEngine.Object.DestroyImmediate(rt); } if (tex != null) UnityEngine.Object.DestroyImmediate(tex); if (camGo != null) UnityEngine.Object.DestroyImmediate(camGo); if (pivot != null) UnityEngine.Object.DestroyImmediate(pivot); EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); } } static void Box(Camera cam, RenderTexture rt, Texture2D tex, int P, Color32[] black, Color32[] white, string tag, string savePng) { Grab(cam, rt, tex, P, Color.black, black, null); Grab(cam, rt, tex, P, Color.white, white, savePng); int cov = 0, minX = int.MaxValue, maxX = -1, minY = int.MaxValue, maxY = -1; for (int y = 0; y < P; y++) for (int x = 0; x < P; x++) { int i = y * P + x; float c = 1f - (((white[i].r - black[i].r) + (white[i].g - black[i].g) + (white[i].b - black[i].b)) / 765f); if (c < 0.5f) continue; cov++; if (x < minX) minX = x; if (x > maxX) maxX = x; if (y < minY) minY = y; if (y > maxY) maxY = y; } L(string.Format(" {0,-14} 덮인 {1,6} · x[{2}..{3}] y[{4}..{5}]", tag, cov, minX, maxX, minY, maxY)); } static void Grab(Camera cam, RenderTexture rt, Texture2D tex, int P, Color bg, Color32[] outPx, string file) { cam.backgroundColor = bg; cam.targetTexture = rt; cam.Render(); cam.targetTexture = null; var active = RenderTexture.active; RenderTexture.active = rt; tex.ReadPixels(new Rect(0, 0, P, P), 0, 0); tex.Apply(false, false); RenderTexture.active = active; if (file != null) File.WriteAllBytes(file, tex.EncodeToPNG()); Array.Copy(tex.GetPixels32(), outPx, outPx.Length); } }