//------------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2019 Tasharen Entertainment Inc //------------------------------------------------- using UnityEngine; using System.Collections.Generic; using System; /// /// Generic interface for the atlas class, making it possible to support both the prefab-based UIAtlas and scriptable object-based NGUIAtlas. /// public interface INGUIAtlas { /// /// Material used by the atlas. /// Material spriteMaterial { get; set; } /// /// List of sprites within the atlas. /// List spriteList { get; set; } /// /// Texture used by the atlas. /// Texture texture { get; } /// /// 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. /// float pixelSize { get; set; } /// /// Whether the atlas is using a premultiplied alpha material. /// bool premultipliedAlpha { get; } /// /// 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. /// INGUIAtlas replacement { get; set; } /// /// Convenience function that retrieves a sprite by name. /// UISpriteData GetSprite (string name); /// /// Convenience function that retrieves a list of all sprite names. /// BetterList GetListOfSprites (); /// /// Convenience function that retrieves a list of all sprite names that contain the specified phrase. /// BetterList GetListOfSprites (string match); /// /// Helper function that determines whether the atlas uses the specified one, taking replacements into account. /// bool References (INGUIAtlas atlas); /// /// Mark all widgets associated with this atlas as having changed. /// void MarkAsChanged (); /// /// Sort the list of sprites within the atlas, making them alphabetical. /// void SortAlphabetically (); } /// /// NGUI Atlas contains a collection of sprites inside one large texture atlas. It's saved as a ScriptableObject. /// public class NGUIAtlas : ScriptableObject, INGUIAtlas { /// /// 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] UnityEngine.Object mReplacement; // Whether the atlas is using a pre-multiplied alpha material. -1 = not checked. 0 = no. 1 = yes. [System.NonSerialized] int mPMA = -1; // Dictionary lookup to speed up sprite retrieval at run-time [System.NonSerialized] Dictionary mSpriteIndices = new Dictionary(); /// /// Material used by the atlas. /// public Material spriteMaterial { get { var rep = replacement; return (rep != null) ? rep.spriteMaterial : material; } set { var rep = replacement; if (rep != null) { rep.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 { var rep = replacement; if (rep != null) return rep.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 { var rep = replacement; if (rep != null) return rep.spriteList; return mSprites; } set { var rep = replacement; if (rep != null) rep.spriteList = value; else mSprites = value; } } /// /// Texture used by the atlas. /// public Texture texture { get { var rep = replacement; return (rep != null) ? rep.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 { var rep = replacement; return (rep != null) ? rep.pixelSize : mPixelSize; } set { var rep = replacement; if (rep != null) { rep.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 INGUIAtlas replacement { get { if (mReplacement == null) return null; return mReplacement as INGUIAtlas; } set { var rep = value; if (rep == this as INGUIAtlas) rep = null; if (mReplacement as INGUIAtlas != rep) { if (rep != null && rep.replacement == this as INGUIAtlas) rep.replacement = null; if (mReplacement != null) MarkAsChanged(); mReplacement = rep as UnityEngine.Object; if (rep != null) material = null; MarkAsChanged(); } } } /// /// Convenience function that retrieves a sprite by name. /// public UISpriteData GetSprite (string name) { var rep = replacement; if (rep != null) return rep.GetSprite(name); if (!string.IsNullOrEmpty(name)) { 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; } } // 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; } /// /// 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 () { var rep = replacement; if (rep != null) return rep.GetListOfSprites(); var 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) { var rep = replacement; if (rep != null) return rep.GetListOfSprites(match); if (string.IsNullOrEmpty(match)) return GetListOfSprites(); var list = new BetterList(); // First try to find an exact match for (int i = 0, imax = mSprites.Count; i < imax; ++i) { var 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. var 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) { var s = mSprites[i]; if (s != null && !string.IsNullOrEmpty(s.name)) { var tl = s.name.ToLower(); var 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. /// public bool References (INGUIAtlas atlas) { if (atlas == null) return false; if (atlas == this as INGUIAtlas) return true; var rep = replacement; return (rep != null) ? rep.References(atlas) : false; } /// /// Mark all widgets associated with this atlas as having changed. /// public void MarkAsChanged () { #if UNITY_EDITOR NGUITools.SetDirty(this); #endif var rep = replacement; if (rep != null) rep.MarkAsChanged(); var list = NGUITools.FindActive(); for (int i = 0, imax = list.Length; i < imax; ++i) { var sp = list[i]; if (NGUITools.CheckIfRelated(this, sp.atlas)) { var atl = sp.atlas; sp.atlas = null; sp.atlas = atl; #if UNITY_EDITOR NGUITools.SetDirty(sp); #endif } } var f0 = Resources.FindObjectsOfTypeAll(); for (int i = 0, imax = f0.Length; i < imax; ++i) { var font = f0[i]; if (font.atlas == null) continue; if (NGUITools.CheckIfRelated(this, font.atlas)) { var atl = font.atlas; font.atlas = null; font.atlas = atl; #if UNITY_EDITOR NGUITools.SetDirty(font); #endif } } var f1 = Resources.FindObjectsOfTypeAll(); for (int i = 0, imax = f1.Length; i < imax; ++i) { var font = f1[i]; if (NGUITools.CheckIfRelated(this, font.atlas)) { var atl = font.atlas; font.atlas = null; font.atlas = atl; #if UNITY_EDITOR NGUITools.SetDirty(font); #endif } } var labels = NGUITools.FindActive(); for (int i = 0, imax = labels.Length; i < imax; ++i) { var lbl = labels[i]; if (lbl.atlas == null) continue; if (NGUITools.CheckIfRelated(this, lbl.atlas)) { var atl = lbl.atlas; var font = lbl.bitmapFont; lbl.bitmapFont = null; lbl.bitmapFont = font; #if UNITY_EDITOR NGUITools.SetDirty(lbl); #endif } } } }