// WL-814k 진단 ⑥ — 3D 프레임 k 와 스프라이트 프레임 k 가 정말 같은 그림인가(격자 비교) // run: unity command run_script --file AgentScripts/WL814k_Diag6.cs --entry WL814k_Diag6.Run using System; using System.IO; using System.Text; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using WL.Look.SpriteBake; public static class WL814k_Diag6 { const string kOut = @"E:\NerdNavis\nn_himminji\Screenshots_WL\WL814k_diag"; const string kTxt = "AgentScripts/WL814k_DIAG6.txt"; const float kOrtho = 3.4f, kPitch = 43.609f; const int kLowH = 240; 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(kOut); L("WL-814k 진단 ⑥ · " + DateTime.Now.ToString("HH:mm:ss")); try { EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); var sun = new GameObject("Sun").AddComponent(); sun.type = LightType.Directional; sun.shadows = LightShadows.None; sun.color = new Color(1f, 0.92f, 0.703f, 1f); sun.intensity = 1.2f; sun.transform.rotation = Quaternion.Euler(26.4f, 238.6f, 0f); var fill = new GameObject("Fill").AddComponent(); fill.type = LightType.Directional; fill.shadows = LightShadows.None; fill.color = new Color(0.596f, 0.703f, 0.839f, 1f); fill.intensity = 0.3f; fill.transform.rotation = Quaternion.Euler(36.1f, 116.1f, 0f); RenderSettings.skybox = AssetDatabase.GetBuiltinExtraResource("Default-Skybox.mat"); RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Skybox; RenderSettings.ambientIntensity = 1f; RenderSettings.fog = false; DynamicGI.UpdateEnvironment(); var look = WLSpriteLookSettings.Instance; var actor = Spawn("Assets/Res_Addr/PC/Ai01.prefab", 0.7f); var set = SpriteBillboard.MatchSet(actor.name, look); var act = set.FindByKey("attack"); var anim = actor.GetComponent(); int cell = 64, cols = act.frameCount, rows = 2, dir = 7; var rig = MakeCam(cell, cell); var sheet = new Color32[cols * cell * rows * cell]; // 위 줄 = 3D (매 컷 직전에 포즈를 다시 찍는다) for (int f = 0; f < cols; f++) { actor.transform.rotation = Quaternion.Euler(0f, dir * 45f, 0f); anim.Play(Animator.StringToHash("attack"), 0, (f + 0.001f) / act.frameCount); anim.Update(0f); var st = anim.GetCurrentAnimatorStateInfo(0); L(string.Format(" 3D f{0} → 상태 해시 {1}(attack={2}) nt {3:0.000}", f, st.shortNameHash, Animator.StringToHash("attack"), st.normalizedTime)); BlitInto(rig.Grab(), cell, cell, sheet, cols * cell, f * cell, 1 * cell); } // 아래 줄 = 스프라이트 var e = SpriteBillboard.TryAttach(actor, set); for (int f = 0; f < cols; f++) { actor.transform.rotation = Quaternion.Euler(0f, dir * 45f, 0f); SpriteBillboard.SetManualFrame(actor, "attack", dir, f); SpriteBillboard.UpdateAll(rig.cam); L(string.Format(" 스프라이트 f{0} → 인덱스 {1} (시작 {2} + {3}*{4} + {5})", f, act.startIndex + dir * act.frameCount + f, act.startIndex, dir, act.frameCount, f)); BlitInto(rig.Grab(), cell, cell, sheet, cols * cell, f * cell, 0 * cell); } SpriteBillboard.RestoreNow("diag6"); Save(sheet, cols * cell, rows * cell, kOut + @"\frames_attack_dir7.png", 6); rig.Dispose(); } catch (Exception ex) { L("EXCEPTION " + ex); } finally { SpriteBillboard.RestoreNow("diag6-end"); EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); } File.WriteAllText(kTxt, s_sb.ToString(), new UTF8Encoding(false)); } static GameObject Spawn(string p, float scale) { var src = AssetDatabase.LoadAssetAtPath(p); var go = (GameObject)PrefabUtility.InstantiatePrefab(src); PrefabUtility.UnpackPrefabInstance(go, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); go.name = Path.GetFileNameWithoutExtension(p) + "(Clone)"; go.transform.position = Vector3.zero; go.transform.localScale = Vector3.one * scale; foreach (var c in go.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in go.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in go.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c); foreach (var c in go.GetComponentsInChildren(true)) UnityEngine.Object.DestroyImmediate(c.gameObject); foreach (var mb in go.GetComponentsInChildren(true)) if (mb != null) UnityEngine.Object.DestroyImmediate(mb); foreach (var sm in go.GetComponentsInChildren(true)) sm.updateWhenOffscreen = true; var an = go.GetComponent(); an.applyRootMotion = false; an.cullingMode = AnimatorCullingMode.AlwaysAnimate; an.Rebind(); var bake = WLSpriteBakeSettings.Instance; foreach (var rend in go.GetComponentsInChildren(true)) { if (rend == null || rend is ParticleSystemRenderer) continue; var mats = rend.sharedMaterials; bool any = false; for (int i = 0; i < mats.Length; i++) { if (mats[i] == null) continue; var t = AssetDatabase.LoadAssetAtPath(bake.toonMaterialDir + "/" + mats[i].name + bake.toonMaterialSuffix + ".mat"); if (t != null) { mats[i] = t; any = true; } } if (any) rend.sharedMaterials = mats; } return go; } sealed class Rig { public Camera cam; public RenderTexture rt; public Texture2D tex; public int w, h; public Color32[] Grab() { cam.targetTexture = rt; cam.Render(); cam.targetTexture = null; var a = RenderTexture.active; RenderTexture.active = rt; tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply(false, false); RenderTexture.active = a; return tex.GetPixels32(); } public void Dispose() { RenderTexture.active = null; if (rt != null) { rt.Release(); UnityEngine.Object.DestroyImmediate(rt); } if (tex != null) UnityEngine.Object.DestroyImmediate(tex); if (cam != null) UnityEngine.Object.DestroyImmediate(cam.gameObject); } } static Rig MakeCam(int w, int h) { var go = new GameObject("Cam"); var c = go.AddComponent(); c.orthographic = true; c.orthographicSize = h * (2f * kOrtho / kLowH) * 0.5f; c.nearClipPlane = 0.01f; c.farClipPlane = 200f; c.clearFlags = CameraClearFlags.SolidColor; c.backgroundColor = new Color(0.34f, 0.40f, 0.25f, 1f); c.allowHDR = false; c.allowMSAA = false; go.transform.rotation = Quaternion.Euler(kPitch, 0f, 0f); go.transform.position = new Vector3(0f, 0.6f, 0f) - go.transform.forward * 40f; var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); rt.filterMode = FilterMode.Point; rt.antiAliasing = 1; rt.Create(); var tex = new Texture2D(w, h, TextureFormat.RGBA32, false, false); return new Rig { cam = c, rt = rt, tex = tex, w = w, h = h }; } static void BlitInto(Color32[] src, int sw, int sh, Color32[] dst, int dw, int dx, int dy) { for (int y = 0; y < sh; y++) for (int x = 0; x < sw; x++) { int di = (dy + y) * dw + (dx + x); if (di >= 0 && di < dst.Length) dst[di] = src[y * sw + x]; } } static void Save(Color32[] px, int w, int h, string file, int zoom) { int ow = w * zoom, oh = h * zoom; var o = new Color32[ow * oh]; for (int y = 0; y < oh; y++) for (int x = 0; x < ow; x++) o[y * ow + x] = px[(y / zoom) * w + (x / zoom)]; var t = new Texture2D(ow, oh, TextureFormat.RGBA32, false, false); t.SetPixels32(o); t.Apply(false, false); File.WriteAllBytes(file, t.EncodeToPNG()); UnityEngine.Object.DestroyImmediate(t); } }