using System; using System.Collections.Generic; using System.IO; using System.Reflection; using UnityEditor; using UnityEngine; /// /// 一键修复图集"渗色 / 串图 / 边缘被邻居图片填充"问题。 /// /// 症状:圆形等带透明边缘的图片,四周被其他图片的像素填充;正方形区域采样/裁剪时会取到邻居图。 /// 成因:padding 过小 + 紧致打包(透明边被裁掉) + 未开 Alpha 扩张 + 图集可能被降采样。 /// /// 本工具对图集的 packingSettings 统一设置: /// padding 4 → 8 相邻 sprite 之间留出大于压缩块的间隙 /// enableTightPacking off 不再裁掉透明边,sprite 保持完整矩形(正方形区域只含自己) /// enableRotation off 不在图集内旋转贴图,避免采样方向错乱(串图) /// enableAlphaDilation on 边缘颜色向外扩张,滤色采样不会取到邻居像素 /// /// 改完必须:重建 Addressables 内容 → 重出包。 /// public static class AtlasPackingFix { private const int Padding = 8; private const bool KeepRotation = false; // 需要省图集空间时可改回 true [MenuItem("Tools/图集工具/修复所有图集(渗色·串图)")] public static void FixAll() { var list = new List(); foreach (var f in Directory.GetFiles("Assets", "*.spriteatlasv2", SearchOption.AllDirectories)) list.Add(f.Replace('\\', '/')); foreach (var f in Directory.GetFiles("Assets", "*.spriteatlas", SearchOption.AllDirectories)) list.Add(f.Replace('\\', '/')); if (list.Count == 0) { Debug.LogWarning("[图集修复] 没找到任何图集"); return; } Apply(list); } [MenuItem("Tools/图集工具/修复选中图集(渗色·串图)")] public static void FixSelected() { var list = new List(); foreach (var guid in Selection.assetGUIDs) { var p = AssetDatabase.GUIDToAssetPath(guid); if (p.EndsWith(".spriteatlas") || p.EndsWith(".spriteatlasv2")) list.Add(p); } if (list.Count == 0) { Debug.LogWarning("[图集修复] 请先在 Project 里选中至少一个 .spriteatlas / .spriteatlasv2"); return; } Apply(list); } private static void Apply(List paths) { int ok = 0, skip = 0; try { AssetDatabase.StartAssetEditing(); foreach (var path in paths) { var importer = AssetImporter.GetAtPath(path); if (importer == null) { skip++; continue; } // 用反射拿 packingSettings,避免不同 Unity 版本字段可见性差异导致编译问题 var prop = importer.GetType().GetProperty("packingSettings"); if (prop == null || !prop.CanRead || !prop.CanWrite) { Debug.LogWarning($"[图集修复] 跳过(无 packingSettings): {path}"); skip++; continue; } object boxed = prop.GetValue(importer); // 装箱的结构体副本 var st = boxed.GetType(); SetField(st, boxed, "padding", Padding); SetField(st, boxed, "enableTightPacking", false); SetField(st, boxed, "enableRotation", KeepRotation); SetField(st, boxed, "enableAlphaDilation", true); prop.SetValue(importer, boxed); importer.SaveAndReimport(); ok++; Debug.Log($"[图集修复] {Path.GetFileName(path)} → padding={Padding}, TightPacking=off, Rotation={(KeepRotation ? "on" : "off")}, AlphaDilation=on"); } } finally { AssetDatabase.StopAssetEditing(); AssetDatabase.Refresh(); } Debug.Log($"[图集修复] 完成:处理 {ok} 个,跳过 {skip} 个。请重建 Addressables 内容后重出包。"); } private static void SetField(Type t, object boxed, string name, object value) { var f = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (f != null) f.SetValue(boxed, value); else Debug.LogWarning($"[图集修复] 字段不存在,已跳过: {name}"); } }