// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik // using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace Animancer { /// Attribute for static methods which try to create a transition from an object. /// /// The method signature must be: /// static ITransition TryCreateTransition(Object target) /// /// https://kybernetik.com.au/animancer/api/Animancer/TryCreateTransitionAttribute [AttributeUsage(AttributeTargets.Method)] public sealed class TryCreateTransitionAttribute : Attribute { /************************************************************************************************************************/ /// The base type of object which the attributed method can handle. public Type ObjectType { get; private set; } /************************************************************************************************************************/ /// Creates a new . public TryCreateTransitionAttribute(Type objectType) { ObjectType = objectType; } /************************************************************************************************************************/ #if UNITY_EDITOR /************************************************************************************************************************/ private static List> _Methods; private static List _TargetTypes; /// [Editor-Only] Ensures that all methods with this attribute have been gathered. private static void InitializeMethods() { if (_Methods != null) return; _Methods = new(); _TargetTypes = new(); foreach (var method in TypeCache.GetMethodsWithAttribute()) { try { var attributes = method.GetCustomAttributes(typeof(TryCreateTransitionAttribute), true); if (!attributes.IsNullOrEmpty()) { var attribute = attributes[0] as TryCreateTransitionAttribute; if (attribute?.ObjectType != null) _TargetTypes.Add(attribute.ObjectType); } var func = Delegate.CreateDelegate(typeof(Func), method); _Methods.Add((Func)func); } catch (Exception exception) { Debug.LogError( $"Failed to create delegate for" + $" {method.DeclaringType.GetNameCS()}.{method.Name}," + $" it must take one {typeof(Object).FullName} parameter" + $" and return {typeof(ITransition).FullName}" + $": {exception}"); } } } /************************************************************************************************************************/ /// [Editor-Only] Validates that the `mainAsset` is actually an asset. public static bool CanCreateAndSave(Object mainAsset) { if (TransitionAssetBase.CreateInstance == null) return false; if (!CanCreate(mainAsset.GetType())) return false; var path = AssetDatabase.GetAssetPath(mainAsset); return !string.IsNullOrEmpty(path); } /// [Editor-Only] Validates that the `mainAsset` is actually an asset. public static bool CanCreate(Type targetType) { InitializeMethods(); for (int i = 0; i < _TargetTypes.Count; i++) if (_TargetTypes[i].IsAssignableFrom(targetType)) return true; return false; } /************************************************************************************************************************/ /// [Editor-Only] Tries to create an asset containing an appropriate transition for the `target`. public static TransitionAssetBase TryCreateTransitionAsset(Object target, bool saveNextToTarget = false) { if (target is TransitionAssetBase asset) return asset; var assetType = TransitionAssetBase.CreateInstance; if (assetType == null) return null; InitializeMethods(); for (int i = 0; i < _Methods.Count; i++) { var transition = _Methods[i](target); if (transition is not null) { var created = TransitionAssetBase.CreateInstance(transition); created.name = target.name; if (saveNextToTarget) { var path = AssetDatabase.GetAssetPath(target); if (string.IsNullOrEmpty(path)) { Debug.LogError( $"Can't create TransitionAsset for '{target}' because it isn't an asset.", target); return created; } path = System.IO.Path.GetDirectoryName(path); path = System.IO.Path.Combine(path, $"{target.name}.asset"); path = AssetDatabase.GenerateUniqueAssetPath(path); AssetDatabase.CreateAsset(created, path); Selection.activeObject = created; } return created; } } return null; } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ } }