using System; using UnityEngine.Animations; using UnityEngine.Scripting.APIUpdating; using UnityEngine.U2D.Common; namespace UnityEngine.U2D.Animation { /// /// Updates a SpriteRenderer's Sprite reference on the Category and Label value it is set. /// /// /// By setting the SpriteResolver's Category and Label value, it will request for a Sprite from /// a SpriteLibrary Component the Sprite that is registered for the Category and Label. /// If a SpriteRenderer is present in the same GameObject, the SpriteResolver will update the /// SpriteRenderer's Sprite reference to the corresponding Sprite. /// [ExecuteInEditMode] [DisallowMultipleComponent] [AddComponentMenu("2D Animation/Sprite Resolver")] [IconAttribute(IconUtility.IconPath + "Animation.SpriteResolver.png")] [DefaultExecutionOrder(-2)] [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.animation@9.0/manual/SL-Resolver.html")] [MovedFrom("UnityEngine.Experimental.U2D.Animation")] public class SpriteResolver : MonoBehaviour, ISerializationCallbackReceiver, IPreviewable { // SpriteHash is the new animation key. // We are keeping the old ones so that the animation clip doesn't break // These are for animation [SerializeField] float m_CategoryHash = 0; [SerializeField] float m_labelHash = 0; [SerializeField] float m_SpriteKey = 0; [SerializeField, DiscreteEvaluation] int m_SpriteHash = 0; // For comparing hash values int m_CategoryHashInt; int m_LabelHashInt; // For OnUpdate during animation playback int m_PreviousCategoryHash; int m_PreviousLabelHash; int m_PreviousSpriteKeyInt; int m_PreviousSpriteHash; #if UNITY_EDITOR bool m_SpriteLibChanged; /// /// Raised when object is deserialized in the Editor. /// public event Action onDeserializedCallback = () => { }; #endif void Reset() { // If the Sprite referred to by the SpriteRenderer exist in the library, // we select the Sprite if(spriteRenderer) SetSprite(spriteRenderer.sprite); } void SetSprite(Sprite sprite) { var sl = spriteLibrary; if (sl != null && sprite != null) { foreach (var cat in sl.categoryNames) { var entries = sl.GetEntryNames(cat); foreach (var ent in entries) { if (sl.GetSprite(cat, ent) == sprite) { m_SpriteHash = SpriteLibrary.GetHashForCategoryAndEntry(cat, ent); return; } } } } } void OnEnable() { InitializeSerializedData(); ResolveSpriteToSpriteRenderer(); } void InitializeSerializedData() { m_CategoryHashInt = InternalEngineBridge.ConvertFloatToInt(m_CategoryHash); m_LabelHashInt = InternalEngineBridge.ConvertFloatToInt(m_labelHash); m_PreviousSpriteKeyInt = SpriteLibraryUtility.Convert32BitTo30BitHash(InternalEngineBridge.ConvertFloatToInt(m_SpriteKey)); m_SpriteKey = InternalEngineBridge.ConvertIntToFloat(m_PreviousSpriteKeyInt); if (m_SpriteHash == 0) { if (m_SpriteKey != 0f) m_SpriteHash = InternalEngineBridge.ConvertFloatToInt(m_SpriteKey); else m_SpriteHash = ConvertCategoryLabelHashToSpriteKey(spriteLibrary, SpriteLibraryUtility.Convert32BitTo30BitHash(m_CategoryHashInt), SpriteLibraryUtility.Convert32BitTo30BitHash(m_LabelHashInt)); } m_PreviousSpriteHash = m_SpriteHash; string newCat, newLab; if (spriteLibrary != null && spriteLibrary.GetCategoryAndEntryNameFromHash(m_SpriteHash, out newCat, out newLab)) { // Populate back in case user is using animating with old animation clip m_CategoryHashInt = SpriteLibraryUtility.GetStringHash(newCat); m_LabelHashInt = SpriteLibraryUtility.GetStringHash(newLab); m_CategoryHash = InternalEngineBridge.ConvertIntToFloat(m_CategoryHashInt); m_labelHash = InternalEngineBridge.ConvertIntToFloat(m_LabelHashInt); } m_PreviousLabelHash = m_LabelHashInt; m_PreviousCategoryHash = m_CategoryHashInt; } SpriteRenderer spriteRenderer => GetComponent(); /// /// Set the Category and label to use. /// /// Category to use. /// Label to use. /// True if the Category and Label were successfully set. public bool SetCategoryAndLabel(string category, string label) { m_SpriteHash = SpriteLibrary.GetHashForCategoryAndEntry(category, label); m_PreviousSpriteHash = m_SpriteHash; return ResolveSpriteToSpriteRenderer(); } /// /// Get the Category set for the SpriteResolver. /// /// The Category's name. public string GetCategory() { var returnString = ""; var sl = spriteLibrary; if (sl) { sl.GetCategoryAndEntryNameFromHash(m_SpriteHash, out returnString, out _); } return returnString; } /// /// Get the Label set for the SpriteResolver. /// /// The Label's name. public string GetLabel() { var returnString = ""; var sl = spriteLibrary; if (sl) sl.GetCategoryAndEntryNameFromHash(m_SpriteHash, out _, out returnString); return returnString; } /// /// Property to get the SpriteLibrary the SpriteResolver is resolving from. /// public SpriteLibrary spriteLibrary => gameObject.GetComponentInParent(true); /// /// Empty method. Implemented for the IPreviewable interface. /// public void OnPreviewUpdate() { } #if UNITY_EDITOR void OnDidApplyAnimationProperties() { if(IsInGUIUpdateLoop()) ResolveUpdatedValue(); } #endif static bool IsInGUIUpdateLoop() => Event.current != null; void LateUpdate() { ResolveUpdatedValue(); } void ResolveUpdatedValue() { if (m_SpriteHash != m_PreviousSpriteHash) { m_PreviousSpriteHash = m_SpriteHash; ResolveSpriteToSpriteRenderer(); } else { var spriteKeyInt = InternalEngineBridge.ConvertFloatToInt(m_SpriteKey); if (spriteKeyInt != m_PreviousSpriteKeyInt) { m_SpriteHash = SpriteLibraryUtility.Convert32BitTo30BitHash(spriteKeyInt); m_PreviousSpriteKeyInt = spriteKeyInt; ResolveSpriteToSpriteRenderer(); } else { m_CategoryHashInt = InternalEngineBridge.ConvertFloatToInt(m_CategoryHash); m_LabelHashInt = InternalEngineBridge.ConvertFloatToInt(m_labelHash); if (m_LabelHashInt != m_PreviousLabelHash || m_CategoryHashInt != m_PreviousCategoryHash) { if (spriteLibrary != null) { m_PreviousCategoryHash = m_CategoryHashInt; m_PreviousLabelHash = m_LabelHashInt; m_SpriteHash = ConvertCategoryLabelHashToSpriteKey(spriteLibrary, SpriteLibraryUtility.Convert32BitTo30BitHash(m_CategoryHashInt), SpriteLibraryUtility.Convert32BitTo30BitHash(m_LabelHashInt)); m_PreviousSpriteHash = m_SpriteHash; ResolveSpriteToSpriteRenderer(); } } } } } internal static int ConvertCategoryLabelHashToSpriteKey(SpriteLibrary library, int categoryHash, int labelHash) { if (library != null) { foreach(var category in library.categoryNames) { if (categoryHash == SpriteLibraryUtility.GetStringHash(category)) { var entries = library.GetEntryNames(category); if (entries != null) { foreach (var entry in entries) { if (labelHash == SpriteLibraryUtility.GetStringHash(entry)) { return SpriteLibrary.GetHashForCategoryAndEntry(category, entry); } } } } } } return 0; } internal Sprite GetSprite(out bool validEntry) { var lib = spriteLibrary; if (lib != null) { return lib.GetSpriteFromCategoryAndEntryHash(m_SpriteHash, out validEntry); } validEntry = false; return null; } /// /// Set the Sprite in SpriteResolver to the SpriteRenderer component that is in the same GameObject. /// /// True if it successfully resolved the Sprite. public bool ResolveSpriteToSpriteRenderer() { m_PreviousSpriteHash = m_SpriteHash; var sprite = GetSprite(out var validEntry); var sr = spriteRenderer; if (sr != null && (sprite != null || validEntry)) sr.sprite = sprite; return validEntry; } void OnTransformParentChanged() { ResolveSpriteToSpriteRenderer(); #if UNITY_EDITOR spriteLibChanged = true; #endif } #if UNITY_EDITOR internal bool spriteLibChanged { get => m_SpriteLibChanged; set => m_SpriteLibChanged = value; } #endif /// /// Called before object is serialized. /// void ISerializationCallbackReceiver.OnBeforeSerialize() { } /// /// Called after object is deserialized. /// void ISerializationCallbackReceiver.OnAfterDeserialize() { #if UNITY_EDITOR onDeserializedCallback(); #endif } } }