//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; /// /// Inspector class used to edit the UIAtlas. /// [CustomEditor(typeof(UIAtlas))] public class UIAtlasInspector : Editor { static public UIAtlasInspector instance; enum AtlasType { Normal, Reference, } UIAtlas mAtlas; AtlasType mType = AtlasType.Normal; UIAtlas mReplacement = null; void OnEnable () { instance = this; } void OnDisable () { instance = null; } /// /// Convenience function -- mark all widgets using the sprite as changed. /// void MarkSpriteAsDirty () { UISpriteData sprite = (mAtlas != null) ? mAtlas.GetSprite(NGUISettings.selectedSprite) : null; if (sprite == null) return; UISprite[] sprites = NGUITools.FindActive(); foreach (UISprite sp in sprites) { if (UIAtlas.CheckIfRelated(sp.atlas, mAtlas) && sp.spriteName == sprite.name) { UIAtlas atl = sp.atlas; sp.atlas = null; sp.atlas = atl; EditorUtility.SetDirty(sp); } } UILabel[] labels = NGUITools.FindActive(); foreach (UILabel lbl in labels) { if (lbl.bitmapFont != null && UIAtlas.CheckIfRelated(lbl.bitmapFont.atlas, mAtlas) && lbl.bitmapFont.UsesSprite(sprite.name)) { UIFont font = lbl.bitmapFont; lbl.bitmapFont = null; lbl.bitmapFont = font; EditorUtility.SetDirty(lbl); } } } /// /// Replacement atlas selection callback. /// void OnSelectAtlas (Object obj) { if (mReplacement != obj) { // Undo doesn't work correctly in this case... so I won't bother. //NGUIEditorTools.RegisterUndo("Atlas Change"); //NGUIEditorTools.RegisterUndo("Atlas Change", mAtlas); mAtlas.replacement = obj as UIAtlas; mReplacement = mAtlas.replacement; NGUITools.SetDirty(mAtlas); if (mReplacement == null) mType = AtlasType.Normal; } } /// /// Draw the inspector widget. /// public override void OnInspectorGUI () { NGUIEditorTools.SetLabelWidth(80f); mAtlas = target as UIAtlas; UISpriteData sprite = (mAtlas != null) ? mAtlas.GetSprite(NGUISettings.selectedSprite) : null; GUILayout.Space(6f); if (mAtlas.replacement != null) { mType = AtlasType.Reference; mReplacement = mAtlas.replacement; } GUILayout.BeginHorizontal(); AtlasType after = (AtlasType)EditorGUILayout.EnumPopup("Atlas Type", mType); NGUIEditorTools.DrawPadding(); GUILayout.EndHorizontal(); if (mType != after) { if (after == AtlasType.Normal) { mType = AtlasType.Normal; OnSelectAtlas(null); } else { mType = AtlasType.Reference; } } if (mType == AtlasType.Reference) { ComponentSelector.Draw(mAtlas.replacement, OnSelectAtlas, true); GUILayout.Space(6f); EditorGUILayout.HelpBox("You can have one atlas simply point to " + "another one. This is useful if you want to be " + "able to quickly replace the contents of one " + "atlas with another one, for example for " + "swapping an SD atlas with an HD one, or " + "replacing an English atlas with a Chinese " + "one. All the sprites referencing this atlas " + "will update their references to the new one.", MessageType.Info); if (mReplacement != mAtlas && mAtlas.replacement != mReplacement) { NGUIEditorTools.RegisterUndo("Atlas Change", mAtlas); mAtlas.replacement = mReplacement; NGUITools.SetDirty(mAtlas); } return; } //GUILayout.Space(6f); Material mat = EditorGUILayout.ObjectField("Material", mAtlas.spriteMaterial, typeof(Material), false) as Material; if (mAtlas.spriteMaterial != mat) { NGUIEditorTools.RegisterUndo("Atlas Change", mAtlas); mAtlas.spriteMaterial = mat; // Ensure that this atlas has valid import settings if (mAtlas.texture != null) NGUIEditorTools.ImportTexture(mAtlas.texture, false, false, !mAtlas.premultipliedAlpha); mAtlas.MarkAsChanged(); } if (mat != null) { TextAsset ta = EditorGUILayout.ObjectField("TP Import", null, typeof(TextAsset), false) as TextAsset; if (ta != null) { // Ensure that this atlas has valid import settings if (mAtlas.texture != null) NGUIEditorTools.ImportTexture(mAtlas.texture, false, false, !mAtlas.premultipliedAlpha); NGUIEditorTools.RegisterUndo("Import Sprites", mAtlas); NGUIJson.LoadSpriteData(mAtlas, ta); if (sprite != null) sprite = mAtlas.GetSprite(sprite.name); mAtlas.MarkAsChanged(); } float pixelSize = EditorGUILayout.FloatField("Pixel Size", mAtlas.pixelSize, GUILayout.Width(120f)); if (pixelSize != mAtlas.pixelSize) { NGUIEditorTools.RegisterUndo("Atlas Change", mAtlas); mAtlas.pixelSize = pixelSize; } } if (mAtlas.spriteMaterial != null) { Color blueColor = new Color(0f, 0.7f, 1f, 1f); Color greenColor = new Color(0.4f, 1f, 0f, 1f); if (sprite == null && mAtlas.spriteList.Count > 0) { string spriteName = NGUISettings.selectedSprite; if (!string.IsNullOrEmpty(spriteName)) sprite = mAtlas.GetSprite(spriteName); if (sprite == null) sprite = mAtlas.spriteList[0]; } if (sprite != null) { if (sprite == null) return; Texture2D tex = mAtlas.spriteMaterial.mainTexture as Texture2D; if (tex != null) { if (!NGUIEditorTools.DrawHeader("Sprite Details")) return; NGUIEditorTools.BeginContents(); GUILayout.Space(3f); NGUIEditorTools.DrawAdvancedSpriteField(mAtlas, sprite.name, SelectSprite, true); GUILayout.Space(6f); GUI.changed = false; GUI.backgroundColor = greenColor; NGUIEditorTools.IntVector sizeA = NGUIEditorTools.IntPair("Dimensions", "X", "Y", sprite.x, sprite.y); NGUIEditorTools.IntVector sizeB = NGUIEditorTools.IntPair(null, "Width", "Height", sprite.width, sprite.height); EditorGUILayout.Separator(); GUI.backgroundColor = blueColor; NGUIEditorTools.IntVector borderA = NGUIEditorTools.IntPair("Border", "Left", "Right", sprite.borderLeft, sprite.borderRight); NGUIEditorTools.IntVector borderB = NGUIEditorTools.IntPair(null, "Bottom", "Top", sprite.borderBottom, sprite.borderTop); EditorGUILayout.Separator(); GUI.backgroundColor = Color.white; NGUIEditorTools.IntVector padA = NGUIEditorTools.IntPair("Padding", "Left", "Right", sprite.paddingLeft, sprite.paddingRight); NGUIEditorTools.IntVector padB = NGUIEditorTools.IntPair(null, "Bottom", "Top", sprite.paddingBottom, sprite.paddingTop); if (GUI.changed) { NGUIEditorTools.RegisterUndo("Atlas Change", mAtlas); sprite.x = sizeA.x; sprite.y = sizeA.y; sprite.width = sizeB.x; sprite.height = sizeB.y; sprite.paddingLeft = padA.x; sprite.paddingRight = padA.y; sprite.paddingBottom = padB.x; sprite.paddingTop = padB.y; sprite.borderLeft = borderA.x; sprite.borderRight = borderA.y; sprite.borderBottom = borderB.x; sprite.borderTop = borderB.y; MarkSpriteAsDirty(); } GUILayout.Space(3f); GUILayout.BeginHorizontal(); if (GUILayout.Button("Duplicate")) { UIAtlasMaker.SpriteEntry se = UIAtlasMaker.DuplicateSprite(mAtlas, sprite.name); if (se != null) NGUISettings.selectedSprite = se.name; } if (GUILayout.Button("Save As...")) { #if UNITY_3_5 string path = EditorUtility.SaveFilePanel("Save As", NGUISettings.currentPath, sprite.name + ".png", "png"); #else string path = EditorUtility.SaveFilePanelInProject("Save As", sprite.name + ".png", "png", "Extract sprite into which file?", NGUISettings.currentPath); #endif if (!string.IsNullOrEmpty(path)) { NGUISettings.currentPath = System.IO.Path.GetDirectoryName(path); UIAtlasMaker.SpriteEntry se = UIAtlasMaker.ExtractSprite(mAtlas, sprite.name); if (se != null) { byte[] bytes = se.tex.EncodeToPNG(); File.WriteAllBytes(path, bytes); AssetDatabase.ImportAsset(path); if (se.temporaryTexture) DestroyImmediate(se.tex); } } } GUILayout.EndHorizontal(); NGUIEditorTools.EndContents(); } if (NGUIEditorTools.DrawHeader("Modify")) { NGUIEditorTools.BeginContents(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(20f); EditorGUILayout.BeginVertical(); NGUISettings.backgroundColor = EditorGUILayout.ColorField("Background", NGUISettings.backgroundColor); if (GUILayout.Button("Add a Shadow")) AddShadow(sprite); if (GUILayout.Button("Add a Soft Outline")) AddOutline(sprite); if (GUILayout.Button("Add a Transparent Border")) AddTransparentBorder(sprite); if (GUILayout.Button("Add a Clamped Border")) AddClampedBorder(sprite); if (GUILayout.Button("Add a Tiled Border")) AddTiledBorder(sprite); EditorGUI.BeginDisabledGroup(!sprite.hasBorder); if (GUILayout.Button("Crop Border")) CropBorder(sprite); EditorGUI.EndDisabledGroup(); EditorGUILayout.EndVertical(); GUILayout.Space(20f); EditorGUILayout.EndHorizontal(); NGUIEditorTools.EndContents(); } if (NGUIEditorTools.previousSelection != null) { GUILayout.Space(3f); GUI.backgroundColor = Color.green; if (GUILayout.Button("<< Return to " + NGUIEditorTools.previousSelection.name)) { NGUIEditorTools.SelectPrevious(); } GUI.backgroundColor = Color.white; } } } } /// /// Sprite selection callback. /// void SelectSprite (string spriteName) { if (NGUISettings.selectedSprite != spriteName) { NGUISettings.selectedSprite = spriteName; Repaint(); } } /// /// All widgets have a preview. /// public override bool HasPreviewGUI () { return true; } /// /// Draw the sprite preview. /// public override void OnPreviewGUI (Rect rect, GUIStyle background) { UISpriteData sprite = (mAtlas != null) ? mAtlas.GetSprite(NGUISettings.selectedSprite) : null; if (sprite == null) return; Texture2D tex = mAtlas.texture as Texture2D; if (tex != null) NGUIEditorTools.DrawSprite(tex, rect, sprite, Color.white); } /// /// Add a transparent border around the sprite. /// void AddTransparentBorder (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width; int h1 = se.tex.height; int w2 = w1 + 2; int h2 = h1 + 2; Color32[] c2 = NGUIEditorTools.AddBorder(se.tex.GetPixels32(), w1, h1); if (se.temporaryTexture) DestroyImmediate(se.tex); ++se.borderLeft; ++se.borderRight; ++se.borderTop; ++se.borderBottom; se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } /// /// Add a border around the sprite that extends the existing edge pixels. /// void AddClampedBorder (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width - se.borderLeft - se.borderRight; int h1 = se.tex.height - se.borderBottom - se.borderTop; int w2 = se.tex.width + 2; int h2 = se.tex.height + 2; Color32[] c1 = se.tex.GetPixels32(); Color32[] c2 = new Color32[w2 * h2]; for (int y2 = 0; y2 < h2; ++y2) { int y1 = se.borderBottom + NGUIMath.ClampIndex(y2 - se.borderBottom - 1, h1); for (int x2 = 0; x2 < w2; ++x2) { int x1 = se.borderLeft + NGUIMath.ClampIndex(x2 - se.borderLeft - 1, w1); c2[x2 + y2 * w2] = c1[x1 + y1 * se.tex.width]; } } if (se.temporaryTexture) DestroyImmediate(se.tex); ++se.borderLeft; ++se.borderRight; ++se.borderTop; ++se.borderBottom; se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } /// /// Add a border around the sprite that copies the pixels from the opposite side, making it possible for the sprite to tile without seams. /// void AddTiledBorder (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width - se.borderLeft - se.borderRight; int h1 = se.tex.height - se.borderBottom - se.borderTop; int w2 = se.tex.width + 2; int h2 = se.tex.height + 2; Color32[] c1 = se.tex.GetPixels32(); Color32[] c2 = new Color32[w2 * h2]; for (int y2 = 0; y2 < h2; ++y2) { int y1 = se.borderBottom + NGUIMath.RepeatIndex(y2 - se.borderBottom - 1, h1); for (int x2 = 0; x2 < w2; ++x2) { int x1 = se.borderLeft + NGUIMath.RepeatIndex(x2 - se.borderLeft - 1, w1); c2[x2 + y2 * w2] = c1[x1 + y1 * se.tex.width]; } } if (se.temporaryTexture) DestroyImmediate(se.tex); ++se.borderLeft; ++se.borderRight; ++se.borderTop; ++se.borderBottom; se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } /// /// Crop the border pixels around the sprite. /// void CropBorder (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width; int h1 = se.tex.height; int w2 = w1 - se.borderLeft - se.borderRight; int h2 = h1 - se.borderTop - se.borderBottom; Color32[] c1 = se.tex.GetPixels32(); Color32[] c2 = new Color32[w2 * h2]; for (int y2 = 0; y2 < h2; ++y2) { int y1 = y2 + se.borderBottom; for (int x2 = 0; x2 < w2; ++x2) { int x1 = x2 + se.borderLeft; c2[x2 + y2 * w2] = c1[x1 + y1 * w1]; } } se.borderLeft = 0; se.borderRight = 0; se.borderTop = 0; se.borderBottom = 0; if (se.temporaryTexture) DestroyImmediate(se.tex); se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } /// /// Add a dark shadow below and to the right of the sprite. /// void AddShadow (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width; int h1 = se.tex.height; int w2 = w1 + 2; int h2 = h1 + 2; Color32[] c2 = NGUIEditorTools.AddBorder(se.tex.GetPixels32(), w1, h1); NGUIEditorTools.AddShadow(c2, w2, h2, NGUISettings.backgroundColor); if (se.temporaryTexture) DestroyImmediate(se.tex); if ((se.borderLeft | se.borderRight | se.borderBottom | se.borderTop) != 0) { ++se.borderLeft; ++se.borderRight; ++se.borderTop; ++se.borderBottom; } se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } /// /// Add a dark shadowy outline around the sprite, giving it some visual depth. /// void AddOutline (UISpriteData sprite) { List sprites = new List(); UIAtlasMaker.ExtractSprites(mAtlas, sprites); UIAtlasMaker.SpriteEntry se = null; for (int i = 0; i < sprites.Count; ++i) { if (sprites[i].name == sprite.name) { se = sprites[i]; break; } } if (se != null) { int w1 = se.tex.width; int h1 = se.tex.height; int w2 = w1 + 2; int h2 = h1 + 2; Color32[] c2 = NGUIEditorTools.AddBorder(se.tex.GetPixels32(), w1, h1); NGUIEditorTools.AddDepth(c2, w2, h2, NGUISettings.backgroundColor); if (se.temporaryTexture) DestroyImmediate(se.tex); if ((se.borderLeft | se.borderRight | se.borderBottom | se.borderTop) != 0) { ++se.borderLeft; ++se.borderRight; ++se.borderTop; ++se.borderBottom; } se.tex = new Texture2D(w2, h2); se.tex.name = sprite.name; se.tex.SetPixels32(c2); se.tex.Apply(); se.temporaryTexture = true; UIAtlasMaker.UpdateAtlas(mAtlas, sprites); DestroyImmediate(se.tex); se.tex = null; } } }