// WL-814k 진단 ③ — AnimationMode 샘플링이 실루엣을 어떻게 바꾸는지 프레임별 실측 // run: unity command run_script --file AgentScripts/WL814k_Diag3.cs --entry WL814k_Diag3.Run using System; using System.IO; using System.Text; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; public static class WL814k_Diag3 { const string kTxt = "AgentScripts/WL814k_DIAG3.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", "Atk2_wing", "LightHit", "death_F3441" }); File.WriteAllText(kTxt, s_sb.ToString(), new UTF8Encoding(false)); Debug.Log("[WL814k] → " + kTxt); } static void Test(string prefabPath, string[] clipNames) { GameObject pivot = null, inst = null, camGo = null; RenderTexture rt = null; Texture2D tex = null; bool am = false; 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); var anim = inst.GetComponent(); L(" Animator 살아있나 = " + (anim != null)); 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 rac = anim.runtimeAnimatorController; var black = new Color32[P * P]; var white = new Color32[P * P]; // ⓐ 샘플링 없이 Box(cam, rt, tex, P, black, white, "샘플링 없음", null); AnimationMode.StartAnimationMode(); am = true; foreach (var name in clipNames) { AnimationClip clip = null; foreach (var c in rac.animationClips) if (c.name == name) { clip = c; break; } if (clip == null) { L(" 클립 " + name + " 없음"); continue; } for (int f = 0; f < 4; f++) { float t = clip.length * f / 4f; AnimationMode.BeginSampling(); AnimationMode.SampleAnimationClip(inst, clip, t); AnimationMode.EndSampling(); inst.transform.localPosition = Vector3.zero; inst.transform.localRotation = Quaternion.identity; string tag = name + "_f" + f; Box(cam, rt, tex, P, black, white, tag, (name == "Idle" && f == 0) ? OutDir + "\\am_" + tag + ".png" : null); L(" 루트 localPos " + inst.transform.localPosition.ToString("F3") + " · lossyScale " + inst.transform.lossyScale.ToString("F3")); } } AnimationMode.StopAnimationMode(); am = false; } catch (Exception ex) { L("EXCEPTION " + ex); } finally { if (am && AnimationMode.InAnimationMode()) AnimationMode.StopAnimationMode(); 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, savePng == null ? null : savePng.Replace(".png", "_black.png")); Grab(cam, rt, tex, P, Color.white, white, savePng == null ? null : savePng.Replace(".png", "_white.png")); 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,-18} 덮인 {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); } }