// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// [Editor-Only] Various utilities used throughout Animancer.
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEditorUtilities
///
public static partial class AnimancerEditorUtilities
{
/************************************************************************************************************************/
#region Misc
/************************************************************************************************************************/
/// Commonly used combinations.
public const BindingFlags
AnyAccessBindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
InstanceBindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
StaticBindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
/************************************************************************************************************************/
/// [Animancer Extension] [Editor-Only]
/// Returns the first attribute on the `member` or null if there is none.
///
public static TAttribute GetAttribute(this ICustomAttributeProvider member, bool inherit = false)
where TAttribute : class
{
var type = typeof(TAttribute);
if (member.IsDefined(type, inherit))
return (TAttribute)member.GetCustomAttributes(type, inherit)[0];
else
return null;
}
/************************************************************************************************************************/
/// [Animancer Extension] [Editor-Only] Is the or NaN?
public static bool IsNaN(this Vector2 vector) => float.IsNaN(vector.x) || float.IsNaN(vector.y);
/// [Animancer Extension] [Editor-Only] Is the , , or NaN?
public static bool IsNaN(this Vector3 vector) => float.IsNaN(vector.x) || float.IsNaN(vector.y) || float.IsNaN(vector.z);
/************************************************************************************************************************/
/// Finds an asset of the specified type anywhere in the project.
public static T FindAssetOfType() where T : Object
{
var filter = typeof(Component).IsAssignableFrom(typeof(T))
? $"t:{nameof(GameObject)}"
: $"t:{typeof(T).Name}";
var guids = AssetDatabase.FindAssets(filter);
if (guids.Length == 0)
return null;
for (int i = 0; i < guids.Length; i++)
{
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
var asset = AssetDatabase.LoadAssetAtPath(path);
if (asset != null)
return asset;
}
return null;
}
/************************************************************************************************************************/
// The "g" format gives a lower case 'e' for exponentials instead of upper case 'E'.
private static readonly ConversionCache
FloatToString = new ConversionCache((value) => $"{value:g}");
/// [Animancer Extension]
/// Calls using "g" as the format and caches the result.
///
public static string ToStringCached(this float value) => FloatToString.Convert(value);
/************************************************************************************************************************/
/// The most recent .
public static PlayModeStateChange PlayModeState { get; private set; }
/// Is the Unity Editor is currently changing between Play Mode and Edit Mode?
public static bool IsChangingPlayMode =>
PlayModeState == PlayModeStateChange.ExitingEditMode ||
PlayModeState == PlayModeStateChange.ExitingPlayMode;
[InitializeOnLoadMethod]
private static void WatchForPlayModeChanges()
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
PlayModeState = EditorApplication.isPlaying ?
PlayModeStateChange.EnteredPlayMode :
PlayModeStateChange.ExitingEditMode;
EditorApplication.playModeStateChanged += (change) => PlayModeState = change;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Collections
/************************************************************************************************************************/
/// Adds default items or removes items to make the equal to the `count`.
public static void SetCount(List list, int count)
{
if (list.Count < count)
{
while (list.Count < count)
list.Add(default);
}
else
{
list.RemoveRange(count, list.Count - count);
}
}
/************************************************************************************************************************/
///
/// Removes any items from the `list` that are null and items that appear multiple times.
/// Returns true if the `list` was modified.
///
public static bool RemoveMissingAndDuplicates(ref List list)
{
if (list == null)
{
list = new List();
return false;
}
var modified = false;
using (ObjectPool.Disposable.AcquireSet