using System.Collections.Generic; using System.Linq; using UnityEngine.Scripting.APIUpdating; namespace UnityEngine.U2D.Animation { /// /// Component that holds a Sprite Library Asset. The component is used by SpriteResolver Component to query for Sprite based on Category and Index /// [DisallowMultipleComponent] [AddComponentMenu("2D Animation/Sprite Library")] [MovedFrom("UnityEngine.Experimental.U2D.Animation")] [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.animation@latest/index.html?subfolder=/manual/SLAsset.html%23sprite-library-component")] public class SpriteLibrary : MonoBehaviour { struct CategoryEntrySprite { public string category; public string entry; public Sprite sprite; } [SerializeField] List m_Library = new List(); [SerializeField] SpriteLibraryAsset m_SpriteLibraryAsset; // Cache for combining data in sprite library asset and main library Dictionary m_CategoryEntryHashCache = null; Dictionary> m_CategoryEntryCache = null; int m_PreviousSpriteLibraryAsset; long m_PreviousModificationHash; /// Get or Set the current SpriteLibraryAsset to use public SpriteLibraryAsset spriteLibraryAsset { set { if (m_SpriteLibraryAsset != value) { m_SpriteLibraryAsset = value; CacheOverrides(); RefreshSpriteResolvers(); } } get { return m_SpriteLibraryAsset; } } void OnEnable() { CacheOverrides(); } /// /// Return the Sprite that is registered for the given Category and Label for the SpriteLibrary /// /// Category name /// Label name /// Sprite associated to the name and index public Sprite GetSprite(string category, string label) { return GetSprite(GetHashForCategoryAndEntry(category, label)); } Sprite GetSprite(int hash) { if (m_CategoryEntryHashCache.ContainsKey(hash)) return m_CategoryEntryHashCache[hash].sprite; return null; } void UpdateCacheOverridesIfNeeded() { if(m_CategoryEntryCache == null || m_PreviousSpriteLibraryAsset != m_SpriteLibraryAsset?.GetInstanceID() || m_PreviousModificationHash != m_SpriteLibraryAsset?.modificationHash) CacheOverrides(); } internal bool GetCategoryAndEntryNameFromHash(int hash, out string category, out string entry) { UpdateCacheOverridesIfNeeded(); if (m_CategoryEntryHashCache.ContainsKey(hash)) { category = m_CategoryEntryHashCache[hash].category; entry = m_CategoryEntryHashCache[hash].entry; return true; } category = null; entry = null; return false; } internal static int GetHashForCategoryAndEntry(string category, string entry) { return SpriteLibraryUtility.GetStringHash($"{category}_{entry}"); } internal Sprite GetSpriteFromCategoryAndEntryHash(int hash, out bool validEntry) { UpdateCacheOverridesIfNeeded(); if (m_CategoryEntryHashCache.ContainsKey(hash)) { validEntry = true; return m_CategoryEntryHashCache[hash].sprite; } validEntry = false; return null; } List GetEntries(string category, bool addIfNotExist) { var index = m_Library.FindIndex(x => x.name == category); if (index < 0) { if (!addIfNotExist) return null; m_Library.Add(new SpriteLibCategory() { name = category, categoryList = new List() }); index = m_Library.Count - 1; } return m_Library[index].categoryList; } SpriteCategoryEntry GetEntry(List entries, string entry, bool addIfNotExist) { var index = entries.FindIndex(x => x.name == entry); if (index < 0) { if (!addIfNotExist) return null; entries.Add(new SpriteCategoryEntry() { name = entry, }); index = entries.Count - 1; } return entries[index]; } /// /// Add or replace an override when querying for the given Category and Label from a SpriteLibraryAsset /// /// Sprite Library Asset to query /// Category name from the Sprite Library Asset to add override /// Label name to add override public void AddOverride(SpriteLibraryAsset spriteLib, string category, string label) { var sprite = spriteLib.GetSprite(category, label); var entries = GetEntries(category, true); var entry = GetEntry(entries, label, true); entry.sprite = sprite; CacheOverrides(); } /// /// Add or replace an override when querying for the given Category. All the categories in the Category will be added. /// /// Sprite Library Asset to query /// Category name from the Sprite Library Asset to add override public void AddOverride(SpriteLibraryAsset spriteLib, string category) { var categoryHash = SpriteLibraryUtility.GetStringHash(category); var cat = spriteLib.categories.FirstOrDefault(x => x.hash == categoryHash); if (cat != null) { var entries = GetEntries(category, true); for (var i = 0; i < cat.categoryList.Count; ++i) { var ent = cat.categoryList[i]; GetEntry(entries, ent.name, true).sprite = ent.sprite; } CacheOverrides(); } } /// /// Add or replace an override when querying for the given Category and Label. /// /// Sprite to override to /// Category name to override /// Label name to override public void AddOverride(Sprite sprite, string category, string label) { GetEntry(GetEntries(category, true), label, true).sprite = sprite; CacheOverrides(); RefreshSpriteResolvers(); } /// /// Remove all Sprite Library override for a given category /// /// Category overrides to remove public void RemoveOverride(string category) { var index = m_Library.FindIndex(x => x.name == category); if (index >= 0) { m_Library.RemoveAt(index); CacheOverrides(); RefreshSpriteResolvers(); } } /// /// Remove Sprite Library override for a given category and label /// /// Category to remove /// Label to remove public void RemoveOverride(string category, string label) { var entries = GetEntries(category, false); if (entries != null) { var index = entries.FindIndex(x => x.name == label); if (index >= 0) { entries.RemoveAt(index); CacheOverrides(); RefreshSpriteResolvers(); } } } /// /// Method to check if a Category and Label pair has an override /// /// Category name /// Label name /// True if override exist, false otherwise public bool HasOverride(string category, string label) { var catOverride = GetEntries(category, false); if (catOverride != null) return GetEntry(catOverride, label, false) != null; return false; } /// /// Request SpriteResolver components that are in the same hierarchy to refresh /// public void RefreshSpriteResolvers() { var spriteResolvers = GetComponentsInChildren(); foreach (var sr in spriteResolvers) { sr.ResolveSpriteToSpriteRenderer(); #if UNITY_EDITOR sr.spriteLibChanged = true; #endif } } internal IEnumerable categoryNames { get { UpdateCacheOverridesIfNeeded(); return m_CategoryEntryCache.Keys; } } internal IEnumerable GetEntryNames(string category) { UpdateCacheOverridesIfNeeded(); if (m_CategoryEntryCache.ContainsKey(category)) return m_CategoryEntryCache[category]; return null; } internal void CacheOverrides() { m_PreviousSpriteLibraryAsset = 0; m_PreviousModificationHash = 0; m_CategoryEntryHashCache = new Dictionary(); m_CategoryEntryCache = new Dictionary>(); if (m_SpriteLibraryAsset) { m_PreviousSpriteLibraryAsset = m_SpriteLibraryAsset.GetInstanceID(); m_PreviousModificationHash = m_SpriteLibraryAsset.modificationHash; foreach (var category in m_SpriteLibraryAsset.categories) { var catName = category.name; m_CategoryEntryCache.Add(catName, new HashSet()); var cacheEntryName = m_CategoryEntryCache[catName]; foreach (var entry in category.categoryList) { m_CategoryEntryHashCache.Add(GetHashForCategoryAndEntry(catName, entry.name), new CategoryEntrySprite() { category = catName, entry = entry.name, sprite = entry.sprite }); cacheEntryName.Add(entry.name); } } } foreach (var category in m_Library) { var catName = category.name; if(!m_CategoryEntryCache.ContainsKey(catName)) m_CategoryEntryCache.Add(catName, new HashSet()); var cacheEntryName = m_CategoryEntryCache[catName]; foreach (var ent in category.categoryList) { if (!cacheEntryName.Contains(ent.name)) cacheEntryName.Add(ent.name); var hash = GetHashForCategoryAndEntry(catName, ent.name); if (!m_CategoryEntryHashCache.ContainsKey(hash)) { m_CategoryEntryHashCache.Add(hash, new CategoryEntrySprite() { category = catName, entry = ent.name, sprite = ent.sprite }); } else { var e = m_CategoryEntryHashCache[hash]; e.sprite = ent.sprite; m_CategoryEntryHashCache[hash] = e; } } } } } }