//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using UnityEditor; using System.Collections.Generic; /// /// Atlas maker lets you create atlases from a bunch of small textures. It's an alternative to using the external Texture Packer. /// using System.Collections; using System.IO; public class UIAtlasMaker : EditorWindow { #region //add by chenbin bool isShowBorrowModeDesc = false; bool isBorrowMode = false; //add by chenbin int runTimes = 0; #endregion static public UIAtlasMaker instance; public class SpriteEntry : UISpriteData { // Sprite texture -- original texture or a temporary texture public Texture2D tex; // Temporary game object -- used to prevent Unity from unloading the texture public GameObject tempGO; // Temporary material -- same usage as the temporary game object public Material tempMat; // Whether the texture is temporary and should be deleted public bool temporaryTexture = false; /// /// HACK: Prevent Unity from unloading temporary textures. /// Discovered by "alexkring": http://www.tasharen.com/forum/index.php?topic=3079.45 /// public void SetTexture(Color32[] newPixels, int newWidth, int newHeight) { Release(); temporaryTexture = true; tex = new Texture2D(newWidth, newHeight); tex.name = name; tex.SetPixels32(newPixels); tex.Apply(); tempMat = new Material(NGUISettings.atlas.spriteMaterial); tempMat.hideFlags = HideFlags.HideAndDontSave; tempMat.SetTexture("_MainTex", tex); tempGO = EditorUtility.CreateGameObjectWithHideFlags(name, HideFlags.HideAndDontSave, typeof(MeshRenderer)); tempGO.GetComponent().sharedMaterial = tempMat; } /// /// Release temporary resources. /// public void Release() { if (temporaryTexture) { Object.DestroyImmediate(tempGO); Object.DestroyImmediate(tempMat); Object.DestroyImmediate(tex); tempGO = null; tempMat = null; tex = null; temporaryTexture = false; } } } Vector2 mScroll = Vector2.zero; List mDelNames = new List(); UIAtlas mLastAtlas; void OnEnable() { instance = this; } void OnDisable() { instance = null; } /// /// Atlas selection callback. /// void OnSelectAtlas(Object obj) { runTimes = 0; //add by chenbin if (NGUISettings.atlas != obj) { NGUISettings.atlas = obj as UIAtlas; Repaint(); } } /// /// Refresh the window on selection. /// void OnSelectionChange() { mDelNames.Clear(); Repaint(); } /// /// Helper function that retrieves the list of currently selected textures. /// List GetSelectedTextures() { List textures = new List(); if (Selection.objects != null && Selection.objects.Length > 0) { Object[] objects = EditorUtility.CollectDependencies(Selection.objects); foreach (Object o in objects) { Texture tex = o as Texture; if (tex == null || tex.name == "Font Texture") continue; if (NGUISettings.atlas == null || NGUISettings.atlas.texture != tex) textures.Add(tex); } } return textures; } /// /// Load the specified list of textures as Texture2Ds, fixing their import properties as necessary. /// static List LoadTextures(List textures) { List list = new List(); foreach (Texture tex in textures) { Texture2D t2 = NGUIEditorTools.ImportTexture(tex, true, false, true); if (t2 != null) list.Add(t2); } return list; } /// /// Used to sort the sprites by pixels used /// static int Compare(SpriteEntry a, SpriteEntry b) { // A is null b is not b is greater so put it at the front of the list if (a == null && b != null) return 1; // A is not null b is null a is greater so put it at the front of the list if (a != null && b == null) return -1; // Get the total pixels used for each sprite int aPixels = a.width * a.height; int bPixels = b.width * b.height; if (aPixels > bPixels) return -1; else if (aPixels < bPixels) return 1; return 0; } /// /// Pack all of the specified sprites into a single texture, updating the outer and inner rects of the sprites as needed. /// static bool PackTextures(Texture2D tex, List sprites) { Texture2D[] textures = new Texture2D[sprites.Count]; Rect[] rects; #if UNITY_3_5 || UNITY_4_0 int maxSize = 4096; #else int maxSize = SystemInfo.maxTextureSize; #endif #if UNITY_ANDROID || UNITY_IPHONE || UNITY_IOS maxSize = Mathf.Min(maxSize, NGUISettings.allow4096 ? 4096 : 2048); #endif if (NGUISettings.unityPacking) { for (int i = 0; i < sprites.Count; ++i) textures [i] = sprites [i].tex; rects = tex.PackTextures(textures, NGUISettings.atlasPadding, maxSize); } else { sprites.Sort(Compare); for (int i = 0; i < sprites.Count; ++i) textures [i] = sprites [i].tex; rects = UITexturePacker.PackTextures(tex, textures, 4, 4, NGUISettings.atlasPadding, maxSize); } for (int i = 0; i < sprites.Count; ++i) { Rect rect = NGUIMath.ConvertToPixels(rects [i], tex.width, tex.height, true); // Apparently Unity can take the liberty of destroying temporary textures without any warning if (textures [i] == null) return false; // Make sure that we don't shrink the textures if (Mathf.RoundToInt(rect.width) != textures [i].width) return false; SpriteEntry se = sprites [i]; se.x = Mathf.RoundToInt(rect.x); se.y = Mathf.RoundToInt(rect.y); se.width = Mathf.RoundToInt(rect.width); se.height = Mathf.RoundToInt(rect.height); } return true; } /// /// Helper function that creates a single sprite list from both the atlas's sprites as well as selected textures. /// Dictionary value meaning: /// 0 = No change /// 1 = Update /// 2 = Add /// Dictionary GetSpriteList(List textures) { Dictionary spriteList = new Dictionary(); // If we have textures to work with, include them as well if (textures.Count > 0) { List texNames = new List(); // foreach (Texture tex in textures) texNames.Add(tex.name); // delete by chenbin #region add by chenbin foreach (Texture tex in textures) { if (NGUISettings.atlas != null && NGUISettings.atlas.isBorrowSpriteMode) { texNames.Add(getTextureName(tex));//add by chenbin } else { if(NGUISettings.addFolder2SpriteName) { texNames.Add(getTextureName(tex)); } else { texNames.Add(tex.name); } } } #endregion texNames.Sort(); foreach (string tex in texNames) { if (string.IsNullOrEmpty(tex)) continue;//add by chenbin // spriteList.Add(tex, 2); if (spriteList.ContainsKey(tex)) spriteList [tex] = 1; else spriteList.Add(tex, 2); } } if (NGUISettings.atlas != null) { BetterList spriteNames = NGUISettings.atlas.GetListOfSprites(); foreach (string sp in spriteNames) { if (spriteList.ContainsKey(sp)) spriteList [sp] = 1; else spriteList.Add(sp, 0); } } return spriteList; } /// /// Add a new sprite to the atlas, given the texture it's coming from and the packed rect within the atlas. /// static public UISpriteData AddSprite(List sprites, SpriteEntry se) { // See if this sprite already exists foreach (UISpriteData sp in sprites) { if (sp.name == se.name) { sp.CopyFrom(se); return sp; } } UISpriteData sprite = new UISpriteData(); sprite.CopyFrom(se); sprites.Add(sprite); return sprite; } /// /// Create a list of sprites using the specified list of textures. /// static public List CreateSprites(List textures) { List list = new List(); foreach (Texture tex in textures) { Texture2D oldTex = NGUIEditorTools.ImportTexture(tex, true, false, true); if (oldTex == null) oldTex = tex as Texture2D; if (oldTex == null) continue; // If we aren't doing trimming, just use the texture as-is if (!NGUISettings.atlasTrimming && !NGUISettings.atlasPMA) { SpriteEntry sprite = new SpriteEntry(); sprite.SetRect(0, 0, oldTex.width, oldTex.height); sprite.tex = oldTex; #region modify chenbin if (NGUISettings.addFolder2SpriteName) { sprite.name = getTextureName (oldTex); } else { sprite.name = oldTex.name; } #endregion sprite.temporaryTexture = false; list.Add(sprite); continue; } // If we want to trim transparent pixels, there is more work to be done Color32[] pixels = oldTex.GetPixels32(); int xmin = oldTex.width; int xmax = 0; int ymin = oldTex.height; int ymax = 0; int oldWidth = oldTex.width; int oldHeight = oldTex.height; // Find solid pixels if (NGUISettings.atlasTrimming) { for (int y = 0, yw = oldHeight; y < yw; ++y) { for (int x = 0, xw = oldWidth; x < xw; ++x) { Color32 c = pixels [y * xw + x]; if (c.a != 0) { if (y < ymin) ymin = y; if (y > ymax) ymax = y; if (x < xmin) xmin = x; if (x > xmax) xmax = x; } } } } else { xmin = 0; xmax = oldWidth - 1; ymin = 0; ymax = oldHeight - 1; } int newWidth = (xmax - xmin) + 1; int newHeight = (ymax - ymin) + 1; if (newWidth > 0 && newHeight > 0) { SpriteEntry sprite = new SpriteEntry(); sprite.x = 0; sprite.y = 0; sprite.width = oldTex.width; sprite.height = oldTex.height; // If the dimensions match, then nothing was actually trimmed if (!NGUISettings.atlasPMA && (newWidth == oldWidth && newHeight == oldHeight)) { sprite.tex = oldTex; // sprite.name = oldTex.name; #region modify chenbin if (NGUISettings.addFolder2SpriteName) { sprite.name = getTextureName (oldTex); } else { sprite.name = oldTex.name; } #endregion sprite.temporaryTexture = false; } else { // Copy the non-trimmed texture data into a temporary buffer Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { for (int x = 0; x < newWidth; ++x) { int newIndex = y * newWidth + x; int oldIndex = (ymin + y) * oldWidth + (xmin + x); if (NGUISettings.atlasPMA) newPixels [newIndex] = NGUITools.ApplyPMA(pixels [oldIndex]); else newPixels [newIndex] = pixels [oldIndex]; } } // Create a new texture // sprite.name = oldTex.name; #region modify chenbin if (NGUISettings.addFolder2SpriteName) { sprite.name = getTextureName (oldTex); } else { sprite.name = oldTex.name; } #endregion sprite.SetTexture(newPixels, newWidth, newHeight); // Remember the padding offset sprite.SetPadding(xmin, ymin, oldWidth - newWidth - xmin, oldHeight - newHeight - ymin); } list.Add(sprite); } } return list; } /// /// Release all temporary textures created for the sprites. /// static public void ReleaseSprites(List sprites) { foreach (SpriteEntry se in sprites) se.Release(); Resources.UnloadUnusedAssets(); } /// /// Replace the sprites within the atlas. /// static public void ReplaceSprites(UIAtlas atlas, List sprites) { // Get the list of sprites we'll be updating List spriteList = atlas.spriteList; List kept = new List(); // Run through all the textures we added and add them as sprites to the atlas for (int i = 0; i < sprites.Count; ++i) { SpriteEntry se = sprites [i]; UISpriteData sprite = AddSprite(spriteList, se); kept.Add(sprite); } // Remove unused sprites for (int i = spriteList.Count; i > 0;) { UISpriteData sp = spriteList [--i]; if (!kept.Contains(sp)) spriteList.RemoveAt(i); } // Sort the sprites so that they are alphabetical within the atlas atlas.SortAlphabetically(); atlas.MarkAsChanged(); } /// /// Duplicate the specified sprite. /// static public string DuplicateSprite(UIAtlas atlas, string spriteName) // modify by chenbin { if ((atlas == null || atlas.texture == null) && !atlas.isBorrowSpriteMode) return null; // modify by chenbin UISpriteData sd = atlas.GetSprite(spriteName); if (sd == null) return null; #region add by chenbin if (atlas.isBorrowSpriteMode) { UISpriteData s = new UISpriteData(); s.name = sd.name + " (Copy)"; ; s.path = sd.path; s.x = 0; s.y = 0; s.width = sd.width; s.height = sd.height; NGUISettings.atlas.spriteList.Add(s); NGUISettings.atlas.spriteMap [s.name] = NGUISettings.atlas.spriteList.Count - 1; atlas.MarkAsChanged(); return s.name; } else { #endregion Texture2D tex = NGUIEditorTools.ImportTexture(atlas.texture, true, true, false); SpriteEntry se = ExtractSprite(sd, tex); if (se != null) { se.name = se.name + " (Copy)"; List sprites = new List(); UIAtlasMaker.ExtractSprites(atlas, sprites); sprites.Add(se); UIAtlasMaker.UpdateAtlas(atlas, sprites); se.Release(); } else NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); return se.name; } } /// /// Extract the specified sprite from the atlas. /// static public SpriteEntry ExtractSprite(UIAtlas atlas, string spriteName) { if (atlas.texture == null) return null; UISpriteData sd = atlas.GetSprite(spriteName); if (sd == null) return null; Texture2D tex = NGUIEditorTools.ImportTexture(atlas.texture, true, true, false); SpriteEntry se = ExtractSprite(sd, tex); NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); return se; } /// /// Extract the specified sprite from the atlas texture. /// static SpriteEntry ExtractSprite(UISpriteData es, Texture2D tex) { return (tex != null) ? ExtractSprite(es, tex.GetPixels32(), tex.width, tex.height) : null; } /// /// Extract the specified sprite from the atlas texture. /// static SpriteEntry ExtractSprite(UISpriteData es, Color32[] oldPixels, int oldWidth, int oldHeight) { int xmin = Mathf.Clamp(es.x, 0, oldWidth); int ymin = Mathf.Clamp(es.y, 0, oldHeight); int xmax = Mathf.Min(xmin + es.width, oldWidth - 1); int ymax = Mathf.Min(ymin + es.height, oldHeight - 1); int newWidth = Mathf.Clamp(es.width, 0, oldWidth); int newHeight = Mathf.Clamp(es.height, 0, oldHeight); if (newWidth == 0 || newHeight == 0) return null; Color32[] newPixels = new Color32[newWidth * newHeight]; for (int y = 0; y < newHeight; ++y) { int cy = ymin + y; if (cy > ymax) cy = ymax; for (int x = 0; x < newWidth; ++x) { int cx = xmin + x; if (cx > xmax) cx = xmax; int newIndex = (newHeight - 1 - y) * newWidth + x; int oldIndex = (oldHeight - 1 - cy) * oldWidth + cx; newPixels [newIndex] = oldPixels [oldIndex]; } } // Create a new sprite SpriteEntry sprite = new SpriteEntry(); sprite.CopyFrom(es); sprite.SetRect(0, 0, newWidth, newHeight); sprite.SetTexture(newPixels, newWidth, newHeight); return sprite; } /// /// Extract sprites from the atlas, adding them to the list. /// static public void ExtractSprites(UIAtlas atlas, List finalSprites) { ShowProgress(0f); // Make the atlas texture readable Texture2D tex = NGUIEditorTools.ImportTexture(atlas.texture, true, true, false); if (tex != null) { Color32[] pixels = null; int width = tex.width; int height = tex.height; List sprites = atlas.spriteList; float count = sprites.Count; int index = 0; foreach (UISpriteData es in sprites) { ShowProgress((index++) / count); bool found = false; foreach (SpriteEntry fs in finalSprites) { if (es.name == fs.name) { fs.CopyBorderFrom(es); found = true; break; } } if (!found) { if (pixels == null) pixels = tex.GetPixels32(); SpriteEntry sprite = ExtractSprite(es, pixels, width, height); if (sprite != null) finalSprites.Add(sprite); } } } // The atlas no longer needs to be readable NGUIEditorTools.ImportTexture(atlas.texture, false, false, !atlas.premultipliedAlpha); ShowProgress(1f); } /// /// Combine all sprites into a single texture and save it to disk. /// static public bool UpdateTexture(UIAtlas atlas, List sprites) { // Get the texture for the atlas Texture2D tex = atlas.texture as Texture2D; string oldPath = (tex != null) ? AssetDatabase.GetAssetPath(tex.GetInstanceID()) : ""; string newPath = NGUIEditorTools.GetSaveableTexturePath(atlas); // Clear the read-only flag in texture file attributes if (System.IO.File.Exists(newPath)) { #if !UNITY_4_1 && !UNITY_4_0 && !UNITY_3_5 if (!AssetDatabase.IsOpenForEdit(newPath)) { Debug.LogError(newPath + " is not editable. Did you forget to do a check out?"); return false; } #endif System.IO.FileAttributes newPathAttrs = System.IO.File.GetAttributes(newPath); newPathAttrs &= ~System.IO.FileAttributes.ReadOnly; System.IO.File.SetAttributes(newPath, newPathAttrs); } bool newTexture = (tex == null || oldPath != newPath); if (newTexture) { // Create a new texture for the atlas tex = new Texture2D(1, 1, TextureFormat.ARGB32, false); } else { // Make the atlas readable so we can save it tex = NGUIEditorTools.ImportTexture(oldPath, true, false, false); } // Pack the sprites into this texture if (PackTextures(tex, sprites)) { byte[] bytes = tex.EncodeToPNG(); System.IO.File.WriteAllBytes(newPath, bytes); bytes = null; // Load the texture we just saved as a Texture2D AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); tex = NGUIEditorTools.ImportTexture(newPath, false, true, !atlas.premultipliedAlpha); // Update the atlas texture if (newTexture) { if (tex == null) Debug.LogError("Failed to load the created atlas saved as " + newPath); else atlas.spriteMaterial.mainTexture = tex; ReleaseSprites(sprites); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } return true; } else { if (!newTexture) NGUIEditorTools.ImportTexture(oldPath, false, true, !atlas.premultipliedAlpha); //Debug.LogError("Operation canceled: The selected sprites can't fit into the atlas.\n" + // "Keep large sprites outside the atlas (use UITexture), and/or use multiple atlases instead."); EditorUtility.DisplayDialog("Operation Canceled", "The selected sprites can't fit into the atlas.\n" + "Keep large sprites outside the atlas (use UITexture), and/or use multiple atlases instead", "OK"); return false; } } /// /// Show a progress bar. /// static public void ShowProgress(float val) { EditorUtility.DisplayProgressBar("Updating", "Updating the atlas, please wait...", val); } /// /// Add the specified texture to the atlas, or update an existing one. /// static public void AddOrUpdate(UIAtlas atlas, Texture2D tex) { if (atlas != null && tex != null) { List textures = new List(); textures.Add(tex); List sprites = CreateSprites(textures); ExtractSprites(atlas, sprites); UpdateAtlas(atlas, sprites); } } /// /// Add the specified texture to the atlas, or update an existing one. /// static public void AddOrUpdate(UIAtlas atlas, SpriteEntry se) { if (atlas != null && se != null) { List sprites = new List(); sprites.Add(se); ExtractSprites(atlas, sprites); UpdateAtlas(atlas, sprites); } } /// /// Update the sprites within the texture atlas, preserving the sprites that have not been selected. /// void UpdateAtlas(List textures, bool keepSprites) { // Create a list of sprites using the collected textures List sprites = CreateSprites(textures); if (sprites.Count > 0) { // Extract sprites from the atlas, filling in the missing pieces if (keepSprites) ExtractSprites(NGUISettings.atlas, sprites); // NOTE: It doesn't seem to be possible to undo writing to disk, and there also seems to be no way of // detecting an Undo event. Without either of these it's not possible to restore the texture saved to disk, // so the undo process doesn't work right. Because of this I'd rather disable it altogether until a solution is found. // The ability to undo this action is always useful //NGUIEditorTools.RegisterUndo("Update Atlas", UISettings.atlas, UISettings.atlas.texture, UISettings.atlas.material); // Update the atlas UpdateAtlas(NGUISettings.atlas, sprites); } else if (!keepSprites) { UpdateAtlas(NGUISettings.atlas, sprites); } } /// /// Update the sprite atlas, keeping only the sprites that are on the specified list. /// static public void UpdateAtlas(UIAtlas atlas, List sprites) { if (sprites.Count > 0) { // Combine all sprites into a single texture and save it if (UpdateTexture(atlas, sprites)) { // Replace the sprites within the atlas ReplaceSprites(atlas, sprites); } // Release the temporary textures ReleaseSprites(sprites); EditorUtility.ClearProgressBar(); return; } else { atlas.spriteList.Clear(); string path = NGUIEditorTools.GetSaveableTexturePath(atlas); atlas.spriteMaterial.mainTexture = null; if (!string.IsNullOrEmpty(path)) AssetDatabase.DeleteAsset(path); } atlas.MarkAsChanged(); Selection.activeGameObject = (NGUISettings.atlas != null) ? NGUISettings.atlas.gameObject : null; EditorUtility.ClearProgressBar(); } /// /// Draw the UI for this tool. /// void OnGUI() { if (mLastAtlas != NGUISettings.atlas) mLastAtlas = NGUISettings.atlas; bool update = false; bool replace = false; NGUIEditorTools.SetLabelWidth(84f); GUILayout.Space(3f); NGUIEditorTools.DrawHeader("Input", true); NGUIEditorTools.BeginContents(false); GUILayout.BeginHorizontal(); { ComponentSelector.Draw("Atlas", NGUISettings.atlas, OnSelectAtlas, true, GUILayout.MinWidth(80f)); EditorGUI.BeginDisabledGroup(NGUISettings.atlas == null); if (GUILayout.Button("New", GUILayout.Width(40f))) NGUISettings.atlas = null; EditorGUI.EndDisabledGroup(); } GUILayout.EndHorizontal(); #region //add by chenbin if (isBorrowMode) { GUI.color = Color.green; } if (GUILayout.Button("说明")) { isShowBorrowModeDesc = !isShowBorrowModeDesc; } if (isShowBorrowModeDesc) { GUILayout.Label("勾选isBorrowSpriteMode时,表明该atlas是空atlas,\n" + "当有sprit对象来borrow时,atlas会根据名字从\n" + "设置的路径中去取得图片资源。\n" + //"其中资源必须在Resources/下,\n" + "另外atlas必须要引用一个material,\n" + "就算引用的是个空material也行。\n"); } if (mLastAtlas != null && runTimes == 0) { runTimes++; isBorrowMode = mLastAtlas.isBorrowSpriteMode; // isUserUnity3DType = mLastAtlas.useUnity3DType; } if (mLastAtlas != null) { mLastAtlas.isBorrowSpriteMode = EditorGUILayout.Toggle("isBorrowSpriteMode", mLastAtlas.isBorrowSpriteMode); if (isBorrowMode != mLastAtlas.isBorrowSpriteMode) { isBorrowMode = mLastAtlas.isBorrowSpriteMode; UnityEditor.EditorUtility.SetDirty(NGUISettings.atlas.gameObject); } // if (mLastAtlas.isBorrowSpriteMode) { // mLastAtlas.useUnity3DType = EditorGUILayout.Toggle ("useUnity3DType", mLastAtlas.useUnity3DType); // if (isUserUnity3DType != mLastAtlas.useUnity3DType) { // isUserUnity3DType = mLastAtlas.useUnity3DType; // UnityEditor.EditorUtility.SetDirty (NGUISettings.atlas.gameObject); // } // } } GUI.color = Color.white; #endregion //end ======add by chenbin List textures = GetSelectedTextures(); if (NGUISettings.atlas != null) { Material mat = NGUISettings.atlas.spriteMaterial; Texture tex = NGUISettings.atlas.texture; // Material information GUILayout.BeginHorizontal(); { if (mat != null) { if (GUILayout.Button("Material", GUILayout.Width(76f))) Selection.activeObject = mat; GUILayout.Label(" " + mat.name); } else { GUI.color = Color.grey; GUILayout.Button("Material", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); // Texture atlas information GUILayout.BeginHorizontal(); { if (tex != null) { if (GUILayout.Button("Texture", GUILayout.Width(76f))) Selection.activeObject = tex; GUILayout.Label(" " + tex.width + "x" + tex.height); } else { GUI.color = Color.grey; GUILayout.Button("Texture", GUILayout.Width(76f)); GUI.color = Color.white; GUILayout.Label(" N/A"); } } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(); NGUISettings.atlasPadding = Mathf.Clamp(EditorGUILayout.IntField("Padding", NGUISettings.atlasPadding, GUILayout.Width(100f)), 0, 8); GUILayout.Label((NGUISettings.atlasPadding == 1 ? "pixel" : "pixels") + " between sprites"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.atlasTrimming = EditorGUILayout.Toggle("Trim Alpha", NGUISettings.atlasTrimming, GUILayout.Width(100f)); GUILayout.Label("Remove empty space"); GUILayout.EndHorizontal(); bool fixedShader = false; if (NGUISettings.atlas != null) { Material mat = NGUISettings.atlas.spriteMaterial; if (mat != null) { Shader shader = mat.shader; if (shader != null) { if (shader.name == "Unlit/Transparent Colored") { NGUISettings.atlasPMA = false; fixedShader = true; } else if (shader.name == "Unlit/Premultiplied Colored") { NGUISettings.atlasPMA = true; fixedShader = true; } } } } if (!fixedShader) { GUILayout.BeginHorizontal(); NGUISettings.atlasPMA = EditorGUILayout.Toggle("PMA Shader", NGUISettings.atlasPMA, GUILayout.Width(100f)); GUILayout.Label("Pre-multiplied alpha", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); } //GUILayout.BeginHorizontal(); //NGUISettings.keepPadding = EditorGUILayout.Toggle("Keep Padding", NGUISettings.keepPadding, GUILayout.Width(100f)); //GUILayout.Label("or replace with trimmed pixels", GUILayout.MinWidth(70f)); //GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.unityPacking = EditorGUILayout.Toggle("Unity Packer", NGUISettings.unityPacking, GUILayout.Width(100f)); GUILayout.Label("or custom packer", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); NGUISettings.trueColorAtlas = EditorGUILayout.Toggle("Truecolor", NGUISettings.trueColorAtlas, GUILayout.Width(100f)); GUILayout.Label("force ARGB32 textures", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); if (!NGUISettings.unityPacking) { GUILayout.BeginHorizontal(); NGUISettings.forceSquareAtlas = EditorGUILayout.Toggle("Force Square", NGUISettings.forceSquareAtlas, GUILayout.Width(100f)); GUILayout.Label("if on, forces a square atlas texture", GUILayout.MinWidth(70f)); GUILayout.EndHorizontal(); } #if UNITY_IPHONE || UNITY_ANDROID || UNITY_IOS GUILayout.BeginHorizontal(); NGUISettings.allow4096 = EditorGUILayout.Toggle("4096x4096", NGUISettings.allow4096, GUILayout.Width(100f)); GUILayout.Label("if off, limit atlases to 2048x2048"); GUILayout.EndHorizontal(); #endif #region add by chenbin GUILayout.BeginHorizontal(); NGUISettings.addFolder2SpriteName = EditorGUILayout.Toggle("Folder add SpriteName", NGUISettings.addFolder2SpriteName, GUILayout.Width(100f)); GUILayout.Label("Add folder name to SpriteName"); GUILayout.EndHorizontal(); #endregion NGUIEditorTools.EndContents(); if (NGUISettings.atlas != null) { GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (textures.Count > 0) { update = GUILayout.Button("Add/Update"); } else if (GUILayout.Button("View Sprites")) { SpriteSelector.ShowSelected(); } GUILayout.Space(20f); GUILayout.EndHorizontal(); } else { EditorGUILayout.HelpBox("You can create a new atlas by selecting one or more textures in the Project View window, then clicking \"Create\".", MessageType.Info); EditorGUI.BeginDisabledGroup(textures.Count == 0); GUILayout.BeginHorizontal(); GUILayout.Space(20f); bool create = GUILayout.Button("Create"); GUILayout.Space(20f); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); if (create) { string path = EditorUtility.SaveFilePanelInProject("Save As", "New Atlas.prefab", "prefab", "Save atlas as...", NGUISettings.currentPath); if (!string.IsNullOrEmpty(path)) { NGUISettings.currentPath = System.IO.Path.GetDirectoryName(path); GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; string matPath = path.Replace(".prefab", ".mat"); replace = true; // Try to load the material Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find(NGUISettings.atlasPMA ? "Unlit/Premultiplied Colored" : "Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } // Create a new prefab for the atlas Object prefab = (go != null) ? go : PrefabUtility.CreateEmptyPrefab(path); // Create a new game object for the atlas string atlasName = path.Replace(".prefab", ""); atlasName = atlasName.Substring(path.LastIndexOfAny(new char[] { '/', '\\' }) + 1); go = new GameObject(atlasName); go.AddComponent().spriteMaterial = mat; // Update the prefab PrefabUtility.ReplacePrefab(go, prefab); DestroyImmediate(go); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Select the atlas go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; NGUISettings.atlas = go.GetComponent(); Selection.activeGameObject = go; } } } string selection = null; Dictionary spriteList = GetSpriteList(textures); if (spriteList.Count > 0) { NGUIEditorTools.DrawHeader("Sprites", true); { GUILayout.BeginHorizontal(); GUILayout.Space(3f); GUILayout.BeginVertical(); mScroll = GUILayout.BeginScrollView(mScroll); bool delete = false; int index = 0; foreach (KeyValuePair iter in spriteList) { ++index; GUILayout.Space(-1f); bool highlight = (UIAtlasInspector.instance != null) && (NGUISettings.selectedSprite == iter.Key); GUI.backgroundColor = highlight ? Color.white : new Color(0.8f, 0.8f, 0.8f); GUILayout.BeginHorizontal(NGUIEditorTools.textArea, GUILayout.MinHeight(20f)); GUI.backgroundColor = Color.white; GUILayout.Label(index.ToString(), GUILayout.Width(24f)); if (GUILayout.Button(iter.Key, "TextField", GUILayout.Height(20f))) selection = iter.Key; if (iter.Value == 2) { GUI.color = Color.green; GUILayout.Label("Add", GUILayout.Width(27f)); GUI.color = Color.white; } else if (iter.Value == 1) { GUI.color = Color.cyan; GUILayout.Label("Update", GUILayout.Width(45f)); GUI.color = Color.white; } else { if (mDelNames.Contains(iter.Key)) { GUI.backgroundColor = Color.red; if (GUILayout.Button("Delete", GUILayout.Width(60f))) { delete = true; } GUI.backgroundColor = Color.green; if (GUILayout.Button("X", GUILayout.Width(22f))) { mDelNames.Remove(iter.Key); delete = false; } GUI.backgroundColor = Color.white; } else { // If we have not yet selected a sprite for deletion, show a small "X" button if (GUILayout.Button("X", GUILayout.Width(22f))) mDelNames.Add(iter.Key); } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(3f); GUILayout.EndHorizontal(); // If this sprite was marked for deletion, remove it from the atlas if (delete) { List sprites = new List(); if (!NGUISettings.atlas.isBorrowSpriteMode) {//add by chenbin ExtractSprites(NGUISettings.atlas, sprites); for (int i = sprites.Count; i > 0;) { SpriteEntry ent = sprites [--i]; if (mDelNames.Contains(ent.name)) sprites.RemoveAt(i); } UpdateAtlas(NGUISettings.atlas, sprites); mDelNames.Clear(); NGUIEditorTools.RepaintSprites(); #region add by chenbin } else { List list = NGUISettings.atlas.spriteList; foreach (string n in mDelNames) { foreach (UISpriteData s in list) { if (s.name == n) { list.Remove(s); break; } } } NGUISettings.atlas.spriteMap.Clear(); for (int i = 0; i < NGUISettings.atlas.spriteList.Count; i++) { UISpriteData tmpsp = NGUISettings.atlas.spriteList [i]; NGUISettings.atlas.spriteMap [tmpsp.name] = i; } UnityEditor.EditorUtility.SetDirty(NGUISettings.atlas.gameObject); } #endregion } else if (update) { if (!isBorrowMode) { //modify by chenbin UpdateAtlas(textures, true); } else { UpdateAtlas_BorrowMode(textures); //add by chenbin } } else if (replace) UpdateAtlas(textures, false); if (NGUISettings.atlas != null && !string.IsNullOrEmpty(selection)) { NGUIEditorTools.SelectSprite(selection); } else if (update || replace) { NGUIEditorTools.UpgradeTexturesToSprites(NGUISettings.atlas); NGUIEditorTools.RepaintSprites(); } } } if (NGUISettings.atlas != null && textures.Count == 0) EditorGUILayout.HelpBox("You can reveal more options by selecting one or more textures in the Project View window.", MessageType.Info); // Uncomment this line if you want to be able to force-sort the atlas //if (NGUISettings.atlas != null && GUILayout.Button("Sort Alphabetically")) NGUISettings.atlas.SortAlphabetically(); } #region //add by chenbin //add by chenbin void UpdateAtlas_BorrowMode(List textures) { UpdateAtlas_BorrowMode(NGUISettings.atlas, textures); } public static void UpdateAtlas_BorrowMode(UIAtlas atlas, List textures) { if (atlas == null || textures.Count == 0) { return; } bool isHadOld = false; foreach (Texture tex in textures) { isHadOld = false; //chang textures type string path = AssetDatabase.GetAssetPath(tex); TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; textureImporter.textureType = TextureImporterType.GUI; textureImporter.mipmapEnabled = false; textureImporter.wrapMode = TextureWrapMode.Clamp; textureImporter.alphaIsTransparency = true; textureImporter.npotScale = TextureImporterNPOTScale.None; textureImporter.filterMode = FilterMode.Trilinear; //改成这种模式好像更省内存 AssetDatabase.ImportAsset(path); path = getTexturePath(tex); if (path == null) continue; string newName = getTextureName(tex); foreach (DictionaryEntry item in atlas.spriteMap) { if (newName == (string)(item.Key)) { UISpriteData sp = atlas.spriteList [(int)(item.Value)]; sp.path = path; // sp.outer = new Rect (0, 0, tex.width, tex.height); //sp.inner = new Rect (0, 0, 0, 0); sp.x = 0; sp.y = 0; sp.width = tex.width; sp.height = tex.height; isHadOld = true; break; } } if (isHadOld) { continue; } UISpriteData s = new UISpriteData(); s.name = newName; s.path = path; // s.outer = new Rect (0, 0, tex.width, tex.height); // s.inner = new Rect (0, 0, 0, 0); s.x = 0; s.y = 0; s.width = tex.width; s.height = tex.height; atlas.spriteList.Add(s); atlas.spriteMap [newName] = atlas.spriteList.Count - 1; } //UISettings.atlas.MarkAsDirty (); textures.Clear(); Resources.UnloadUnusedAssets(); UnityEditor.EditorUtility.SetDirty(atlas.gameObject); } public static string getTexturePath(Texture tex) { if (tex == null) { Debug.LogError("getTexturePath: tex == null"); return ""; } string tmpPath = AssetDatabase.GetAssetPath(tex.GetInstanceID()); tmpPath = filter(tmpPath); Debug.Log(tmpPath); return tmpPath; } public static string getTextureName(Texture tex) { if (tex == null) { Debug.LogError("getTexturePath: tex == null"); return ""; } string tmpPath = AssetDatabase.GetAssetPath(tex.GetInstanceID()); tmpPath = Path.GetDirectoryName(tmpPath); tmpPath = Path.GetFileName(tmpPath); return tmpPath + "_" + tex.name; } static string filter(string path) { ArrayList replaces = new ArrayList(); string basePath = Application.dataPath; replaces.Add(basePath + "/StreamingAssets/"); replaces.Add(basePath + "/Resources/"); replaces.Add(basePath); replaces.Add("Assets/"); string str = ""; for (int i = 0; i < replaces.Count; i++) { str = replaces [i].ToString(); path = path.Replace(str, ""); } return path; } #endregion }