因为新的项目(Unity4.7的版本环境)使用UI2DSprite的缘故,Atlas的build方式改成了tag的方式,图集也不再实时可见,每次打图优化图集的时间成本变得比较高,没办法,懒癌又起。。。
using UnityEngine; using System.Collections.Generic; using System.Collections; using UnityEditor; using System.IO; using System.Linq; public class PreviewTextureAtlasWinEditor : EditorWindow { private SortedDictionary<string, List<string>> texTagPathMap = new SortedDictionary<string, List<string>>(); private List<Texture2D> texLst = new List<Texture2D>(); private string textureAtlasTagToPreview = ""; private bool needRefresh = false; private float scaleRate = 1f; private float minScaleRate = 0.3f; private float maxScaleRate = 2f; private static PreviewTextureAtlasWinEditor win; [MenuItem("AtlasOptimize/图集预览 %#p", false, 10)] public static void PreviewTextureAtlas() { win = GetWindow<PreviewTextureAtlasWinEditor>(true, "预览图集", true); win.Show(); win.refreshTagMap(); } private int lastSelectedIndex = 0; private string[] tagArr; private bool useTag; void OnGUI() { EditorGUILayout.BeginHorizontal(); needRefresh = EditorGUILayout.Toggle("ForceRefresh:", needRefresh); useTag = EditorGUILayout.Toggle("UseTag:", useTag); if(useTag && tagArr != null && tagArr.Length != 0) { lastSelectedIndex = EditorGUILayout.Popup("AtlasTag", lastSelectedIndex, tagArr); textureAtlasTagToPreview = tagArr[lastSelectedIndex]; } else { textureAtlasTagToPreview = EditorGUILayout.TextField("当前搜索图集:", textureAtlasTagToPreview); } EditorGUILayout.EndHorizontal(); if (GUILayout.Button("预览图集")) { findEnd = false; findTexWithTag(textureAtlasTagToPreview); } if(findEnd) { float tLeft = 0f; float tTop = 70f; int tWidth = Mathf.IsPowerOfTwo(previewAtlas.width) ? previewAtlas.width : Mathf.NextPowerOfTwo(previewAtlas.width); int tHeight = Mathf.IsPowerOfTwo(previewAtlas.height) ? previewAtlas.height : Mathf.NextPowerOfTwo(previewAtlas.height); EditorGUI.DrawTextureTransparent(new Rect(tLeft, tTop, tWidth * scaleRate, tHeight * scaleRate), previewAtlas); EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Width:", GUILayout.Width(45)); EditorGUILayout.LabelField(tWidth.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Height:", GUILayout.Width(45)); EditorGUILayout.LabelField(tHeight.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("图元数:", GUILayout.Width(45)); EditorGUILayout.LabelField(rectArr.Length.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); scaleRate = EditorGUILayout.Slider("Scale", scaleRate, minScaleRate, maxScaleRate); EditorGUILayout.EndHorizontal(); if (Event.current.type == EventType.MouseDown) { var tMousePos = Event.current.mousePosition; var tWinPos = win.position; Vector2 tPos = new Vector2((tMousePos.x - tLeft) / (tWidth * scaleRate), 1f - (tMousePos.y - tTop) / (tHeight * scaleRate)); //屏幕坐标系与uv坐标系 for(int i = 0; i < rectArr.Length; i++) { if(rectArr[i].Contains(tPos)) { var tAssetPath = texTagPathMap[textureAtlasTagToPreview][i]; //EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(tAssetPath, typeof(Texture))); Selection.activeObject = AssetDatabase.LoadAssetAtPath(tAssetPath, typeof(Texture)); break; } } } if (Event.current.type == EventType.ScrollWheel) { float tDeltaScale = Event.current.delta.y / 150f; scaleRate += tDeltaScale; if(scaleRate < minScaleRate) { scaleRate = minScaleRate; } if(scaleRate > maxScaleRate) { scaleRate = maxScaleRate; } win.Repaint(); } } if (Event.current.type == EventType.KeyDown && tagArr != null && tagArr.Length != 0) { if(Event.current.keyCode == KeyCode.LeftArrow) { lastSelectedIndex--; if(lastSelectedIndex < 0) { lastSelectedIndex = tagArr.Length + lastSelectedIndex; } findEnd = false; win.Repaint(); } else if(Event.current.keyCode == KeyCode.RightArrow) { lastSelectedIndex++; if(lastSelectedIndex >= tagArr.Length) { lastSelectedIndex = lastSelectedIndex - tagArr.Length; } findEnd = false; win.Repaint(); } } } private void refreshTagMap() { var tAllTextureAssetsGUID = AssetDatabase.FindAssets("t:Texture").ToList<string>(); string tAssetPath; TextureImporter tTexImporter; for(int i = 0; i < tAllTextureAssetsGUID.Count; i++) //过滤 { tAssetPath = AssetDatabase.GUIDToAssetPath(tAllTextureAssetsGUID[i]); for(int m = 0; m < filterString.Count; m++) { if(tAssetPath.Contains(filterString[m])) { tAllTextureAssetsGUID.RemoveAt(i); i--; break; } } } if(needRefresh || texTagPathMap.Count == 0) { needRefresh = false; texTagPathMap.Clear(); //缓存 for (int i = 0; i < tAllTextureAssetsGUID.Count; ++i) { tAssetPath = AssetDatabase.GUIDToAssetPath(tAllTextureAssetsGUID[i]); tTexImporter = TextureImporter.GetAtPath(tAssetPath) as TextureImporter; if(string.IsNullOrEmpty(tTexImporter.spritePackingTag)) { continue; } if(!texTagPathMap.ContainsKey(tTexImporter.spritePackingTag)) { texTagPathMap[tTexImporter.spritePackingTag] = new List<string>(); } texTagPathMap[tTexImporter.spritePackingTag].Add(tAssetPath); if (EditorUtility.DisplayCancelableProgressBar("CacheTagPathing", tAssetPath, ((i + 1) * 1.0f) / tAllTextureAssetsGUID.Count)) { EditorUtility.ClearProgressBar(); return; } } EditorUtility.ClearProgressBar(); tagArr = texTagPathMap.Keys.ToArray(); } } private bool findEnd = false; IList<string> filterString = new List<string>() { "Resources", "Editor", "Plugins", //各种带过滤插件等 }; private void findTexWithTag(string _tag) { string tAssetPath; TextureImporter tTexImporter; refreshTagMap(); if(texTagPathMap.ContainsKey(textureAtlasTagToPreview)) { texLst.Clear(); int tCount = texTagPathMap[textureAtlasTagToPreview].Count; bool[] tReadableArr = new bool[tCount]; //状态备份 for(int i = 0; i < tCount; i++) { tAssetPath = texTagPathMap[textureAtlasTagToPreview][i]; tTexImporter = TextureImporter.GetAtPath(tAssetPath) as TextureImporter; tReadableArr[i] = tTexImporter.isReadable; tTexImporter.isReadable = true; AssetDatabase.SaveAssets(); AssetDatabase.ImportAsset(tAssetPath); //强制刷新 var tTex2D = AssetDatabase.LoadAssetAtPath(tAssetPath, typeof(Texture2D)) as Texture2D; texLst.Add(tTex2D); if (EditorUtility.DisplayCancelableProgressBar("PreviewAtlasing", tAssetPath, ((i + 1) * 1.0f) / tCount)) { EditorUtility.ClearProgressBar(); return; } } PackAtlas(texLst.ToArray()); EditorUtility.ClearProgressBar(); findEnd = true; //状态还原 for(int i = 0; i < tCount; i++) { tAssetPath = texTagPathMap[textureAtlasTagToPreview][i]; tTexImporter = TextureImporter.GetAtPath(tAssetPath) as TextureImporter; tTexImporter.isReadable = tReadableArr[i]; AssetDatabase.SaveAssets(); AssetDatabase.ImportAsset(tAssetPath); } } } private Texture2D previewAtlas = new Texture2D(1, 1); private Rect[] rectArr; public void PackAtlas(Texture2D[] _texArr, int _maxAtlasSize = 2048, int _padding = 0) { previewAtlas.Resize(1, 1); rectArr = previewAtlas.PackTextures(_texArr, _padding, _maxAtlasSize); } }
其中,做预览时,迫不得已,考虑到工程的股友问题已经成型,策划、美术对项目的修改标准已经稍乱,年底的时间,项目推进度、改bug,无奈,考虑到各种svn的状态修改与提交成本,只好先做状态备份预还原(此时时间会有所消耗),只好待项目版本告一段落,再开项目总结会议,重新再压一遍标准,标准若能严格执行,则又可省时一大步了。
考虑到美术或策划或下面的某执行程序可能再增加新的资源时,希望预览到新增美术资源的打图效果及图集大小,于是再进一步:
public class PreviewTextureAtlasByHandWinEditor : EditorWindow { private SortedDictionary<string, List<string>> texTagPathMap = new SortedDictionary<string, List<string>>(); private List<Texture2D> texLst = new List<Texture2D>(); private List<string> texPathLst = new List<string>(); private float scaleRate = 1f; private float minScaleRate = 0.3f; private float maxScaleRate = 2f; private static PreviewTextureAtlasByHandWinEditor win; [MenuItem("AtlasOptimize/预览图集ByHand", false, 11)] public static void PreviewTextureAtlas() { win = GetWindow<PreviewTextureAtlasByHandWinEditor>(true, "预览图集", true); win.Show(); } private Texture2D previewAtlas = new Texture2D(64, 64); private Rect[] rectArr = new Rect[0]; public void PackAtlas(Texture2D[] _texArr, int _maxAtlasSize = 2048, int _padding = 0) { previewAtlas.Resize(1, 1); rectArr = previewAtlas.PackTextures(_texArr, _padding, _maxAtlasSize); } private Vector2 lastMousePos = Vector2.zero; private string selectAssetPath; void OnGUI() { float tLeft = 0f; float tTop = 20f; int tWidth = Mathf.IsPowerOfTwo(previewAtlas.width) ? previewAtlas.width : Mathf.NextPowerOfTwo(previewAtlas.width); int tHeight = Mathf.IsPowerOfTwo(previewAtlas.height) ? previewAtlas.height : Mathf.NextPowerOfTwo(previewAtlas.height); var tRect = new Rect(tLeft, tTop, tWidth * scaleRate, tHeight * scaleRate); EditorGUI.DrawTextureTransparent(tRect, previewAtlas); EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Width:", GUILayout.Width(45)); EditorGUILayout.LabelField(tWidth.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Height:", GUILayout.Width(45)); EditorGUILayout.LabelField(tHeight.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("图元数:", GUILayout.Width(45)); EditorGUILayout.LabelField(rectArr.Length.ToString(), GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); scaleRate = EditorGUILayout.Slider("Scale", scaleRate, minScaleRate, maxScaleRate); EditorGUILayout.EndHorizontal(); if (Event.current.type == EventType.MouseDown) { var tMousePos = Event.current.mousePosition; var tWinPos = win.position; Vector2 tPos = new Vector2((tMousePos.x - tLeft) / (tWidth * scaleRate), 1f - (tMousePos.y - tTop) / (tHeight * scaleRate)); //屏幕坐标系与uv坐标系 for(int i = 0; i < rectArr.Length; i++) { if(rectArr[i].Contains(tPos)) { selectAssetPath = texPathLst[i]; //EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(selectAssetPath, typeof(Texture))); Selection.activeObject = AssetDatabase.LoadAssetAtPath(selectAssetPath, typeof(Texture)); break; } } } if(!string.IsNullOrEmpty(selectAssetPath) && Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete) { packTex(false, selectAssetPath); selectAssetPath = ""; win.Repaint(); } if (Event.current.type == EventType.ScrollWheel) { float tDeltaScale = Event.current.delta.y / 150f; scaleRate += tDeltaScale; if(scaleRate < minScaleRate) { scaleRate = minScaleRate; } if(scaleRate > maxScaleRate) { scaleRate = maxScaleRate; } win.Repaint(); } if (Event.current.type == EventType.DragUpdated) //DragExited触发时,鼠标位置会突变,暂未定位原因 { lastMousePos = Event.current.mousePosition; DragAndDrop.visualMode = DragAndDropVisualMode.Generic; } if (Event.current.type == EventType.DragExited) //动态拖入添加,删除texture { if(!tRect.Contains(lastMousePos)) { return; } if (DragAndDrop.paths != null && DragAndDrop.paths.Length > 0) { packTex(true, DragAndDrop.paths); //todo: 过滤非texture、文件夹 win.Repaint(); } } } private void packTex(bool _isAppend, params string[] _assetPath) { bool tIsDirty = false; if(_isAppend) { for(int i = 0; i < _assetPath.Length; i++) { if(texPathLst.Contains(_assetPath[i])) { continue; } texPathLst.Add(_assetPath[i]); tIsDirty = true; } } else { for(int i = 0; i < _assetPath.Length; i++) { if(!texPathLst.Contains(_assetPath[i])) { continue; } texPathLst.Remove(_assetPath[i]); tIsDirty = true; } } if(!tIsDirty) { return; } bool[] tReadableState = new bool[texPathLst.Count]; TextureImporter tTexImporter; texLst.Clear(); for(int i = 0; i < texPathLst.Count; i++) { tTexImporter = TextureImporter.GetAtPath(texPathLst[i]) as TextureImporter; tReadableState[i] = tTexImporter.isReadable; //状态备份 tTexImporter.isReadable = true; AssetDatabase.SaveAssets(); AssetDatabase.ImportAsset(texPathLst[i]); var tTex2D = AssetDatabase.LoadAssetAtPath(texPathLst[i], typeof(Texture2D)) as Texture2D; texLst.Add(tTex2D); } PackAtlas(texLst.ToArray()); //还原状态 for(int i = 0; i < texPathLst.Count; i++) { tTexImporter = TextureImporter.GetAtPath(texPathLst[i]) as TextureImporter; tTexImporter.isReadable = tReadableState[i]; AssetDatabase.SaveAssets(); AssetDatabase.ImportAsset(texPathLst[i]); } } }
为方便定位图集中的每个图元,两种工具都添加了图集到图元的索引(与unity自带的spritepacker相反,可以适当配合使用),都只是做了简单的预览优化之用,未突破unit已提供的不同打图选项设置。目前初版而已,待稍后标准重建,效率自然不成问题。