// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using System.Collections.Generic; using UnityEngine; namespace Animancer { /// /// Replaces the with a copy of it that uses a different /// during every . /// /// /// /// This script is not specific to Animancer and will work with any animation system if you remove the /// [] and [] attributes. /// /// https://kybernetik.com.au/animancer/api/Animancer/SpriteRendererTextureSwap /// [AddComponentMenu(Strings.MenuPrefix + "Sprite Renderer Texture Swap")] [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(SpriteRendererTextureSwap))] [DefaultExecutionOrder(DefaultExecutionOrder)] public class SpriteRendererTextureSwap : MonoBehaviour { /************************************************************************************************************************/ /// Execute very late (32000 is last). public const int DefaultExecutionOrder = 30000; /************************************************************************************************************************/ [SerializeField] [Tooltip("The SpriteRenderer that will have its Sprite modified")] private SpriteRenderer _Renderer; /// The that will have its modified. public ref SpriteRenderer Renderer => ref _Renderer; /************************************************************************************************************************/ [SerializeField] [Tooltip("The replacement for the original Sprite texture")] private Texture2D _Texture; /// The replacement for the original . /// /// If this texture has any s set up in its import settings, they will be completely /// ignored because this system creates new s at runtime. The texture doesn't even need to /// be set to mode. /// /// Call before setting this if you want to destroy any sprites created for the /// previous texture. /// public Texture2D Texture { get => _Texture; set { _Texture = value; RefreshSpriteMap(); } } /************************************************************************************************************************/ private Dictionary _SpriteMap; private void RefreshSpriteMap() => _SpriteMap = GetSpriteMap(_Texture); /************************************************************************************************************************/ protected virtual void Awake() => RefreshSpriteMap(); protected virtual void OnValidate() => RefreshSpriteMap(); /************************************************************************************************************************/ protected virtual void LateUpdate() { if (_Renderer == null) return; var sprite = _Renderer.sprite; if (TrySwapTexture(_SpriteMap, _Texture, ref sprite)) _Renderer.sprite = sprite; } /************************************************************************************************************************/ /// Destroys all sprites created for the current . public void ClearCache() { DestroySprites(_SpriteMap); } /************************************************************************************************************************/ private static readonly Dictionary> TextureToSpriteMap = new Dictionary>(); /************************************************************************************************************************/ /// Returns a cached dictionary mapping original sprites to duplicates using the specified `texture`. public static Dictionary GetSpriteMap(Texture2D texture) { if (texture == null) return null; if (!TextureToSpriteMap.TryGetValue(texture, out var map)) TextureToSpriteMap.Add(texture, map = new Dictionary()); return map; } /************************************************************************************************************************/ /// /// If the is not already using the specified `texture`, this method replaces the /// `sprite` with a cached duplicate which uses that `texture` instead. /// public static bool TrySwapTexture(Dictionary spriteMap, Texture2D texture, ref Sprite sprite) { if (spriteMap == null || sprite == null || texture == null || sprite.texture == texture) return false; if (!spriteMap.TryGetValue(sprite, out var otherSprite)) { var pivot = sprite.pivot; pivot.x /= sprite.rect.width; pivot.y /= sprite.rect.height; otherSprite = Sprite.Create(texture, sprite.rect, pivot, sprite.pixelsPerUnit, 0, SpriteMeshType.FullRect, sprite.border, false); #if UNITY_ASSERTIONS var name = sprite.name; var originalTextureName = sprite.texture.name; var index = name.IndexOf(originalTextureName); if (index >= 0) { var newName = texture.name + name.Substring(index + originalTextureName.Length, name.Length - (index + originalTextureName.Length)); if (index > 0) newName = name.Substring(0, index) + newName; name = newName; } otherSprite.name = name; #endif spriteMap.Add(sprite, otherSprite); } sprite = otherSprite; return true; } /************************************************************************************************************************/ /// Destroys all the . public static void DestroySprites(Dictionary spriteMap) { if (spriteMap == null) return; foreach (var sprite in spriteMap.Values) Destroy(sprite); spriteMap.Clear(); } /************************************************************************************************************************/ /// Destroys all sprites created for the `texture`. public static void DestroySprites(Texture2D texture) { if (TextureToSpriteMap.TryGetValue(texture, out var spriteMap)) { TextureToSpriteMap.Remove(texture); DestroySprites(spriteMap); } } /************************************************************************************************************************/ } }