using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditor.U2D; using UnityEngine; using UnityEngine.U2D; using Object = UnityEngine.Object; /// /// 图集导入管线。 /// public class SpritePostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (var s in importedAssets) { EditorSpriteSaveInfo.OnImportSprite(s); } foreach (var s in deletedAssets) { EditorSpriteSaveInfo.OnDeleteSprite(s); } foreach (var s in movedFromAssetPaths) { EditorSpriteSaveInfo.OnDeleteSprite(s); } foreach (var s in movedAssets) { EditorSpriteSaveInfo.OnImportSprite(s); } } } public static class EditorSpriteSaveInfo { private const string NormalAtlasDir = "Assets/Atlas"; private const string UISpritePath = "Assets/AssetRaw/UIRaw"; private const string UIAtlasPath = "Assets/AssetRaw/UIRaw/Atlas"; private const string UIRawPath = "Assets/AssetRaw/UIRaw/UIRaw"; private static List m_dirtyAtlasList = new List(); private static Dictionary> m_allASprites = new Dictionary>(); private static Dictionary m_uiAtlasMap = new Dictionary(); private static bool m_inited = false; private static bool m_dirty = false; public static void Init() { if (m_inited) { return; } EditorApplication.update += CheckDirty; } public static void CheckDirty() { if (m_dirty) { m_dirty = false; AssetDatabase.Refresh(); float lastProgress = -1; for (int i = 0; i < m_dirtyAtlasList.Count; i++) { string atlasName = m_dirtyAtlasList[i]; Debug.Log("更新图集 : " + atlasName); var curProgress = (float)i / m_dirtyAtlasList.Count; if (curProgress > lastProgress + 0.01f) { lastProgress = curProgress; var progressText = $"当前进度:{i}/{m_dirtyAtlasList.Count} {atlasName}"; bool cancel = EditorUtility.DisplayCancelableProgressBar("刷新图集" + atlasName, progressText, curProgress); if (cancel) { break; } } bool isUI = atlasName.StartsWith("UIRaw"); SaveAtlas(atlasName, isUI); } EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); m_dirtyAtlasList.Clear(); } } public static void OnImportSprite(string assetPath) { if (!assetPath.StartsWith(UISpritePath)) { return; } TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter; if (ti != null) { var modify = false; if (assetPath.StartsWith(UISpritePath)) { if (ti.textureType != TextureImporterType.Sprite) { ti.textureType = TextureImporterType.Sprite; modify = true; } if (!string.IsNullOrEmpty(ti.spritePackingTag)) { ti.spritePackingTag = string.Empty; modify = true; } var setting = new TextureImporterSettings(); ti.ReadTextureSettings(setting); if (setting.spriteGenerateFallbackPhysicsShape) { setting.spriteGenerateFallbackPhysicsShape = false; ti.SetTextureSettings(setting); modify = true; } if (IsKeepRawImage(assetPath)) { //调整android格式 var andPlatformSettings = ti.GetPlatformTextureSettings("Android"); if (!andPlatformSettings.overridden) { andPlatformSettings.overridden = true; modify = true; } if (andPlatformSettings.format != TextureImporterFormat.ASTC_6x6) { andPlatformSettings.format = TextureImporterFormat.ASTC_6x6; ti.SetPlatformTextureSettings(andPlatformSettings); modify = true; } //调整ios格式 var iosPlatformSettings = ti.GetPlatformTextureSettings("iPhone"); if (!iosPlatformSettings.overridden) { iosPlatformSettings.overridden = true; modify = true; } if (iosPlatformSettings.format != TextureImporterFormat.ASTC_5x5) { iosPlatformSettings.format = TextureImporterFormat.ASTC_5x5; iosPlatformSettings.compressionQuality = 50; ti.SetPlatformTextureSettings(iosPlatformSettings); modify = true; } } } if (modify) { ti.SaveAndReimport(); } if (ti.textureType == TextureImporterType.Sprite) { OnProcessSprite(assetPath); } } } /// /// 是否保持散图(不打图集) /// /// /// public static bool IsKeepRawImage(string dirPath) { return dirPath.Contains("UIRaw/Raw/") || dirPath.Contains("UIRaw_Raw_"); } public static string GetSpritePath(string assetPath) { string path = assetPath.Substring(0, assetPath.LastIndexOf(".")); path = path.Replace("Assets/AssetRaw/", ""); return path; } /// /// 根据文件路径,返回图集名称 /// /// /// public static string GetPackageTag(string fullName) { fullName = fullName.Replace("\\", "/"); int idx = fullName.LastIndexOf("UIRaw", StringComparison.Ordinal); if (idx == -1) { return ""; } if (IsKeepRawImage(fullName)) { return ""; } var atlasPath = fullName.Substring(idx); string str = atlasPath; str = str.Substring(0, str.LastIndexOf("/", StringComparison.Ordinal)).Replace("/", "_"); return str; } public static void OnProcessSprite(string assetPath) { if (!assetPath.StartsWith("Assets")) { return; } if (assetPath.StartsWith("Assets/UIRaw_Delete")) { return; } Init(); var spriteName = Path.GetFileNameWithoutExtension(assetPath); var spritePath = GetSpritePath(assetPath); if (!m_uiAtlasMap.TryGetValue(spriteName, out string oldAssetPath) || spritePath == oldAssetPath) { m_uiAtlasMap[spriteName] = spritePath; m_dirty = true; } else { Debug.LogError($"有重名的图片:{spriteName}\n旧图集:{oldAssetPath}\n新图集:{spritePath} "); m_uiAtlasMap[spriteName] = spritePath; m_dirty = true; } string atlasName = GetPackageTag(assetPath); if (string.IsNullOrEmpty(atlasName)) { bool keepRaw = IsKeepRawImage(assetPath); if (!keepRaw) { Debug.LogError($"empty packingTag of asset :{assetPath} !!!"); } return; } else { List ret; if (!m_allASprites.TryGetValue(atlasName, out ret)) { ret = new List(); m_allASprites.Add(atlasName, ret); } if (!ret.Contains(assetPath)) { ret.Add(assetPath); m_dirty = true; if (!m_dirtyAtlasList.Contains(atlasName)) { m_dirtyAtlasList.Add(atlasName); } } } } public static void OnDeleteSprite(string assetPath) { if (assetPath.StartsWith("Assets/UIRaw_Delete")) { return; } if (!assetPath.StartsWith(UISpritePath)) { return; } Init(); string atlasName = GetPackageTag(assetPath); if (!m_allASprites.TryGetValue(atlasName, out var ret)) { return; } //改成文件名的匹配 if (!ret.Exists(s => Path.GetFileName(s) == Path.GetFileName(assetPath))) { return; } if (assetPath.StartsWith(UISpritePath)) { var spriteName = Path.GetFileNameWithoutExtension(assetPath); if (m_uiAtlasMap.ContainsKey(spriteName)) { m_uiAtlasMap.Remove(spriteName); m_dirty = true; } } ret.Remove(assetPath); m_dirty = true; if (!m_dirtyAtlasList.Contains(atlasName)) { m_dirtyAtlasList.Add(atlasName); } } #region 更新图集 public static void SaveAtlas(string atlasName, bool isUI) { List spriteList = new List(); if (m_allASprites.ContainsKey(atlasName)) { var list = m_allASprites[atlasName]; list.Sort(StringComparer.Ordinal); foreach (var s in list) { var sprite = AssetDatabase.LoadAssetAtPath(s); if (sprite != null) { spriteList.Add(sprite); } } } var path = $"{NormalAtlasDir}/{atlasName}.spriteatlas"; if (spriteList.Count == 0) { if (File.Exists(path)) { AssetDatabase.DeleteAsset(path); } return; } var atlas = new SpriteAtlas(); var setting = new SpriteAtlasPackingSettings { blockOffset = 1, padding = 2, enableRotation = true }; bool isOpaque = atlasName.Contains("Opaque"); var textureSetting = new SpriteAtlasTextureSettings { generateMipMaps = false, sRGB = true, filterMode = FilterMode.Bilinear }; atlas.SetTextureSettings(textureSetting); var iphonePlatformSetting = atlas.GetPlatformSettings("iPhone"); if (!iphonePlatformSetting.overridden) { iphonePlatformSetting.overridden = true; iphonePlatformSetting.format = isOpaque ? TextureImporterFormat.ASTC_5x5 : TextureImporterFormat.ASTC_5x5; iphonePlatformSetting.compressionQuality = 100; atlas.SetPlatformSettings(iphonePlatformSetting); } var androidPlatformSetting = atlas.GetPlatformSettings("Android"); if (isOpaque && !androidPlatformSetting.overridden) { androidPlatformSetting.overridden = true; androidPlatformSetting.format = TextureImporterFormat.ETC_RGB4; androidPlatformSetting.compressionQuality = 100; atlas.SetPlatformSettings(androidPlatformSetting); } atlas.SetPackingSettings(setting); atlas.Add(spriteList.ToArray()); AssetDatabase.CreateAsset(atlas, path); } #endregion #region 重新生成图集 private static Dictionary> m_tempAllASprites = new Dictionary>(); [MenuItem("TEngine/图集/重新生成UI图集")] static void ForceGenAtlas() { Init(); List needSaveAtlas = new List(); m_tempAllASprites.Clear(); var findAssets = AssetDatabase.FindAssets("t:sprite", new[] { UIAtlasPath }); foreach (var findAsset in findAssets) { var path = AssetDatabase.GUIDToAssetPath(findAsset); var atlasName = GetPackageTag(path); if (!m_tempAllASprites.TryGetValue(atlasName, out var spriteList)) { spriteList = new List(); m_tempAllASprites[atlasName] = spriteList; } if (!spriteList.Contains(path)) { spriteList.Add(path); } } //有变化的才刷 var iter = m_tempAllASprites.GetEnumerator(); while (iter.MoveNext()) { bool needSave = false; var atlasName = iter.Current.Key; var newSpritesList = iter.Current.Value; List existSprites; if (m_allASprites.TryGetValue(atlasName, out existSprites)) { if (existSprites.Count != newSpritesList.Count) { needSave = true; existSprites.Clear(); existSprites.AddRange(newSpritesList); } else { for (int i = 0; i < newSpritesList.Count; i++) { if (!existSprites.Contains(newSpritesList[i])) { needSave = true; break; } } if (needSave) { existSprites.Clear(); existSprites.AddRange(newSpritesList); } } } else { needSave = true; m_allASprites.Add(atlasName, new List(newSpritesList)); } if (needSave && !needSaveAtlas.Contains(atlasName)) { needSaveAtlas.Add(atlasName); } } iter.Dispose(); foreach (var atlas in needSaveAtlas) { Debug.LogFormat("Gen atlas:{0}", atlas); SaveAtlas(atlas, true); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); SpriteAtlasUtility.PackAllAtlases(EditorUserBuildSettings.activeBuildTarget); Debug.Log("Gen end"); } #endregion }