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;
}
}
}
}
}
}