123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR
- using System;
- using System.Collections.Generic;
- using UnityEditor;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace Animancer.Editor.Tools
- {
- /// <summary>[Editor-Only] [Pro-Only]
- /// A base <see cref="AnimancerToolsWindow.Tool"/> for modifying <see cref="Sprite"/>s.
- /// </summary>
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
- /// Animancer Tools</see>
- /// </remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/SpriteModifierTool
- ///
- [Serializable]
- public abstract class SpriteModifierTool : AnimancerToolsWindow.Tool
- {
- /************************************************************************************************************************/
- private static readonly List<Sprite> SelectedSprites = new();
- private static bool _HasGatheredSprites;
- /// <summary>The currently selected <see cref="Sprite"/>s.</summary>
- public static List<Sprite> Sprites
- {
- get
- {
- if (!_HasGatheredSprites)
- {
- _HasGatheredSprites = true;
- GatherSelectedSprites(SelectedSprites);
- }
- return SelectedSprites;
- }
- }
- /// <inheritdoc/>
- public override void OnSelectionChanged()
- {
- _HasGatheredSprites = false;
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override void DoBodyGUI()
- {
- #if !UNITY_2D_SPRITE
- EditorGUILayout.HelpBox(
- "This tool works best with Unity's '2D Sprite' package." +
- " You should import it via the Package Manager before using this tool.",
- MessageType.Warning);
- if (AnimancerGUI.TryUseClickEventInLastRect())
- EditorApplication.ExecuteMenuItem("Window/Package Manager");
- #endif
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Adds all <see cref="Sprite"/>s in the <see cref="Selection.objects"/> or their sub-assets to the
- /// list of `sprites`.
- /// </summary>
- public static void GatherSelectedSprites(List<Sprite> sprites)
- {
- sprites.Clear();
- var selection = Selection.objects;
- for (int i = 0; i < selection.Length; i++)
- {
- var selected = selection[i];
- if (selected is Sprite sprite)
- {
- sprites.Add(sprite);
- }
- else if (selected is Texture2D texture)
- {
- sprites.AddRange(LoadAllSpritesInTexture(texture));
- }
- }
- sprites.Sort(NaturalCompare);
- }
- /************************************************************************************************************************/
- /// <summary>Returns all the <see cref="Sprite"/> sub-assets of the `texture`.</summary>
- public static Sprite[] LoadAllSpritesInTexture(Texture2D texture)
- => LoadAllSpritesAtPath(AssetDatabase.GetAssetPath(texture));
- /// <summary>Returns all the <see cref="Sprite"/> assets at the `path`.</summary>
- public static Sprite[] LoadAllSpritesAtPath(string path)
- {
- var assets = AssetDatabase.LoadAllAssetsAtPath(path);
- var sprites = new List<Sprite>();
- for (int j = 0; j < assets.Length; j++)
- {
- if (assets[j] is Sprite sprite)
- sprites.Add(sprite);
- }
- return sprites.ToArray();
- }
- /************************************************************************************************************************/
- /// <summary>Calls <see cref="EditorUtility.NaturalCompare"/> on the <see cref="Object.name"/>s.</summary>
- public static int NaturalCompare(Object a, Object b)
- => EditorUtility.NaturalCompare(a.name, b.name);
- /************************************************************************************************************************/
- /// <summary>The message to confirm that the user is certain they want to apply the changes.</summary>
- protected virtual string AreYouSure
- => "Are you sure you want to modify these Sprites?";
- /// <summary>Called immediately after the user confirms they want to apply changes.</summary>
- protected virtual void BeforeApply() { }
- /// <summary>Called after all changes are applied.</summary>
- protected virtual void AfterApply() { }
- /// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
- protected virtual void Modify(SpriteDataEditor data, int index, Sprite sprite) { }
- /// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
- protected virtual void Modify(TextureImporter importer, List<Sprite> sprites)
- {
- var dataEditor = new SpriteDataEditor(importer);
- var hasError = false;
- for (int i = 0; i < sprites.Count; i++)
- {
- var sprite = sprites[i];
- var index = dataEditor.IndexOf(sprite);
- if (index < 0)
- continue;
- Modify(dataEditor, index, sprite);
- sprites.RemoveAt(i--);
- if (!dataEditor.ValidateBounds(index, sprite))
- hasError = true;
- }
- if (!hasError)
- dataEditor.Apply();
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Asks the user if they want to modify the target <see cref="Sprite"/>s and calls <see cref="Modify"/>
- /// on each of them before saving any changes.
- /// </summary>
- protected void AskAndApply()
- {
- if (!EditorUtility.DisplayDialog("Are You Sure?",
- AreYouSure + "\n\nThis operation cannot be undone.",
- "Modify", "Cancel"))
- return;
- BeforeApply();
- var pathToSprites = new Dictionary<string, List<Sprite>>();
- var sprites = Sprites;
- for (int i = 0; i < sprites.Count; i++)
- {
- var sprite = sprites[i];
- var path = AssetDatabase.GetAssetPath(sprite);
- if (!pathToSprites.TryGetValue(path, out var spritesAtPath))
- pathToSprites.Add(path, spritesAtPath = new());
- spritesAtPath.Add(sprite);
- }
- foreach (var asset in pathToSprites)
- {
- var importer = (TextureImporter)AssetImporter.GetAtPath(asset.Key);
- Modify(importer, asset.Value);
- if (asset.Value.Count > 0)
- {
- var message = StringBuilderPool.Instance.Acquire()
- .Append("Modification failed: unable to find data in '")
- .Append(asset.Key)
- .Append("' for ")
- .Append(asset.Value.Count)
- .Append(" Sprites:");
- for (int i = 0; i < sprites.Count; i++)
- {
- message.AppendLine()
- .Append(" - ")
- .Append(sprites[i].name);
- }
- Debug.LogError(message.ReleaseToString(), AssetDatabase.LoadAssetAtPath<Object>(asset.Key));
- }
- }
- AfterApply();
- }
- /************************************************************************************************************************/
- }
- }
- #endif
|