因为新的项目(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已提供的不同打图选项设置。目前初版而已,待稍后标准重建,效率自然不成问题。