//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using System.Collections.Generic; using System; /// /// UI Atlas contains a collection of sprites inside one large texture atlas. /// [AddComponentMenu("NGUI/UI/Atlas")] public class UIAtlas : MonoBehaviour { // Legacy functionality, removed in 3.0. Do not use. [System.Serializable] class Sprite { public string name = "Unity Bug"; public Rect outer = new Rect(0f, 0f, 1f, 1f); public Rect inner = new Rect(0f, 0f, 1f, 1f); public bool rotated = false; // Padding is needed for trimmed sprites and is relative to sprite width and height public float paddingLeft = 0f; public float paddingRight = 0f; public float paddingTop = 0f; public float paddingBottom = 0f; public bool hasPadding { get { return paddingLeft != 0f || paddingRight != 0f || paddingTop != 0f || paddingBottom != 0f; } } } /// /// Legacy functionality, removed in 3.0. Do not use. /// enum Coordinates { Pixels, TexCoords, } // Material used by this atlas. Name is kept only for backwards compatibility, it used to be public. [HideInInspector][SerializeField] Material material; // List of all sprites inside the atlas. Name is kept only for backwards compatibility, it used to be public. [HideInInspector][SerializeField] List mSprites = new List(); // Size in pixels for the sake of MakePixelPerfect functions. [HideInInspector][SerializeField] float mPixelSize = 1f; // Replacement atlas can be used to completely bypass this atlas, pulling the data from another one instead. [HideInInspector][SerializeField] UIAtlas mReplacement; // Legacy functionality -- do not use [HideInInspector][SerializeField] Coordinates mCoordinates = Coordinates.Pixels; [HideInInspector][SerializeField] List sprites = new List(); // Whether the atlas is using a pre-multiplied alpha material. -1 = not checked. 0 = no. 1 = yes. int mPMA = -1; // Dictionary lookup to speed up sprite retrieval at run-time Dictionary mSpriteIndices = new Dictionary(); //当前Atlas所属的窗体 private GameObject mOwnerForm = null; public GameObject OwnerForm { get { return mOwnerForm; } set { mOwnerForm = value; } } /// /// Material used by the atlas. /// public Material spriteMaterial { get { return (mReplacement != null) ? mReplacement.spriteMaterial : material; } set { if (mReplacement != null) { mReplacement.spriteMaterial = value; } else { if (material == null) { mPMA = 0; material = value; } else { MarkAsChanged(); mPMA = -1; material = value; MarkAsChanged(); } } } } /// /// Whether the atlas is using a premultiplied alpha material. /// public bool premultipliedAlpha { get { if (mReplacement != null) return mReplacement.premultipliedAlpha; if (mPMA == -1) { Material mat = spriteMaterial; mPMA = (mat != null && mat.shader != null && mat.shader.name.Contains("Premultiplied")) ? 1 : 0; } return (mPMA == 1); } } /// /// List of sprites within the atlas. /// public List spriteList { get { if (mReplacement != null) return mReplacement.spriteList; if (mSprites.Count == 0) Upgrade(); return mSprites; } set { if (mReplacement != null) { mReplacement.spriteList = value; } else { mSprites = value; } } } /// /// Texture used by the atlas. /// public Texture texture { get { return (mReplacement != null) ? mReplacement.texture : (material != null ? material.mainTexture as Texture : null); } } /// /// Pixel size is a multiplier applied to widgets dimensions when performing MakePixelPerfect() pixel correction. /// Most obvious use would be on retina screen displays. The resolution doubles, but with UIRoot staying the same /// for layout purposes, you can still get extra sharpness by switching to an HD atlas that has pixel size set to 0.5. /// public float pixelSize { get { return (mReplacement != null) ? mReplacement.pixelSize : mPixelSize; } set { if (mReplacement != null) { mReplacement.pixelSize = value; } else { float val = Mathf.Clamp(value, 0.25f, 4f); if (mPixelSize != val) { mPixelSize = val; MarkAsChanged(); } } } } /// /// Setting a replacement atlas value will cause everything using this atlas to use the replacement atlas instead. /// Suggested use: set up all your widgets to use a dummy atlas that points to the real atlas. Switching that atlas /// to another one (for example an HD atlas) is then a simple matter of setting this field on your dummy atlas. /// public UIAtlas replacement { get { return mReplacement; } set { UIAtlas rep = value; if (rep == this) rep = null; if (mReplacement != rep) { if (rep != null && rep.replacement == this) rep.replacement = null; //根据实际的使用情况,当前图集被修改了,不需要重新修改图集mReplacement. 2015.12.16 -- by gzg //if (mReplacement != null) MarkAsChanged(); mReplacement = rep; if (rep != null) material = null; MarkAsChanged(); } } } /// /// Convenience function that retrieves a sprite by name. /// public UISpriteData GetSprite (string name) { if (mReplacement != null) { return mReplacement.GetSprite(name); } else if (!string.IsNullOrEmpty(name)) { if (mSprites.Count == 0) Upgrade(); if (mSprites.Count == 0) return null; // O(1) lookup via a dictionary #if UNITY_EDITOR if (Application.isPlaying) #endif { // The number of indices differs from the sprite list? Rebuild the indices. if (mSpriteIndices.Count != mSprites.Count) MarkSpriteListAsChanged(); int index; if (mSpriteIndices.TryGetValue(name, out index)) { // If the sprite is present, return it as-is if (index > -1 && index < mSprites.Count) return mSprites[index]; // The sprite index was out of range -- perhaps the sprite was removed? Rebuild the indices. MarkSpriteListAsChanged(); // Try to look up the index again return mSpriteIndices.TryGetValue(name, out index) ? mSprites[index] : null; } } //非运行时才执行此代码 if(!Application.isPlaying) { // Sequential O(N) lookup. for (int i = 0, imax = mSprites.Count; i < imax; ++i) { UISpriteData s = mSprites[i]; // string.Equals doesn't seem to work with Flash export if (!string.IsNullOrEmpty(s.name) && name == s.name) { #if UNITY_EDITOR if (!Application.isPlaying) return s; #endif // If this point was reached then the sprite is present in the non-indexed list, // so the sprite indices should be updated. MarkSpriteListAsChanged(); return s; } } } } return null; } /// /// Convenience function that returns the name of a random sprite that begins with the specified value. /// public string GetRandomSprite (string startsWith) { if (GetSprite(startsWith) == null) { System.Collections.Generic.List sprites = spriteList; System.Collections.Generic.List choices = new System.Collections.Generic.List(); foreach (UISpriteData sd in sprites) { if (sd.name.StartsWith(startsWith)) choices.Add(sd.name); } return (choices.Count > 0) ? choices[UnityEngine.Random.Range(0, choices.Count)] : null; } return startsWith; } /// /// Rebuild the sprite indices. Call this after modifying the spriteList at run time. /// public void MarkSpriteListAsChanged () { #if UNITY_EDITOR if (Application.isPlaying) #endif { mSpriteIndices.Clear(); for (int i = 0, imax = mSprites.Count; i < imax; ++i) mSpriteIndices[mSprites[i].name] = i; } } /// /// Sort the list of sprites within the atlas, making them alphabetical. /// public void SortAlphabetically () { mSprites.Sort(delegate(UISpriteData s1, UISpriteData s2) { return s1.name.CompareTo(s2.name); }); #if UNITY_EDITOR NGUITools.SetDirty(this); #endif } /// /// Convenience function that retrieves a list of all sprite names. /// public BetterList GetListOfSprites () { if (mReplacement != null) return mReplacement.GetListOfSprites(); if (mSprites.Count == 0) Upgrade(); BetterList list = new BetterList(); for (int i = 0, imax = mSprites.Count; i < imax; ++i) { UISpriteData s = mSprites[i]; if (s != null && !string.IsNullOrEmpty(s.name)) list.Add(s.name); } return list; } /// /// Convenience function that retrieves a list of all sprite names that contain the specified phrase /// public BetterList GetListOfSprites (string match) { if (mReplacement) return mReplacement.GetListOfSprites(match); if (string.IsNullOrEmpty(match)) return GetListOfSprites(); if (mSprites.Count == 0) Upgrade(); BetterList list = new BetterList(); // First try to find an exact match for (int i = 0, imax = mSprites.Count; i < imax; ++i) { UISpriteData s = mSprites[i]; if (s != null && !string.IsNullOrEmpty(s.name) && string.Equals(match, s.name, StringComparison.OrdinalIgnoreCase)) { list.Add(s.name); return list; } } // No exact match found? Split up the search into space-separated components. string[] keywords = match.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < keywords.Length; ++i) keywords[i] = keywords[i].ToLower(); // Try to find all sprites where all keywords are present for (int i = 0, imax = mSprites.Count; i < imax; ++i) { UISpriteData s = mSprites[i]; if (s != null && !string.IsNullOrEmpty(s.name)) { string tl = s.name.ToLower(); int matches = 0; for (int b = 0; b < keywords.Length; ++b) { if (tl.Contains(keywords[b])) ++matches; } if (matches == keywords.Length) list.Add(s.name); } } return list; } /// /// Helper function that determines whether the atlas uses the specified one, taking replacements into account. /// bool References (UIAtlas atlas) { if (atlas == null) return false; if (atlas == this) return true; return (mReplacement != null) ? mReplacement.References(atlas) : false; } /// /// Helper function that determines whether the two atlases are related. /// static public bool CheckIfRelated (UIAtlas a, UIAtlas b) { if (a == null || b == null) return false; return a == b || a.References(b) || b.References(a); } /// /// Mark all widgets associated with this atlas as having changed. /// public void MarkAsChanged () { #if UNITY_EDITOR NGUITools.SetDirty(gameObject); #endif //根据实际的使用情况,当前图集被修改了,不需要重新修改图集mReplacement. 2015.12.16 -- by gzg //if (mReplacement != null) mReplacement.MarkAsChanged(); if (mOwnerForm != null && !mOwnerForm.activeSelf) return; UISprite[] list = mOwnerForm==null ? NGUITools.FindActive() : mOwnerForm.GetComponentsInChildren(); for (int i = 0, imax = list.Length; i < imax; ++i) { UISprite sp = list[i]; if (CheckIfRelated(this, sp.atlas)) { UIAtlas atl = sp.atlas; sp.atlas = null; sp.atlas = atl; #if UNITY_EDITOR NGUITools.SetDirty(sp); #endif } } UIFont[] fonts = mOwnerForm == null ? Resources.FindObjectsOfTypeAll(typeof(UIFont)) as UIFont[]:new UIFont[0]; for (int i = 0, imax = fonts.Length; i < imax; ++i) { UIFont font = fonts[i]; if (CheckIfRelated(this, font.atlas)) { UIAtlas atl = font.atlas; font.atlas = null; font.atlas = atl; #if UNITY_EDITOR NGUITools.SetDirty(font); #endif } } UILabel[] labels = mOwnerForm==null? NGUITools.FindActive() : mOwnerForm.GetComponentsInChildren(); for (int i = 0, imax = labels.Length; i < imax; ++i) { UILabel lbl = labels[i]; if (lbl.bitmapFont != null && CheckIfRelated(this, lbl.bitmapFont.atlas)) { UIFont font = lbl.bitmapFont; lbl.bitmapFont = null; lbl.bitmapFont = font; #if UNITY_EDITOR NGUITools.SetDirty(lbl); #endif } } } /// /// Performs an upgrade from the legacy way of specifying data to the new one. /// bool Upgrade () { if (mReplacement) return mReplacement.Upgrade(); if (mSprites.Count == 0 && sprites.Count > 0 && material) { Texture tex = material.mainTexture; int width = (tex != null) ? tex.width : 512; int height = (tex != null) ? tex.height : 512; for (int i = 0; i < sprites.Count; ++i) { Sprite old = sprites[i]; Rect outer = old.outer; Rect inner = old.inner; if (mCoordinates == Coordinates.TexCoords) { NGUIMath.ConvertToPixels(outer, width, height, true); NGUIMath.ConvertToPixels(inner, width, height, true); } UISpriteData sd = new UISpriteData(); sd.name = old.name; sd.x = Mathf.RoundToInt(outer.xMin); sd.y = Mathf.RoundToInt(outer.yMin); sd.width = Mathf.RoundToInt(outer.width); sd.height = Mathf.RoundToInt(outer.height); sd.paddingLeft = Mathf.RoundToInt(old.paddingLeft * outer.width); sd.paddingRight = Mathf.RoundToInt(old.paddingRight * outer.width); sd.paddingBottom = Mathf.RoundToInt(old.paddingBottom * outer.height); sd.paddingTop = Mathf.RoundToInt(old.paddingTop * outer.height); sd.borderLeft = Mathf.RoundToInt(inner.xMin - outer.xMin); sd.borderRight = Mathf.RoundToInt(outer.xMax - inner.xMax); sd.borderBottom = Mathf.RoundToInt(outer.yMax - inner.yMax); sd.borderTop = Mathf.RoundToInt(inner.yMin - outer.yMin); mSprites.Add(sd); } sprites.Clear(); #if UNITY_EDITOR NGUITools.SetDirty(this); UnityEditor.AssetDatabase.SaveAssets(); #endif return true; } return false; } //卸载材质 -- 在运行是,删除atlas时,删除材质 public void DestoryAssets() { if (spriteMaterial != null) { //Debug.LogError("UIAtlas: DestoryAssets:" + name); var mainTex = spriteMaterial.mainTexture; if (mainTex != null) Resources.UnloadAsset(mainTex); if (spriteMaterial.HasProperty("_AlphaTex")) { var alphaTex = spriteMaterial.GetTexture("_AlphaTex"); if (alphaTex != null) Resources.UnloadAsset(alphaTex); } DestroyImmediate(spriteMaterial, true); } } //清理序列化资源 public void ClearSerializeFields() { material = null; mSprites = new List(); sprites = new List(); mReplacement = null; } }