// WL-814k 베이크 드라이버 (에디트 모드 전용 · 게임 코드 아님) // 에셋 만들기 : unity command run_script --file AgentScripts/WL814k_Bake.cs --entry WL814k_Bake.MakeAssets // 시범 2종 굽기: unity command run_script --file AgentScripts/WL814k_Bake.cs --entry WL814k_Bake.BakeDemo // 임의 프리팹 : WL814k_Bake.Targets 를 바꾸거나 에디터 창(WL ▸ 도트 스프라이트 굽기)을 쓴다. // // 🔴 이 드라이버에만 「어떤 프리팹을 구울지」가 적혀 있다. 툴(Assets/WL/Look/Sprite/Editor/WLSpriteBaker.cs) // 에는 캐릭터 이름이 0개다 — PD 가 캐릭터 에셋을 바꿔도 툴은 그대로 쓴다. using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEditor; using UnityEngine; using WL.Look.SpriteBake; using WL.Look.SpriteBake.EditorTools; public static class WL814k_Bake { const string kResDir = "Assets/WL/Look/Sprite/Resources/WL"; const string kOutDir = "Assets/WL/Look/Sprite/Baked"; const string kTxt = "AgentScripts/WL814k_BAKE.txt"; // 🔴 1차 시범 = PC 1종 + 잡몹 1종 (전수 전환은 PD 승인 뒤) // 두 번째 값 = 인게임 실제 루트 스케일(0 이면 프리팹 자체 값). // PC 는 ClassConfig.f_Scale 0.7 로 스폰된다(814b·발주서 실측) → 그 크기로 구워야 // 스프라이트 1px 이 화면 1px 과 정확히 맞는다. 몹은 프리팹 스케일 그대로(Batty_A = 1.5). public static readonly string[] Targets = { "Assets/Res_Addr/PC/Ai01.prefab", "Assets/Res_Addr/Mobs/Mob/Batty_A.prefab" }; public static readonly float[] TargetScales = { 0.7f, 0f }; static StringBuilder s_sb; static void L(string s) { s_sb.AppendLine(s); Debug.Log("[WL814k] " + s); } // ═════════════════════════════════════════════════════════════════════════ // ① 설정 에셋 2개 만들기 // ═════════════════════════════════════════════════════════════════════════ public static void MakeAssets() { s_sb = new StringBuilder(); L("WL-814k 설정 에셋 · " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Directory.CreateDirectory(kResDir); Directory.CreateDirectory(kOutDir); var bake = AssetDatabase.LoadAssetAtPath(kResDir + "/WLSpriteBakeSettings.asset"); if (bake == null) { bake = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(bake, kResDir + "/WLSpriteBakeSettings.asset"); L("만듦 " + kResDir + "/WLSpriteBakeSettings.asset"); } else L("있음 " + kResDir + "/WLSpriteBakeSettings.asset"); var look = AssetDatabase.LoadAssetAtPath(kResDir + "/WLSpriteLookSettings.asset"); if (look == null) { look = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(look, kResDir + "/WLSpriteLookSettings.asset"); L("만듦 " + kResDir + "/WLSpriteLookSettings.asset"); } else L("있음 " + kResDir + "/WLSpriteLookSettings.asset"); AssetDatabase.SaveAssetIfDirty(bake); AssetDatabase.SaveAssetIfDirty(look); WLSpriteBakeSettings.ClearCache(); WLSpriteLookSettings.ClearCache(); L(string.Format("굽기 기본값 — 내림각 {0:0.000}° · 픽셀 {1:0.00000} m · 방향 {2} · 프레임 {3}(자동 {4}) · 팔레트 {5}단계 · 알파컷 {6}", bake.PitchDeg, bake.WorldPerPixel, bake.dirCount, bake.frameSize, bake.autoFrameSize, bake.paletteLevels, bake.alphaCutoff)); File.WriteAllText(kTxt, s_sb.ToString(), new UTF8Encoding(false)); } // ═════════════════════════════════════════════════════════════════════════ // ② 시범 2종 굽기 + SO 목록 연결 // ═════════════════════════════════════════════════════════════════════════ public static void BakeDemo() { BakeList(Targets, TargetScales); } public static void BakeList(string[] paths, float[] scales) { s_sb = new StringBuilder(); L("WL-814k 굽기 · " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " · Unity " + Application.unityVersion); var sets = new List(); double total = 0; for (int ti = 0; ti < paths.Length; ti++) { var p = paths[ti]; float scale = (scales != null && ti < scales.Length) ? scales[ti] : 0f; L(""); L("──────── " + p + (scale > 0f ? " (루트 스케일 " + scale + ")" : " (프리팹 스케일 그대로)")); // 🔴 굽기마다 설정 에셋을 다시 읽는다 — PNG 임포트가 AssetDatabase 를 돌려 // 참조만 들고 있던 SO 가 언로드돼 null 이 되는 것을 실측했다(첫 시도 실패 원인). var cfg = AssetDatabase.LoadAssetAtPath(kResDir + "/WLSpriteBakeSettings.asset"); if (cfg == null) { L("FAIL — 굽기 설정 에셋 없음. MakeAssets 먼저."); break; } var r = WLSpriteBaker.Bake(p, cfg, kOutDir, null, scale); total += r.seconds; L(r.log.ToString().TrimEnd()); foreach (var n in r.notes) L(" · 「미확인/주의」 " + n); if (!r.ok) { L("FAIL"); continue; } L(string.Format("OK — 프레임 {0}×{0} px · 방향 {1} · 총 {2}칸 · 시트 {3}×{4} · PNG {5:N0} B ({6:N1} KB) · {7:0.00}초 · 캐릭터 {8:0} px", r.frameSize, r.dirCount, r.totalFrames, r.sheetW, r.sheetH, r.pngBytes, r.pngBytes / 1024.0, r.seconds, r.charPixelHeight)); L(" PNG = " + r.pngPath); L(" SO = " + r.setPath); var set = AssetDatabase.LoadAssetAtPath(r.setPath); if (set != null) { sets.Add(set); var sb2 = new StringBuilder(); foreach (var a in set.actions) sb2.Append(string.Format("{0}(시작 {1}·{2}프레임·{3:0.0}fps·상태 [{4}]·클립 {5}) ", a.key, a.startIndex, a.frameCount, a.fps, string.Join("/", a.stateNames), a.clipName)); L(" 동작표 = " + sb2.ToString().TrimEnd()); L(string.Format(" 발밑 픽셀 ({0:0.0}, {1:0.0}) · 픽셀 1개 {2:0.00000} m · 베이크 스케일 {3:0.000}", set.footPixelX, set.footPixelY, set.worldPerPixel, set.bakeScale)); } } // 런타임 SO 에 시트 목록 연결 var look = AssetDatabase.LoadAssetAtPath(kResDir + "/WLSpriteLookSettings.asset"); if (look != null) { look.sets = sets.ToArray(); EditorUtility.SetDirty(look); AssetDatabase.SaveAssetIfDirty(look); L(""); L("런타임 SO 연결 — sets " + sets.Count + "개 (" + kResDir + "/WLSpriteLookSettings.asset)"); } L(""); L(string.Format("합계 굽기 시간 = {0:0.00}초 / {1}종 → 1종당 {2:0.00}초", total, paths.Length, total / Math.Max(1, paths.Length))); Write(); } static void Write() { File.WriteAllText(kTxt, s_sb.ToString(), new UTF8Encoding(false)); Debug.Log("[WL814k] → " + kTxt); } }