using System.IO; using UnityEditor; using UnityEngine; namespace TKS.EditorTools { /// /// Assets/Resources/Units/<art>/<clip>/f###.png 임포트 설정: 단일 스프라이트 · 피벗 = meta.json pivot(발) · PPU = meta.ppu · 밉맵 없음 · 무압축. /// meta.json 은 skeletal-anim-pipeline `rig_frames_export.py` 가 쓴다. /// public class UnitArtImporter : AssetPostprocessor { public const string Root = "Assets/Resources/Units/"; /// 전장 배경(원화 세션 납품 · 5:8 불투명 · PPU 100 = 전장 20×32 월드). public const string BoardRoot = "Assets/Resources/Board/"; /// 챕터 월드맵(Maps = 2160×3840 불투명) · 거점 UI 부품(UI = 투명 · pivot·9-slice·PPU = UI/import.json). public const string ChapterMapRoot = "Assets/Resources/ChapterMap/"; [System.Serializable] class ChapterUiImport { public string file; public float[] pivot; public float[] border; public float ppu; } [System.Serializable] class ChapterUiImportList { public ChapterUiImport[] items; } void OnPreprocessTexture() { string path = assetPath.Replace('\\', '/'); if (path.StartsWith(ChapterMapRoot)) { PreprocessChapterMap(path); return; } if (path.StartsWith(BoardRoot)) { var bi = (TextureImporter)assetImporter; bi.textureType = TextureImporterType.Sprite; bi.spriteImportMode = SpriteImportMode.Single; bi.mipmapEnabled = false; bi.filterMode = FilterMode.Bilinear; bi.alphaIsTransparency = false; bi.textureCompression = TextureImporterCompression.Compressed; bi.maxTextureSize = 4096; bi.spritePixelsPerUnit = 100f; var bs = new TextureImporterSettings(); bi.ReadTextureSettings(bs); bs.spriteMeshType = SpriteMeshType.FullRect; bs.spriteAlignment = (int)SpriteAlignment.Center; bi.SetTextureSettings(bs); return; } if (!path.StartsWith(Root)) return; var ti = (TextureImporter)assetImporter; ti.textureType = TextureImporterType.Sprite; ti.spriteImportMode = SpriteImportMode.Single; ti.mipmapEnabled = false; ti.filterMode = FilterMode.Bilinear; ti.alphaIsTransparency = true; ti.textureCompression = TextureImporterCompression.Uncompressed; ti.maxTextureSize = 2048; var meta = LoadMeta(path); var s = new TextureImporterSettings(); ti.ReadTextureSettings(s); s.spriteMeshType = SpriteMeshType.FullRect; if (meta != null && meta.pivot != null && meta.pivot.Length == 2) { s.spriteAlignment = (int)SpriteAlignment.Custom; s.spritePivot = new Vector2(meta.pivot[0], meta.pivot[1]); } if (meta != null && meta.ppu > 0.01f) s.spritePixelsPerUnit = meta.ppu; ti.SetTextureSettings(s); } void PreprocessChapterMap(string path) { var ti = (TextureImporter)assetImporter; ti.textureType = TextureImporterType.Sprite; ti.spriteImportMode = SpriteImportMode.Single; ti.mipmapEnabled = false; ti.filterMode = FilterMode.Bilinear; var s = new TextureImporterSettings(); if (path.StartsWith(ChapterMapRoot + "Maps/")) { ti.alphaIsTransparency = false; ti.textureCompression = TextureImporterCompression.Compressed; ti.maxTextureSize = 4096; ti.spritePixelsPerUnit = 100f; ti.ReadTextureSettings(s); s.spriteMeshType = SpriteMeshType.FullRect; s.spriteAlignment = (int)SpriteAlignment.Center; ti.SetTextureSettings(s); return; } ti.alphaIsTransparency = true; ti.textureCompression = TextureImporterCompression.Uncompressed; ti.maxTextureSize = 1024; ti.ReadTextureSettings(s); s.spriteMeshType = SpriteMeshType.FullRect; var item = FindChapterUi(path); if (item != null) { if (item.pivot != null && item.pivot.Length == 2) { s.spriteAlignment = (int)SpriteAlignment.Custom; s.spritePivot = new Vector2(item.pivot[0], 1f - item.pivot[1]); // import.json = 이미지 좌표(위 → 아래) } if (item.border != null && item.border.Length == 4) s.spriteBorder = new Vector4(item.border[0], item.border[1], item.border[2], item.border[3]); if (item.ppu > 0.01f) s.spritePixelsPerUnit = item.ppu; } ti.SetTextureSettings(s); } static ChapterUiImport FindChapterUi(string texPath) { string dir = Path.GetDirectoryName(texPath); if (string.IsNullOrEmpty(dir)) return null; string json = Path.Combine(dir, "import.json"); if (!File.Exists(json)) return null; try { var list = JsonUtility.FromJson(File.ReadAllText(json)); string name = Path.GetFileName(texPath); if (list != null && list.items != null) foreach (var it in list.items) if (it.file == name) return it; } catch { // 표가 깨졌으면 기본 설정 } return null; } static UnitArtMeta LoadMeta(string texPath) { string clipDir = Path.GetDirectoryName(texPath); string artDir = clipDir != null ? Path.GetDirectoryName(clipDir) : null; if (string.IsNullOrEmpty(artDir)) return null; string metaPath = Path.Combine(artDir, "meta.json"); if (!File.Exists(metaPath)) return null; try { return JsonUtility.FromJson(File.ReadAllText(metaPath)); } catch { return null; } } /// 프레임을 새로 넣거나 meta.json 피벗을 바꾼 뒤 임포트 설정을 다시 적용한다(메뉴 · CLI `-executeMethod TKS.EditorTools.UnitArtImporter.ReimportAll`). [MenuItem("TKS/Reimport Unit Art (유닛 아트 재임포트)")] public static void ReimportAll() { foreach (var root in new[] { Root, BoardRoot, ChapterMapRoot }) { string dir = root.TrimEnd('/'); if (!AssetDatabase.IsValidFolder(dir)) { Debug.Log("[TKS] " + dir + " 없음"); continue; } AssetDatabase.ImportAsset(dir, ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate); Debug.Log("[TKS] 아트 재임포트 완료 · " + dir); } AssetDatabase.Refresh(); } } }