com.wallstop-studios.unity-helpers 2.0.0-rc27 → 2.0.0-rc31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +63 -63
- package/.github/workflows/npm-publish.yml +60 -0
- package/.github/workflows/unity-package.yml +101 -0
- package/.github/workflows/unity-tests.yml +38 -0
- package/Editor/AnimationCopier.cs +158 -158
- package/Editor/AnimationCopier.cs.meta +2 -2
- package/Editor/AnimationCreator.cs +262 -262
- package/Editor/AnimationCreator.cs.meta +11 -11
- package/Editor/AnimationEventEditor.cs +887 -887
- package/Editor/AnimatorControllerCopier.cs +162 -162
- package/Editor/AnimatorControllerCopier.cs.meta +2 -2
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs +34 -34
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs.meta +2 -2
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/EnsureTextureSizeWizard.cs +110 -110
- package/Editor/EnsureTextureSizeWizard.cs.meta +2 -2
- package/Editor/PrefabCheckWizard.cs +165 -165
- package/Editor/PrefabCheckWizard.cs.meta +11 -11
- package/Editor/SpriteSettingsApplier.cs +168 -168
- package/Editor/SpriteSettingsApplier.cs.meta +2 -2
- package/Editor/StringInListeDrawer.cs +56 -56
- package/Editor/TextureResizerWizard.cs +181 -181
- package/Editor/TextureResizerWizard.cs.meta +2 -2
- package/Editor/TextureSettingsApplier.cs +171 -171
- package/Editor/TextureSettingsApplier.cs.meta +2 -2
- package/Editor/Utils/EditorUtilities.cs +22 -22
- package/Editor/Utils/EditorUtilities.cs.meta +11 -11
- package/Editor/Utils/ReadOnlyPropertyDrawer.cs +26 -26
- package/Editor/Utils/ReadOnlyPropertyDrawer.cs.meta +11 -11
- package/Editor/Utils.meta +8 -8
- package/Editor/WallstopStudios.UnityHelpers.Editor.asmdef +17 -17
- package/Editor/WallstopStudios.UnityHelpers.Editor.asmdef.meta +7 -7
- package/LICENSE +21 -21
- package/LICENSE.md +6 -6
- package/LICENSE.meta +7 -7
- package/README.md +117 -117
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.dll.meta +33 -33
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.xml +223 -223
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.xml.meta +7 -7
- package/Runtime/Binaries/System.Text.Encodings.Web.dll.meta +33 -33
- package/Runtime/Binaries/System.Text.Encodings.Web.xml +935 -935
- package/Runtime/Binaries/System.Text.Encodings.Web.xml.meta +7 -7
- package/Runtime/Binaries/System.Text.Json.dll.meta +33 -33
- package/Runtime/Binaries/System.Text.Json.xml +4829 -4829
- package/Runtime/Binaries/System.Text.Json.xml.meta +7 -7
- package/Runtime/Binaries.meta +8 -8
- package/Runtime/Core/Attributes/AnimationEventAttribute.cs +131 -131
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +189 -189
- package/Runtime/Core/Attributes/KSerializableAttribute.cs +19 -19
- package/Runtime/Core/Attributes/NotNullAttribute.cs +32 -32
- package/Runtime/Core/Attributes/ParentComponent.cs +184 -184
- package/Runtime/Core/Attributes/ReadOnlyAttribute.cs +6 -6
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +14 -14
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +119 -119
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs.meta +11 -11
- package/Runtime/Core/Attributes/ValidateAssignmentAttribute.cs +101 -101
- package/Runtime/Core/Attributes/ValidateAssignmentAttribute.cs.meta +11 -11
- package/Runtime/Core/Attributes.meta +8 -8
- package/Runtime/Core/DataStructure/Adapters/FastVector2Int.cs +92 -92
- package/Runtime/Core/DataStructure/Adapters/FastVector3Int.cs +185 -185
- package/Runtime/Core/DataStructure/Adapters/KGuid.cs +305 -305
- package/Runtime/Core/DataStructure/Adapters/KVector2.cs +80 -80
- package/Runtime/Core/DataStructure/Circle.cs +50 -50
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +155 -155
- package/Runtime/Core/DataStructure/ISpatialTree.cs +60 -60
- package/Runtime/Core/DataStructure/ISpatialTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/KDTree.cs +290 -290
- package/Runtime/Core/DataStructure/KDTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/QuadTree.cs +279 -279
- package/Runtime/Core/DataStructure/RTree.cs +336 -336
- package/Runtime/Core/DataStructure/RTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/StringWrapper.cs +91 -91
- package/Runtime/Core/DataStructure/TimedCache.cs +51 -51
- package/Runtime/Core/Extension/AnimatorExtensions.cs +25 -25
- package/Runtime/Core/Extension/CircleExtensions.cs +25 -25
- package/Runtime/Core/Extension/ColorExtensions.cs +338 -338
- package/Runtime/Core/Extension/DictionaryExtensions.cs +251 -251
- package/Runtime/Core/Extension/DirectionExtensions.cs +213 -213
- package/Runtime/Core/Extension/HashSetExtensions.cs +12 -12
- package/Runtime/Core/Extension/IEnumerableExtensions.cs +122 -122
- package/Runtime/Core/Extension/IListExtensions.cs +89 -89
- package/Runtime/Core/Extension/LoggingExtensions.cs +258 -258
- package/Runtime/Core/Extension/RandomExtensions.cs +109 -109
- package/Runtime/Core/Extension/StringExtensions.cs +151 -151
- package/Runtime/Core/Extension/UnityExtensions.cs +1607 -1607
- package/Runtime/Core/Helper/ArrayConverter.cs +39 -39
- package/Runtime/Core/Helper/ArrayConverter.cs.meta +2 -2
- package/Runtime/Core/Helper/AssignUtilities.cs +14 -14
- package/Runtime/Core/Helper/AssignUtilities.cs.meta +11 -11
- package/Runtime/Core/Helper/Enumerables.cs +17 -17
- package/Runtime/Core/Helper/Geometry.cs +43 -43
- package/Runtime/Core/Helper/Helpers.cs +722 -722
- package/Runtime/Core/Helper/Helpers.cs.meta +11 -11
- package/Runtime/Core/Helper/IterationHelpers.cs +32 -32
- package/Runtime/Core/Helper/IterationHelpers.cs.meta +11 -11
- package/Runtime/Core/Helper/LifetimeHelpers.cs +13 -13
- package/Runtime/Core/Helper/Objects.cs +767 -767
- package/Runtime/Core/Helper/Partials/LogHelpers.cs +13 -13
- package/Runtime/Core/Helper/Partials/LogHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/MathHelpers.cs +30 -30
- package/Runtime/Core/Helper/Partials/MathHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +388 -388
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/TransformHelpers.cs +167 -167
- package/Runtime/Core/Helper/Partials/TransformHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials.meta +2 -2
- package/Runtime/Core/Helper/ReflectionHelpers.cs +152 -152
- package/Runtime/Core/Helper/ReflectionHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/SpriteHelpers.cs +86 -86
- package/Runtime/Core/Helper/SpriteHelpers.cs.meta +11 -11
- package/Runtime/Core/Helper/StringInList.cs +31 -31
- package/Runtime/Core/Helper/StringInList.cs.meta +11 -11
- package/Runtime/Core/Helper/WallMath.cs +166 -166
- package/Runtime/Core/Math/Line.cs +55 -55
- package/Runtime/Core/Math/Parabola.cs +47 -47
- package/Runtime/Core/Math/PointPolygonCheck.cs +36 -36
- package/Runtime/Core/Math/PointPolygonCheck.cs.meta +11 -11
- package/Runtime/Core/Math/Range.cs +92 -92
- package/Runtime/Core/Math/XXHash.cs +310 -310
- package/Runtime/Core/Math/XXHash.cs.meta +11 -11
- package/Runtime/Core/Model/Direction.cs +43 -43
- package/Runtime/Core/OneOf/FastOneOf.cs +152 -152
- package/Runtime/Core/OneOf/None.cs +4 -4
- package/Runtime/Core/Random/AbstractRandom.cs +561 -561
- package/Runtime/Core/Random/DotNetRandom.cs +52 -52
- package/Runtime/Core/Random/DotNetRandom.cs.meta +2 -2
- package/Runtime/Core/Random/IRandom.cs +160 -160
- package/Runtime/Core/Random/NativePcgRandom.cs +97 -97
- package/Runtime/Core/Random/PRNG.cs +7 -7
- package/Runtime/Core/Random/PRNG.cs.meta +2 -2
- package/Runtime/Core/Random/PcgRandom.cs +149 -149
- package/Runtime/Core/Random/PerlinNoise.cs +369 -369
- package/Runtime/Core/Random/PerlinNoise.cs.meta +2 -2
- package/Runtime/Core/Random/RandomState.cs +131 -131
- package/Runtime/Core/Random/RandomUtilities.cs +26 -26
- package/Runtime/Core/Random/RandomUtilities.cs.meta +11 -11
- package/Runtime/Core/Random/RomuDuo.cs +116 -116
- package/Runtime/Core/Random/RomuDuo.cs.meta +2 -2
- package/Runtime/Core/Random/SplitMix64.cs +94 -94
- package/Runtime/Core/Random/SplitMix64.cs.meta +2 -2
- package/Runtime/Core/Random/SquirrelRandom.cs +84 -84
- package/Runtime/Core/Random/SystemRandom.cs +162 -162
- package/Runtime/Core/Random/ThreadLocalRandom.cs +12 -12
- package/Runtime/Core/Random/UnityRandom.cs +57 -57
- package/Runtime/Core/Random/UnityRandom.cs.meta +11 -11
- package/Runtime/Core/Random/WyRandom.cs +121 -121
- package/Runtime/Core/Random/WyRandom.cs.meta +2 -2
- package/Runtime/Core/Random/XorShiftRandom.cs +47 -47
- package/Runtime/Core/Random/XorShiftRandom.cs.meta +11 -11
- package/Runtime/Core/Random/XorShiroRandom.cs +117 -117
- package/Runtime/Core/Random/XorShiroRandom.cs.meta +2 -2
- package/Runtime/Core/Serialization/JsonConverters/Vector2Converter.cs +74 -74
- package/Runtime/Core/Serialization/JsonConverters/Vector3Converter.cs +81 -81
- package/Runtime/Core/Serialization/Serializer.cs +184 -184
- package/Runtime/Core/Threading/SingleThreadedThreadPool.cs +107 -107
- package/Runtime/Protobuf-Net/System.Buffers.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Collections.Immutable.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Collections.Immutable.xml +5379 -5379
- package/Runtime/Protobuf-Net/System.Collections.Immutable.xml.meta +7 -7
- package/Runtime/Protobuf-Net/System.Numerics.Vectors.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.xml +290 -290
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.xml.meta +7 -7
- package/Runtime/Protobuf-Net/protobuf-net.Core.dll.meta +33 -33
- package/Runtime/Protobuf-Net/protobuf-net.dll.meta +33 -33
- package/Runtime/UI/LayeredImage.cs +364 -364
- package/Runtime/UI/LayeredImage.cs.meta +2 -2
- package/Runtime/UI.meta +2 -2
- package/Runtime/Utils/AnimationEventEqualityComparer.cs +161 -161
- package/Runtime/Utils/AnimatorEnumStateMachine.cs +88 -88
- package/Runtime/Utils/Buffers.cs +32 -32
- package/Runtime/Utils/CenterPointOffset.cs +30 -30
- package/Runtime/Utils/CenterPointOffset.cs.meta +2 -2
- package/Runtime/Utils/CircleLineRenderer.cs +134 -134
- package/Runtime/Utils/CoroutineHandler.cs +4 -4
- package/Runtime/Utils/CoroutineHandler.cs.meta +2 -2
- package/Runtime/Utils/MatchColliderToSprite.cs +94 -94
- package/Runtime/Utils/MatchColliderToSprite.cs.meta +2 -2
- package/Runtime/Utils/Oscillator.cs +27 -27
- package/Runtime/Utils/RuntimeSingleton.cs +57 -57
- package/Runtime/Utils/RuntimeSingleton.cs.meta +11 -11
- package/Runtime/Utils/SetTextureImportData.cs +69 -69
- package/Runtime/Utils/SpriteRendererMetadata.cs +312 -312
- package/Runtime/Utils/SpriteRendererMetadata.cs.meta +2 -2
- package/Runtime/Utils/SpriteRendererSyncer.cs +100 -100
- package/Runtime/Utils/SpriteRendererSyncer.cs.meta +2 -2
- package/Runtime/Utils/TextureScale.cs +179 -179
- package/Runtime/Utils/TextureScale.cs.meta +2 -2
- package/Runtime/WallstopStudios.UnityHelpers.asmdef +13 -13
- package/Tests/Runtime/Attributes/ChildComponentTests.cs +81 -81
- package/Tests/Runtime/Attributes/Components/ExpectChildSpriteRenderers.cs +28 -28
- package/Tests/Runtime/Attributes/Components/ExpectParentSpriteRenderers.cs +28 -28
- package/Tests/Runtime/Attributes/ParentComponentTests.cs +68 -68
- package/Tests/Runtime/Components/RelationalComponentTester.cs +34 -34
- package/Tests/Runtime/Components/RelationalComponentTester.cs.meta +2 -2
- package/Tests/Runtime/Components.meta +2 -2
- package/Tests/Runtime/DataStructures/BalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/BalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/CyclicBufferTests.cs +324 -324
- package/Tests/Runtime/DataStructures/QuadTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/QuadTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/SpatialTreeTests.cs +130 -130
- package/Tests/Runtime/DataStructures/SpatialTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/UnbalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/UnbalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures.meta +8 -8
- package/Tests/Runtime/Extensions/DictionaryExtensionTests.cs +439 -439
- package/Tests/Runtime/Extensions/DictionaryExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +76 -76
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs +27 -27
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions/StringExtensionTests.cs +31 -31
- package/Tests/Runtime/Extensions/StringExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions.meta +2 -2
- package/Tests/Runtime/Helper/ArrayConverterTests.cs +19 -19
- package/Tests/Runtime/Helper/ArrayConverterTests.cs.meta +2 -2
- package/Tests/Runtime/Helper/ObjectHelperTests.cs +402 -402
- package/Tests/Runtime/Helper/ObjectHelperTests.cs.meta +2 -2
- package/Tests/Runtime/Helper/WallMathTests.cs +233 -233
- package/Tests/Runtime/Helper/WallMathTests.cs.meta +2 -2
- package/Tests/Runtime/Helper.meta +2 -2
- package/Tests/Runtime/Performance/KDTreePerformanceTests.cs +14 -14
- package/Tests/Runtime/Performance/KDTreePerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs +14 -14
- package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +139 -139
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +37 -37
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +2 -2
- package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs +154 -154
- package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs.meta +11 -11
- package/Tests/Runtime/Performance/UnbalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/Performance/UnbalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/Performance.meta +8 -8
- package/Tests/Runtime/Random/DotNetRandomTests.cs +9 -9
- package/Tests/Runtime/Random/DotNetRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/PcgRandomTests.cs +9 -9
- package/Tests/Runtime/Random/PcgRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/RandomTestBase.cs +787 -787
- package/Tests/Runtime/Random/RandomTestBase.cs.meta +11 -11
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs +9 -9
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs +9 -9
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/SquirrelRandomTests.cs +14 -14
- package/Tests/Runtime/Random/SquirrelRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/SystemRandomTests.cs +10 -10
- package/Tests/Runtime/Random/SystemRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/UnityRandomTests.cs +9 -9
- package/Tests/Runtime/Random/UnityRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/WyRandomTests.cs +9 -9
- package/Tests/Runtime/Random/WyRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/XorShiftRandomTests.cs +9 -9
- package/Tests/Runtime/Random/XorShiftRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/XorShiroRandomTests.cs +9 -9
- package/Tests/Runtime/Random/XorShiroRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random.meta +8 -8
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +84 -84
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs.meta +2 -2
- package/Tests/Runtime/Serialization.meta +2 -2
- package/Tests/Runtime/Utils/SpriteRendererMetadataTests.cs +399 -399
- package/Tests/Runtime/Utils/SpriteRendererMetadataTests.cs.meta +2 -2
- package/Tests/Runtime/Utils.meta +2 -2
- package/Tests/Runtime/WallstopStudios.UnityHelpers.Tests.Runtime.asmdef +22 -22
- package/Tests/Runtime/WallstopStudios.UnityHelpers.Tests.Runtime.asmdef.meta +7 -7
- package/Tests/Runtime.meta +8 -8
- package/package.json +38 -38
|
@@ -1,887 +1,887 @@
|
|
|
1
|
-
namespace UnityHelpers.Editor
|
|
2
|
-
{
|
|
3
|
-
#if UNITY_EDITOR
|
|
4
|
-
using System;
|
|
5
|
-
using System.Linq;
|
|
6
|
-
using System.Collections.Generic;
|
|
7
|
-
using System.Reflection;
|
|
8
|
-
using Core.Attributes;
|
|
9
|
-
using Core.Helper;
|
|
10
|
-
using UnityEngine;
|
|
11
|
-
using UnityEditor;
|
|
12
|
-
using UnityHelpers.Utils;
|
|
13
|
-
|
|
14
|
-
// https://gist.githubusercontent.com/yujen/5e1cd78e2a341260b38029de08a449da/raw/ac60c1002e0e14375de5b2b0a167af00df3f74b4/SeniaAnimationEventEditor.cs
|
|
15
|
-
public sealed class AnimationEventEditor : EditorWindow
|
|
16
|
-
{
|
|
17
|
-
private static readonly IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> TypesToMethods;
|
|
18
|
-
|
|
19
|
-
static AnimationEventEditor()
|
|
20
|
-
{
|
|
21
|
-
Dictionary<Type, IReadOnlyList<MethodInfo>> typesToMethods = AppDomain
|
|
22
|
-
.CurrentDomain.GetAssemblies()
|
|
23
|
-
.SelectMany(assembly => assembly.GetTypes())
|
|
24
|
-
.Where(type => type.IsClass)
|
|
25
|
-
.Where(type => typeof(MonoBehaviour).IsAssignableFrom(type))
|
|
26
|
-
.ToDictionary(
|
|
27
|
-
type => type,
|
|
28
|
-
type => (IReadOnlyList<MethodInfo>)type.GetPossibleAnimatorEventsForType()
|
|
29
|
-
);
|
|
30
|
-
foreach (KeyValuePair<Type, IReadOnlyList<MethodInfo>> entry in typesToMethods.ToList())
|
|
31
|
-
{
|
|
32
|
-
if (entry.Value.Count <= 0)
|
|
33
|
-
{
|
|
34
|
-
_ = typesToMethods.Remove(entry.Key);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
TypesToMethods = typesToMethods;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
[MenuItem("Tools/Unity Helpers/AnimationEvent Editor")]
|
|
42
|
-
private static void AnimationEventEditorMenu()
|
|
43
|
-
{
|
|
44
|
-
GetWindow(typeof(AnimationEventEditor));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public class AnimationEventItem
|
|
48
|
-
{
|
|
49
|
-
public AnimationEventItem(AnimationEvent animationEvent)
|
|
50
|
-
{
|
|
51
|
-
this.animationEvent = animationEvent;
|
|
52
|
-
search = string.Empty;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public Type selectedType;
|
|
56
|
-
public MethodInfo selectedMethod;
|
|
57
|
-
public string search;
|
|
58
|
-
public AnimationEvent animationEvent;
|
|
59
|
-
public Texture2D texture;
|
|
60
|
-
public bool isTextureReadable;
|
|
61
|
-
public bool isInvalidTextureRect;
|
|
62
|
-
public Sprite sprite;
|
|
63
|
-
public int? originalIndex;
|
|
64
|
-
public bool overrideEnumValues;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> Lookup =>
|
|
68
|
-
_explicitMode ? AnimationEventHelpers.TypesToMethods : TypesToMethods;
|
|
69
|
-
|
|
70
|
-
private int MaxFrameIndex =>
|
|
71
|
-
_currentClip == null
|
|
72
|
-
? 0
|
|
73
|
-
: (int)Math.Round(_currentClip.frameRate * _currentClip.length);
|
|
74
|
-
|
|
75
|
-
private Vector2 _scrollPosition;
|
|
76
|
-
private Animator _sourceAnimator;
|
|
77
|
-
private AnimationClip _currentClip;
|
|
78
|
-
private bool _explicitMode = true;
|
|
79
|
-
private bool _controlFrameTime = false;
|
|
80
|
-
private string _animationSearchString = string.Empty;
|
|
81
|
-
private List<ObjectReferenceKeyframe> _referenceCurve;
|
|
82
|
-
|
|
83
|
-
private readonly List<AnimationEvent> _baseClipEvents = new();
|
|
84
|
-
private readonly List<AnimationEventItem> _state = new();
|
|
85
|
-
private readonly Dictionary<AnimationEventItem, string> _lastSeenSearch = new();
|
|
86
|
-
|
|
87
|
-
private readonly Dictionary<
|
|
88
|
-
AnimationEventItem,
|
|
89
|
-
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>>
|
|
90
|
-
> _lookups = new();
|
|
91
|
-
|
|
92
|
-
private int _selectedFrameIndex = -1;
|
|
93
|
-
|
|
94
|
-
private void OnGUI()
|
|
95
|
-
{
|
|
96
|
-
Animator tmpAnimator =
|
|
97
|
-
EditorGUILayout.ObjectField(
|
|
98
|
-
"Animator Object",
|
|
99
|
-
_sourceAnimator,
|
|
100
|
-
typeof(Animator),
|
|
101
|
-
true
|
|
102
|
-
) as Animator;
|
|
103
|
-
if (tmpAnimator == null)
|
|
104
|
-
{
|
|
105
|
-
_sourceAnimator = null;
|
|
106
|
-
_state.Clear();
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (_sourceAnimator != tmpAnimator)
|
|
111
|
-
{
|
|
112
|
-
_sourceAnimator = tmpAnimator;
|
|
113
|
-
_currentClip = null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
_explicitMode = EditorGUILayout.Toggle(
|
|
117
|
-
new GUIContent(
|
|
118
|
-
"Explicit Mode",
|
|
119
|
-
"If true, restricts results to only those that explicitly with [AnimationEvent]"
|
|
120
|
-
),
|
|
121
|
-
_explicitMode
|
|
122
|
-
);
|
|
123
|
-
_controlFrameTime = EditorGUILayout.Toggle(
|
|
124
|
-
new GUIContent(
|
|
125
|
-
"Control Frame Time",
|
|
126
|
-
"Select to edit precise time of animation events instead of snapping to nearest frame"
|
|
127
|
-
),
|
|
128
|
-
_controlFrameTime
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
AnimationClip selectedClip = DrawAndFilterAnimationClips();
|
|
132
|
-
if (selectedClip == null)
|
|
133
|
-
{
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (_currentClip != selectedClip)
|
|
138
|
-
{
|
|
139
|
-
_currentClip = selectedClip;
|
|
140
|
-
RefreshAnimationEvents();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
_selectedFrameIndex = EditorGUILayout.IntField("FrameIndex", _selectedFrameIndex);
|
|
144
|
-
|
|
145
|
-
float frameRate = _currentClip.frameRate;
|
|
146
|
-
float oldFrameRate = frameRate;
|
|
147
|
-
if (GUILayout.Button("Add Event"))
|
|
148
|
-
{
|
|
149
|
-
if (0 <= _selectedFrameIndex)
|
|
150
|
-
{
|
|
151
|
-
_state.Add(
|
|
152
|
-
new AnimationEventItem(
|
|
153
|
-
new AnimationEvent { time = _selectedFrameIndex / frameRate }
|
|
154
|
-
)
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
frameRate = _currentClip.frameRate = EditorGUILayout.FloatField("FrameRate", frameRate);
|
|
160
|
-
DrawGuiLine(height: 5, color: new Color(0f, 0.5f, 1f, 1f));
|
|
161
|
-
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
|
162
|
-
|
|
163
|
-
// Need a copy because we might be mutating it
|
|
164
|
-
List<AnimationEventItem> stateCopy = _state.ToList();
|
|
165
|
-
for (int i = 0; i < stateCopy.Count; ++i)
|
|
166
|
-
{
|
|
167
|
-
AnimationEventItem item = stateCopy[i];
|
|
168
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
169
|
-
|
|
170
|
-
int frame = Mathf.RoundToInt(animEvent.time * oldFrameRate);
|
|
171
|
-
EditorGUILayout.PrefixLabel("Frame " + frame);
|
|
172
|
-
|
|
173
|
-
DrawSpritePreview(item);
|
|
174
|
-
|
|
175
|
-
EditorGUI.indentLevel++;
|
|
176
|
-
RenderAnimationEventItem(item, frame, frameRate);
|
|
177
|
-
|
|
178
|
-
if (i != stateCopy.Count - 1)
|
|
179
|
-
{
|
|
180
|
-
DrawGuiLine(height: 3, color: new Color(0f, 1f, 0.3f, 1f));
|
|
181
|
-
EditorGUILayout.Space();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
EditorGUI.indentLevel--;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
EditorGUILayout.EndScrollView();
|
|
188
|
-
|
|
189
|
-
DrawControlButtons();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
private AnimationClip DrawAndFilterAnimationClips()
|
|
193
|
-
{
|
|
194
|
-
_animationSearchString = EditorGUILayout.TextField(
|
|
195
|
-
"Animation Search",
|
|
196
|
-
_animationSearchString
|
|
197
|
-
);
|
|
198
|
-
List<AnimationClip> animationClips =
|
|
199
|
-
_sourceAnimator.runtimeAnimatorController.animationClips.ToList();
|
|
200
|
-
int selectedIndex;
|
|
201
|
-
if (string.IsNullOrEmpty(_animationSearchString) || _animationSearchString == "*")
|
|
202
|
-
{
|
|
203
|
-
selectedIndex = EditorGUILayout.Popup(
|
|
204
|
-
"Animation",
|
|
205
|
-
animationClips.IndexOf(_currentClip),
|
|
206
|
-
animationClips.Select(clip => clip.name).ToArray()
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
else
|
|
210
|
-
{
|
|
211
|
-
List<string> searchTerms = _animationSearchString
|
|
212
|
-
.Split(" ")
|
|
213
|
-
.Select(searchPart => searchPart.ToLowerInvariant().Trim())
|
|
214
|
-
.Where(trimmed => !string.IsNullOrEmpty(trimmed) && trimmed != "*")
|
|
215
|
-
.ToList();
|
|
216
|
-
|
|
217
|
-
if (0 < searchTerms.Count)
|
|
218
|
-
{
|
|
219
|
-
foreach (AnimationClip animationClip in animationClips.ToList())
|
|
220
|
-
{
|
|
221
|
-
if (_currentClip == animationClip)
|
|
222
|
-
{
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
foreach (string searchTerm in searchTerms)
|
|
227
|
-
{
|
|
228
|
-
if (animationClip.name.ToLowerInvariant().Contains(searchTerm))
|
|
229
|
-
{
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
animationClips.Remove(animationClip);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
selectedIndex = EditorGUILayout.Popup(
|
|
239
|
-
"Animation",
|
|
240
|
-
animationClips.IndexOf(_currentClip),
|
|
241
|
-
animationClips.Select(clip => clip.name).ToArray()
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (selectedIndex < 0)
|
|
246
|
-
{
|
|
247
|
-
_currentClip = null;
|
|
248
|
-
RefreshAnimationEvents();
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return animationClips[selectedIndex];
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
private int AnimationEventComparison(AnimationEventItem lhs, AnimationEventItem rhs)
|
|
256
|
-
{
|
|
257
|
-
if (ReferenceEquals(lhs, rhs))
|
|
258
|
-
{
|
|
259
|
-
return 0;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (ReferenceEquals(null, rhs))
|
|
263
|
-
{
|
|
264
|
-
return -1;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (ReferenceEquals(null, lhs))
|
|
268
|
-
{
|
|
269
|
-
return 1;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return AnimationEventEqualityComparer.Instance.Compare(
|
|
273
|
-
lhs.animationEvent,
|
|
274
|
-
rhs.animationEvent
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
private void DrawControlButtons()
|
|
279
|
-
{
|
|
280
|
-
if (
|
|
281
|
-
_baseClipEvents.SequenceEqual(
|
|
282
|
-
_state.Select(item => item.animationEvent),
|
|
283
|
-
AnimationEventEqualityComparer.Instance
|
|
284
|
-
)
|
|
285
|
-
)
|
|
286
|
-
{
|
|
287
|
-
GUILayout.Label("No changes detected...");
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
Color oldColor = GUI.color;
|
|
292
|
-
GUI.color = Color.green;
|
|
293
|
-
if (GUILayout.Button("Save"))
|
|
294
|
-
{
|
|
295
|
-
SaveAnimation();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
GUI.color = oldColor;
|
|
299
|
-
if (GUILayout.Button("Reset"))
|
|
300
|
-
{
|
|
301
|
-
RefreshAnimationEvents();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
!_state.SequenceEqual(
|
|
306
|
-
_state.OrderBy(
|
|
307
|
-
item => item.animationEvent,
|
|
308
|
-
AnimationEventEqualityComparer.Instance
|
|
309
|
-
)
|
|
310
|
-
)
|
|
311
|
-
)
|
|
312
|
-
{
|
|
313
|
-
if (GUILayout.Button("Re-Order"))
|
|
314
|
-
{
|
|
315
|
-
_state.Sort(AnimationEventComparison);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private void RenderAnimationEventItem(AnimationEventItem item, int frame, float frameRate)
|
|
321
|
-
{
|
|
322
|
-
int index = _state.IndexOf(item);
|
|
323
|
-
EditorGUILayout.BeginHorizontal();
|
|
324
|
-
try
|
|
325
|
-
{
|
|
326
|
-
if (
|
|
327
|
-
1 <= index
|
|
328
|
-
&& Math.Abs(_state[index - 1].animationEvent.time - item.animationEvent.time)
|
|
329
|
-
< 0.001f
|
|
330
|
-
&& GUILayout.Button("Move Up")
|
|
331
|
-
)
|
|
332
|
-
{
|
|
333
|
-
_state.RemoveAt(index);
|
|
334
|
-
_state.Insert(index - 1, item);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
index < _state.Count - 1
|
|
339
|
-
&& Math.Abs(_state[index + 1].animationEvent.time - item.animationEvent.time)
|
|
340
|
-
< 0.001f
|
|
341
|
-
&& GUILayout.Button("Move Down")
|
|
342
|
-
)
|
|
343
|
-
{
|
|
344
|
-
_state.RemoveAt(index);
|
|
345
|
-
_state.Insert(index + 1, item);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
0 <= index
|
|
350
|
-
&& index < _baseClipEvents.Count
|
|
351
|
-
&& !AnimationEventEqualityComparer.Instance.Equals(
|
|
352
|
-
item.animationEvent,
|
|
353
|
-
_baseClipEvents[index]
|
|
354
|
-
)
|
|
355
|
-
&& GUILayout.Button("Reset")
|
|
356
|
-
)
|
|
357
|
-
{
|
|
358
|
-
AnimationEventEqualityComparer.Instance.CopyInto(
|
|
359
|
-
item.animationEvent,
|
|
360
|
-
_baseClipEvents[index]
|
|
361
|
-
);
|
|
362
|
-
item.selectedType = null;
|
|
363
|
-
item.selectedMethod = null;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (GUILayout.Button($"Remove Event at frame {frame}"))
|
|
367
|
-
{
|
|
368
|
-
_state.Remove(item);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
finally
|
|
373
|
-
{
|
|
374
|
-
EditorGUILayout.EndHorizontal();
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup = FilterLookup(item);
|
|
378
|
-
|
|
379
|
-
TryPopulateTypeAndMethod(item, lookup);
|
|
380
|
-
|
|
381
|
-
List<Type> orderedTypes = lookup.Keys.OrderBy(type => type.FullName).Take(20).ToList();
|
|
382
|
-
if (item.selectedType != null && !orderedTypes.Contains(item.selectedType))
|
|
383
|
-
{
|
|
384
|
-
orderedTypes.Add(item.selectedType);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
string[] orderedTypeNames = orderedTypes.Select(type => type.FullName).ToArray();
|
|
388
|
-
|
|
389
|
-
SelectFrameTime(item, frame, frameRate);
|
|
390
|
-
|
|
391
|
-
SelectFunctionName(item);
|
|
392
|
-
|
|
393
|
-
if (!SelectTypes(item, orderedTypes, orderedTypeNames))
|
|
394
|
-
{
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (!SelectMethods(item, lookup))
|
|
399
|
-
{
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
RenderEventParameters(item);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
private void SelectFrameTime(AnimationEventItem item, int frame, float frameRate)
|
|
407
|
-
{
|
|
408
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
409
|
-
float previousTime = animEvent.time;
|
|
410
|
-
if (_controlFrameTime)
|
|
411
|
-
{
|
|
412
|
-
float proposedFrameTime = EditorGUILayout.FloatField("FrameTime", animEvent.time);
|
|
413
|
-
animEvent.time = Mathf.Clamp(proposedFrameTime, 0, _currentClip.length);
|
|
414
|
-
}
|
|
415
|
-
else
|
|
416
|
-
{
|
|
417
|
-
int proposedFrame = EditorGUILayout.IntField("FrameIndex", frame);
|
|
418
|
-
animEvent.time = Mathf.Clamp(proposedFrame, 0, MaxFrameIndex) / frameRate;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
422
|
-
if (previousTime != animEvent.time)
|
|
423
|
-
{
|
|
424
|
-
item.texture = null;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
private void SelectFunctionName(AnimationEventItem item)
|
|
429
|
-
{
|
|
430
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
431
|
-
animEvent.functionName = EditorGUILayout.TextField(
|
|
432
|
-
"FunctionName",
|
|
433
|
-
animEvent.functionName ?? string.Empty
|
|
434
|
-
);
|
|
435
|
-
if (!_explicitMode)
|
|
436
|
-
{
|
|
437
|
-
item.search = EditorGUILayout.TextField("Search", item.search);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
private void TryPopulateTypeAndMethod(
|
|
442
|
-
AnimationEventItem item,
|
|
443
|
-
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup
|
|
444
|
-
)
|
|
445
|
-
{
|
|
446
|
-
if (item.selectedType != null)
|
|
447
|
-
{
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
452
|
-
foreach (
|
|
453
|
-
KeyValuePair<Type, IReadOnlyList<MethodInfo>> entry in lookup.OrderBy(kvp =>
|
|
454
|
-
kvp.Key.FullName
|
|
455
|
-
)
|
|
456
|
-
)
|
|
457
|
-
{
|
|
458
|
-
foreach (MethodInfo method in entry.Value)
|
|
459
|
-
{
|
|
460
|
-
if (
|
|
461
|
-
string.Equals(method.Name, animEvent.functionName, StringComparison.Ordinal)
|
|
462
|
-
)
|
|
463
|
-
{
|
|
464
|
-
item.selectedType = entry.Key;
|
|
465
|
-
item.selectedMethod = method;
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
private bool SelectTypes(
|
|
473
|
-
AnimationEventItem item,
|
|
474
|
-
IList<Type> orderedTypes,
|
|
475
|
-
string[] orderedTypeNames
|
|
476
|
-
)
|
|
477
|
-
{
|
|
478
|
-
int existingIndex = orderedTypes.IndexOf(item.selectedType);
|
|
479
|
-
int selectedTypeIndex = EditorGUILayout.Popup(
|
|
480
|
-
"TypeName",
|
|
481
|
-
existingIndex,
|
|
482
|
-
orderedTypeNames
|
|
483
|
-
);
|
|
484
|
-
item.selectedType = selectedTypeIndex < 0 ? null : orderedTypes[selectedTypeIndex];
|
|
485
|
-
if (existingIndex != selectedTypeIndex)
|
|
486
|
-
{
|
|
487
|
-
item.selectedMethod = null;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return item.selectedType != null;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
private bool SelectMethods(
|
|
494
|
-
AnimationEventItem item,
|
|
495
|
-
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup
|
|
496
|
-
)
|
|
497
|
-
{
|
|
498
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
499
|
-
if (!lookup.TryGetValue(item.selectedType, out IReadOnlyList<MethodInfo> methods))
|
|
500
|
-
{
|
|
501
|
-
methods = new List<MethodInfo>(0);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (item.selectedMethod == null || !methods.Contains(item.selectedMethod))
|
|
505
|
-
{
|
|
506
|
-
foreach (MethodInfo method in methods)
|
|
507
|
-
{
|
|
508
|
-
if (
|
|
509
|
-
string.Equals(method.Name, animEvent.functionName, StringComparison.Ordinal)
|
|
510
|
-
)
|
|
511
|
-
{
|
|
512
|
-
item.selectedMethod = method;
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (item.selectedMethod != null && !methods.Contains(item.selectedMethod))
|
|
518
|
-
{
|
|
519
|
-
methods = methods.Concat(Enumerables.Of(item.selectedMethod)).ToList();
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
int selectedMethodIndex = EditorGUILayout.Popup(
|
|
524
|
-
"MethodName",
|
|
525
|
-
methods.ToList().IndexOf(item.selectedMethod),
|
|
526
|
-
methods.Select(method => method.Name).ToArray()
|
|
527
|
-
);
|
|
528
|
-
if (0 <= selectedMethodIndex)
|
|
529
|
-
{
|
|
530
|
-
item.selectedMethod = methods[selectedMethodIndex];
|
|
531
|
-
animEvent.functionName = item.selectedMethod.Name;
|
|
532
|
-
return true;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
private void RenderEventParameters(AnimationEventItem item)
|
|
539
|
-
{
|
|
540
|
-
AnimationEvent animEvent = item.animationEvent;
|
|
541
|
-
ParameterInfo[] arrayParameterInfo = item.selectedMethod.GetParameters();
|
|
542
|
-
if (arrayParameterInfo.Length == 1)
|
|
543
|
-
{
|
|
544
|
-
EditorGUI.indentLevel++;
|
|
545
|
-
|
|
546
|
-
Type paramType = arrayParameterInfo[0].ParameterType;
|
|
547
|
-
if (paramType == typeof(int))
|
|
548
|
-
{
|
|
549
|
-
animEvent.intParameter = EditorGUILayout.IntField(
|
|
550
|
-
"IntParameter",
|
|
551
|
-
animEvent.intParameter
|
|
552
|
-
);
|
|
553
|
-
}
|
|
554
|
-
else if (paramType.BaseType == typeof(Enum))
|
|
555
|
-
{
|
|
556
|
-
string[] enumNamesArray = Enum.GetNames(paramType);
|
|
557
|
-
List<string> enumNames = enumNamesArray.ToList();
|
|
558
|
-
string enumName = Enum.GetName(paramType, animEvent.intParameter);
|
|
559
|
-
|
|
560
|
-
int index = EditorGUILayout.Popup(
|
|
561
|
-
$"{paramType.Name}",
|
|
562
|
-
enumNames.IndexOf(enumName),
|
|
563
|
-
enumNamesArray
|
|
564
|
-
);
|
|
565
|
-
if (0 <= index)
|
|
566
|
-
{
|
|
567
|
-
animEvent.intParameter = (int)Enum.Parse(paramType, enumNames[index]);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
item.overrideEnumValues = EditorGUILayout.Toggle(
|
|
571
|
-
"Override",
|
|
572
|
-
item.overrideEnumValues
|
|
573
|
-
);
|
|
574
|
-
if (item.overrideEnumValues)
|
|
575
|
-
{
|
|
576
|
-
animEvent.intParameter = EditorGUILayout.IntField(
|
|
577
|
-
"IntParameter",
|
|
578
|
-
animEvent.intParameter
|
|
579
|
-
);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
else if (paramType == typeof(float))
|
|
583
|
-
{
|
|
584
|
-
animEvent.floatParameter = EditorGUILayout.FloatField(
|
|
585
|
-
"FloatParameter",
|
|
586
|
-
animEvent.floatParameter
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
else if (paramType == typeof(string))
|
|
590
|
-
{
|
|
591
|
-
animEvent.stringParameter = EditorGUILayout.TextField(
|
|
592
|
-
"StringParameter",
|
|
593
|
-
animEvent.stringParameter
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
else if (paramType == typeof(UnityEngine.Object))
|
|
597
|
-
{
|
|
598
|
-
animEvent.objectReferenceParameter = EditorGUILayout.ObjectField(
|
|
599
|
-
"ObjectReferenceParameter",
|
|
600
|
-
animEvent.objectReferenceParameter,
|
|
601
|
-
typeof(UnityEngine.Object),
|
|
602
|
-
true
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
EditorGUI.indentLevel--;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
private IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> FilterLookup(
|
|
611
|
-
AnimationEventItem item
|
|
612
|
-
)
|
|
613
|
-
{
|
|
614
|
-
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup;
|
|
615
|
-
if (!_explicitMode)
|
|
616
|
-
{
|
|
617
|
-
if (
|
|
618
|
-
!_lastSeenSearch.TryGetValue(item, out string lastSearch)
|
|
619
|
-
|| !string.Equals(
|
|
620
|
-
lastSearch,
|
|
621
|
-
item.search,
|
|
622
|
-
StringComparison.InvariantCultureIgnoreCase
|
|
623
|
-
)
|
|
624
|
-
|| !_lookups.TryGetValue(item, out lookup)
|
|
625
|
-
)
|
|
626
|
-
{
|
|
627
|
-
Dictionary<Type, List<MethodInfo>> filtered = Lookup.ToDictionary(
|
|
628
|
-
kvp => kvp.Key,
|
|
629
|
-
kvp => kvp.Value.ToList()
|
|
630
|
-
);
|
|
631
|
-
List<string> searchTerms = item
|
|
632
|
-
.search.Split(" ")
|
|
633
|
-
.Select(searchTerm => searchTerm.Trim().ToLowerInvariant())
|
|
634
|
-
.Where(trimmed => !string.IsNullOrEmpty(trimmed) && trimmed != "*")
|
|
635
|
-
.ToList();
|
|
636
|
-
|
|
637
|
-
if (0 < searchTerms.Count)
|
|
638
|
-
{
|
|
639
|
-
foreach (KeyValuePair<Type, List<MethodInfo>> entry in filtered.ToList())
|
|
640
|
-
{
|
|
641
|
-
foreach (string searchTerm in searchTerms)
|
|
642
|
-
{
|
|
643
|
-
if (entry.Key.FullName.ToLowerInvariant().Contains(searchTerm))
|
|
644
|
-
{
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (
|
|
649
|
-
entry.Value.Any(methodInfo =>
|
|
650
|
-
methodInfo.Name.ToLowerInvariant().Contains(searchTerm)
|
|
651
|
-
)
|
|
652
|
-
)
|
|
653
|
-
{
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
_ = filtered.Remove(entry.Key);
|
|
658
|
-
break;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
_lookups[item] = lookup = filtered.ToDictionary(
|
|
664
|
-
kvp => kvp.Key,
|
|
665
|
-
kvp => (IReadOnlyList<MethodInfo>)kvp.Value
|
|
666
|
-
);
|
|
667
|
-
_lastSeenSearch[item] = item.search;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
else
|
|
671
|
-
{
|
|
672
|
-
lookup = Lookup;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
return lookup;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
private void DrawSpritePreview(AnimationEventItem item)
|
|
679
|
-
{
|
|
680
|
-
SetupPreviewData(item);
|
|
681
|
-
|
|
682
|
-
string spriteName = item.sprite == null ? string.Empty : item.sprite.name;
|
|
683
|
-
if (item.texture != null)
|
|
684
|
-
{
|
|
685
|
-
GUILayout.Label(item.texture);
|
|
686
|
-
}
|
|
687
|
-
else if (!item.isTextureReadable && !string.IsNullOrEmpty(spriteName))
|
|
688
|
-
{
|
|
689
|
-
EditorGUILayout.BeginHorizontal();
|
|
690
|
-
try
|
|
691
|
-
{
|
|
692
|
-
GUILayout.Label($"Sprite '{spriteName}' required \"Read/Write\" enabled");
|
|
693
|
-
if (item.sprite != null && GUILayout.Button("Fix"))
|
|
694
|
-
{
|
|
695
|
-
string assetPath = AssetDatabase.GetAssetPath(item.sprite.texture);
|
|
696
|
-
if (string.IsNullOrEmpty(assetPath))
|
|
697
|
-
{
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
TextureImporter tImporter =
|
|
702
|
-
AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
703
|
-
if (tImporter == null)
|
|
704
|
-
{
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
tImporter.isReadable = true;
|
|
709
|
-
EditorUtility.SetDirty(tImporter);
|
|
710
|
-
tImporter.SaveAndReimport();
|
|
711
|
-
EditorUtility.SetDirty(item.sprite);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
finally
|
|
715
|
-
{
|
|
716
|
-
EditorGUILayout.EndHorizontal();
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
else if (item.isInvalidTextureRect && !string.IsNullOrEmpty(spriteName))
|
|
720
|
-
{
|
|
721
|
-
GUILayout.Label($"Sprite '{spriteName}' is packed too tightly inside its texture");
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
private void SetupPreviewData(AnimationEventItem item)
|
|
726
|
-
{
|
|
727
|
-
if (item.texture != null)
|
|
728
|
-
{
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
if (TryFindSpriteForEvent(item, out Sprite currentSprite))
|
|
733
|
-
{
|
|
734
|
-
item.sprite = currentSprite;
|
|
735
|
-
item.isTextureReadable = currentSprite.texture.isReadable;
|
|
736
|
-
item.isInvalidTextureRect = false;
|
|
737
|
-
if (item.isTextureReadable)
|
|
738
|
-
{
|
|
739
|
-
Rect? maybeTextureRect = null;
|
|
740
|
-
try
|
|
741
|
-
{
|
|
742
|
-
maybeTextureRect = currentSprite.textureRect;
|
|
743
|
-
}
|
|
744
|
-
catch
|
|
745
|
-
{
|
|
746
|
-
item.isInvalidTextureRect = true;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
if (maybeTextureRect != null)
|
|
750
|
-
{
|
|
751
|
-
Rect textureRect = maybeTextureRect.Value;
|
|
752
|
-
item.texture = CopyTexture(textureRect, currentSprite.texture);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
else
|
|
757
|
-
{
|
|
758
|
-
item.sprite = null;
|
|
759
|
-
item.isTextureReadable = false;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
private bool TryFindSpriteForEvent(AnimationEventItem item, out Sprite sprite)
|
|
764
|
-
{
|
|
765
|
-
sprite = null;
|
|
766
|
-
foreach (
|
|
767
|
-
ObjectReferenceKeyframe keyFrame in _referenceCurve
|
|
768
|
-
?? Enumerable.Empty<ObjectReferenceKeyframe>()
|
|
769
|
-
)
|
|
770
|
-
{
|
|
771
|
-
if (keyFrame.time <= item.animationEvent.time)
|
|
772
|
-
{
|
|
773
|
-
Sprite frameSprite = keyFrame.value as Sprite;
|
|
774
|
-
if (frameSprite == null)
|
|
775
|
-
{
|
|
776
|
-
continue;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
sprite = frameSprite;
|
|
780
|
-
continue;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
return sprite != null;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
return sprite != null;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
private Texture2D CopyTexture(Rect textureRect, Texture2D sourceTexture)
|
|
790
|
-
{
|
|
791
|
-
int width = (int)Math.Ceiling(textureRect.width);
|
|
792
|
-
int height = (int)Math.Ceiling(textureRect.height);
|
|
793
|
-
Texture2D texture = new(width, height)
|
|
794
|
-
{
|
|
795
|
-
wrapMode = TextureWrapMode.Clamp,
|
|
796
|
-
filterMode = FilterMode.Point,
|
|
797
|
-
};
|
|
798
|
-
Vector2 offset = textureRect.position;
|
|
799
|
-
int offsetX = (int)Math.Ceiling(offset.x);
|
|
800
|
-
int offsetY = (int)Math.Ceiling(offset.y);
|
|
801
|
-
for (int x = 0; x < width; ++x)
|
|
802
|
-
{
|
|
803
|
-
for (int y = 0; y < height; ++y)
|
|
804
|
-
{
|
|
805
|
-
Color sourcePixel = sourceTexture.GetPixel(offsetX + x, offsetY + y);
|
|
806
|
-
texture.SetPixel(x, y, sourcePixel);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
texture.Apply();
|
|
811
|
-
return texture;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
private void RefreshAnimationEvents()
|
|
815
|
-
{
|
|
816
|
-
_state.Clear();
|
|
817
|
-
_baseClipEvents.Clear();
|
|
818
|
-
_lookups.Clear();
|
|
819
|
-
_lastSeenSearch.Clear();
|
|
820
|
-
if (_currentClip == null)
|
|
821
|
-
{
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
for (int i = 0; i < _currentClip.events.Length; i++)
|
|
826
|
-
{
|
|
827
|
-
AnimationEvent animEvent = _currentClip.events[i];
|
|
828
|
-
_state.Add(new AnimationEventItem(animEvent) { originalIndex = i });
|
|
829
|
-
_baseClipEvents.Add(AnimationEventEqualityComparer.Instance.Copy(animEvent));
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
_selectedFrameIndex = MaxFrameIndex;
|
|
833
|
-
_referenceCurve = AnimationUtility
|
|
834
|
-
.GetObjectReferenceCurve(
|
|
835
|
-
_currentClip,
|
|
836
|
-
EditorCurveBinding.PPtrCurve("", typeof(SpriteRenderer), "m_Sprite")
|
|
837
|
-
)
|
|
838
|
-
.ToList();
|
|
839
|
-
_referenceCurve.Sort(
|
|
840
|
-
(lhs, rhs) =>
|
|
841
|
-
{
|
|
842
|
-
int comparison = lhs.time.CompareTo(rhs.time);
|
|
843
|
-
if (comparison != 0)
|
|
844
|
-
{
|
|
845
|
-
return comparison;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
string lhsName =
|
|
849
|
-
lhs.value == null ? string.Empty : lhs.value.name ?? string.Empty;
|
|
850
|
-
string rhsName =
|
|
851
|
-
rhs.value == null ? string.Empty : rhs.value.name ?? string.Empty;
|
|
852
|
-
return string.Compare(lhsName, rhsName, StringComparison.OrdinalIgnoreCase);
|
|
853
|
-
}
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
private void SaveAnimation()
|
|
858
|
-
{
|
|
859
|
-
if (_currentClip != null)
|
|
860
|
-
{
|
|
861
|
-
AnimationUtility.SetAnimationEvents(
|
|
862
|
-
_currentClip,
|
|
863
|
-
_state.Select(item => item.animationEvent).ToArray()
|
|
864
|
-
);
|
|
865
|
-
EditorUtility.SetDirty(_currentClip);
|
|
866
|
-
AssetDatabase.SaveAssetIfDirty(_currentClip);
|
|
867
|
-
_baseClipEvents.Clear();
|
|
868
|
-
foreach (AnimationEventItem item in _state)
|
|
869
|
-
{
|
|
870
|
-
_baseClipEvents.Add(
|
|
871
|
-
AnimationEventEqualityComparer.Instance.Copy(item.animationEvent)
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
private void DrawGuiLine(int height = 1, Color? color = null)
|
|
878
|
-
{
|
|
879
|
-
Rect rect = EditorGUILayout.GetControlRect(false, height);
|
|
880
|
-
rect.height = height;
|
|
881
|
-
int minusWidth = EditorGUI.indentLevel * 16;
|
|
882
|
-
rect.xMin += minusWidth;
|
|
883
|
-
EditorGUI.DrawRect(rect, color ?? new Color(0.5f, 0.5f, 0.5f, 1f));
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
#endif
|
|
887
|
-
}
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
#if UNITY_EDITOR
|
|
4
|
+
using System;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Collections.Generic;
|
|
7
|
+
using System.Reflection;
|
|
8
|
+
using Core.Attributes;
|
|
9
|
+
using Core.Helper;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
using UnityEditor;
|
|
12
|
+
using UnityHelpers.Utils;
|
|
13
|
+
|
|
14
|
+
// https://gist.githubusercontent.com/yujen/5e1cd78e2a341260b38029de08a449da/raw/ac60c1002e0e14375de5b2b0a167af00df3f74b4/SeniaAnimationEventEditor.cs
|
|
15
|
+
public sealed class AnimationEventEditor : EditorWindow
|
|
16
|
+
{
|
|
17
|
+
private static readonly IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> TypesToMethods;
|
|
18
|
+
|
|
19
|
+
static AnimationEventEditor()
|
|
20
|
+
{
|
|
21
|
+
Dictionary<Type, IReadOnlyList<MethodInfo>> typesToMethods = AppDomain
|
|
22
|
+
.CurrentDomain.GetAssemblies()
|
|
23
|
+
.SelectMany(assembly => assembly.GetTypes())
|
|
24
|
+
.Where(type => type.IsClass)
|
|
25
|
+
.Where(type => typeof(MonoBehaviour).IsAssignableFrom(type))
|
|
26
|
+
.ToDictionary(
|
|
27
|
+
type => type,
|
|
28
|
+
type => (IReadOnlyList<MethodInfo>)type.GetPossibleAnimatorEventsForType()
|
|
29
|
+
);
|
|
30
|
+
foreach (KeyValuePair<Type, IReadOnlyList<MethodInfo>> entry in typesToMethods.ToList())
|
|
31
|
+
{
|
|
32
|
+
if (entry.Value.Count <= 0)
|
|
33
|
+
{
|
|
34
|
+
_ = typesToMethods.Remove(entry.Key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TypesToMethods = typesToMethods;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[MenuItem("Tools/Unity Helpers/AnimationEvent Editor")]
|
|
42
|
+
private static void AnimationEventEditorMenu()
|
|
43
|
+
{
|
|
44
|
+
GetWindow(typeof(AnimationEventEditor));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public class AnimationEventItem
|
|
48
|
+
{
|
|
49
|
+
public AnimationEventItem(AnimationEvent animationEvent)
|
|
50
|
+
{
|
|
51
|
+
this.animationEvent = animationEvent;
|
|
52
|
+
search = string.Empty;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public Type selectedType;
|
|
56
|
+
public MethodInfo selectedMethod;
|
|
57
|
+
public string search;
|
|
58
|
+
public AnimationEvent animationEvent;
|
|
59
|
+
public Texture2D texture;
|
|
60
|
+
public bool isTextureReadable;
|
|
61
|
+
public bool isInvalidTextureRect;
|
|
62
|
+
public Sprite sprite;
|
|
63
|
+
public int? originalIndex;
|
|
64
|
+
public bool overrideEnumValues;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> Lookup =>
|
|
68
|
+
_explicitMode ? AnimationEventHelpers.TypesToMethods : TypesToMethods;
|
|
69
|
+
|
|
70
|
+
private int MaxFrameIndex =>
|
|
71
|
+
_currentClip == null
|
|
72
|
+
? 0
|
|
73
|
+
: (int)Math.Round(_currentClip.frameRate * _currentClip.length);
|
|
74
|
+
|
|
75
|
+
private Vector2 _scrollPosition;
|
|
76
|
+
private Animator _sourceAnimator;
|
|
77
|
+
private AnimationClip _currentClip;
|
|
78
|
+
private bool _explicitMode = true;
|
|
79
|
+
private bool _controlFrameTime = false;
|
|
80
|
+
private string _animationSearchString = string.Empty;
|
|
81
|
+
private List<ObjectReferenceKeyframe> _referenceCurve;
|
|
82
|
+
|
|
83
|
+
private readonly List<AnimationEvent> _baseClipEvents = new();
|
|
84
|
+
private readonly List<AnimationEventItem> _state = new();
|
|
85
|
+
private readonly Dictionary<AnimationEventItem, string> _lastSeenSearch = new();
|
|
86
|
+
|
|
87
|
+
private readonly Dictionary<
|
|
88
|
+
AnimationEventItem,
|
|
89
|
+
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>>
|
|
90
|
+
> _lookups = new();
|
|
91
|
+
|
|
92
|
+
private int _selectedFrameIndex = -1;
|
|
93
|
+
|
|
94
|
+
private void OnGUI()
|
|
95
|
+
{
|
|
96
|
+
Animator tmpAnimator =
|
|
97
|
+
EditorGUILayout.ObjectField(
|
|
98
|
+
"Animator Object",
|
|
99
|
+
_sourceAnimator,
|
|
100
|
+
typeof(Animator),
|
|
101
|
+
true
|
|
102
|
+
) as Animator;
|
|
103
|
+
if (tmpAnimator == null)
|
|
104
|
+
{
|
|
105
|
+
_sourceAnimator = null;
|
|
106
|
+
_state.Clear();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (_sourceAnimator != tmpAnimator)
|
|
111
|
+
{
|
|
112
|
+
_sourceAnimator = tmpAnimator;
|
|
113
|
+
_currentClip = null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_explicitMode = EditorGUILayout.Toggle(
|
|
117
|
+
new GUIContent(
|
|
118
|
+
"Explicit Mode",
|
|
119
|
+
"If true, restricts results to only those that explicitly with [AnimationEvent]"
|
|
120
|
+
),
|
|
121
|
+
_explicitMode
|
|
122
|
+
);
|
|
123
|
+
_controlFrameTime = EditorGUILayout.Toggle(
|
|
124
|
+
new GUIContent(
|
|
125
|
+
"Control Frame Time",
|
|
126
|
+
"Select to edit precise time of animation events instead of snapping to nearest frame"
|
|
127
|
+
),
|
|
128
|
+
_controlFrameTime
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
AnimationClip selectedClip = DrawAndFilterAnimationClips();
|
|
132
|
+
if (selectedClip == null)
|
|
133
|
+
{
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (_currentClip != selectedClip)
|
|
138
|
+
{
|
|
139
|
+
_currentClip = selectedClip;
|
|
140
|
+
RefreshAnimationEvents();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_selectedFrameIndex = EditorGUILayout.IntField("FrameIndex", _selectedFrameIndex);
|
|
144
|
+
|
|
145
|
+
float frameRate = _currentClip.frameRate;
|
|
146
|
+
float oldFrameRate = frameRate;
|
|
147
|
+
if (GUILayout.Button("Add Event"))
|
|
148
|
+
{
|
|
149
|
+
if (0 <= _selectedFrameIndex)
|
|
150
|
+
{
|
|
151
|
+
_state.Add(
|
|
152
|
+
new AnimationEventItem(
|
|
153
|
+
new AnimationEvent { time = _selectedFrameIndex / frameRate }
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
frameRate = _currentClip.frameRate = EditorGUILayout.FloatField("FrameRate", frameRate);
|
|
160
|
+
DrawGuiLine(height: 5, color: new Color(0f, 0.5f, 1f, 1f));
|
|
161
|
+
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
|
162
|
+
|
|
163
|
+
// Need a copy because we might be mutating it
|
|
164
|
+
List<AnimationEventItem> stateCopy = _state.ToList();
|
|
165
|
+
for (int i = 0; i < stateCopy.Count; ++i)
|
|
166
|
+
{
|
|
167
|
+
AnimationEventItem item = stateCopy[i];
|
|
168
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
169
|
+
|
|
170
|
+
int frame = Mathf.RoundToInt(animEvent.time * oldFrameRate);
|
|
171
|
+
EditorGUILayout.PrefixLabel("Frame " + frame);
|
|
172
|
+
|
|
173
|
+
DrawSpritePreview(item);
|
|
174
|
+
|
|
175
|
+
EditorGUI.indentLevel++;
|
|
176
|
+
RenderAnimationEventItem(item, frame, frameRate);
|
|
177
|
+
|
|
178
|
+
if (i != stateCopy.Count - 1)
|
|
179
|
+
{
|
|
180
|
+
DrawGuiLine(height: 3, color: new Color(0f, 1f, 0.3f, 1f));
|
|
181
|
+
EditorGUILayout.Space();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
EditorGUI.indentLevel--;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
EditorGUILayout.EndScrollView();
|
|
188
|
+
|
|
189
|
+
DrawControlButtons();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private AnimationClip DrawAndFilterAnimationClips()
|
|
193
|
+
{
|
|
194
|
+
_animationSearchString = EditorGUILayout.TextField(
|
|
195
|
+
"Animation Search",
|
|
196
|
+
_animationSearchString
|
|
197
|
+
);
|
|
198
|
+
List<AnimationClip> animationClips =
|
|
199
|
+
_sourceAnimator.runtimeAnimatorController.animationClips.ToList();
|
|
200
|
+
int selectedIndex;
|
|
201
|
+
if (string.IsNullOrEmpty(_animationSearchString) || _animationSearchString == "*")
|
|
202
|
+
{
|
|
203
|
+
selectedIndex = EditorGUILayout.Popup(
|
|
204
|
+
"Animation",
|
|
205
|
+
animationClips.IndexOf(_currentClip),
|
|
206
|
+
animationClips.Select(clip => clip.name).ToArray()
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{
|
|
211
|
+
List<string> searchTerms = _animationSearchString
|
|
212
|
+
.Split(" ")
|
|
213
|
+
.Select(searchPart => searchPart.ToLowerInvariant().Trim())
|
|
214
|
+
.Where(trimmed => !string.IsNullOrEmpty(trimmed) && trimmed != "*")
|
|
215
|
+
.ToList();
|
|
216
|
+
|
|
217
|
+
if (0 < searchTerms.Count)
|
|
218
|
+
{
|
|
219
|
+
foreach (AnimationClip animationClip in animationClips.ToList())
|
|
220
|
+
{
|
|
221
|
+
if (_currentClip == animationClip)
|
|
222
|
+
{
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
foreach (string searchTerm in searchTerms)
|
|
227
|
+
{
|
|
228
|
+
if (animationClip.name.ToLowerInvariant().Contains(searchTerm))
|
|
229
|
+
{
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
animationClips.Remove(animationClip);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
selectedIndex = EditorGUILayout.Popup(
|
|
239
|
+
"Animation",
|
|
240
|
+
animationClips.IndexOf(_currentClip),
|
|
241
|
+
animationClips.Select(clip => clip.name).ToArray()
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (selectedIndex < 0)
|
|
246
|
+
{
|
|
247
|
+
_currentClip = null;
|
|
248
|
+
RefreshAnimationEvents();
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return animationClips[selectedIndex];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private int AnimationEventComparison(AnimationEventItem lhs, AnimationEventItem rhs)
|
|
256
|
+
{
|
|
257
|
+
if (ReferenceEquals(lhs, rhs))
|
|
258
|
+
{
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (ReferenceEquals(null, rhs))
|
|
263
|
+
{
|
|
264
|
+
return -1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (ReferenceEquals(null, lhs))
|
|
268
|
+
{
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return AnimationEventEqualityComparer.Instance.Compare(
|
|
273
|
+
lhs.animationEvent,
|
|
274
|
+
rhs.animationEvent
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private void DrawControlButtons()
|
|
279
|
+
{
|
|
280
|
+
if (
|
|
281
|
+
_baseClipEvents.SequenceEqual(
|
|
282
|
+
_state.Select(item => item.animationEvent),
|
|
283
|
+
AnimationEventEqualityComparer.Instance
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
{
|
|
287
|
+
GUILayout.Label("No changes detected...");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
Color oldColor = GUI.color;
|
|
292
|
+
GUI.color = Color.green;
|
|
293
|
+
if (GUILayout.Button("Save"))
|
|
294
|
+
{
|
|
295
|
+
SaveAnimation();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
GUI.color = oldColor;
|
|
299
|
+
if (GUILayout.Button("Reset"))
|
|
300
|
+
{
|
|
301
|
+
RefreshAnimationEvents();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (
|
|
305
|
+
!_state.SequenceEqual(
|
|
306
|
+
_state.OrderBy(
|
|
307
|
+
item => item.animationEvent,
|
|
308
|
+
AnimationEventEqualityComparer.Instance
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
{
|
|
313
|
+
if (GUILayout.Button("Re-Order"))
|
|
314
|
+
{
|
|
315
|
+
_state.Sort(AnimationEventComparison);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private void RenderAnimationEventItem(AnimationEventItem item, int frame, float frameRate)
|
|
321
|
+
{
|
|
322
|
+
int index = _state.IndexOf(item);
|
|
323
|
+
EditorGUILayout.BeginHorizontal();
|
|
324
|
+
try
|
|
325
|
+
{
|
|
326
|
+
if (
|
|
327
|
+
1 <= index
|
|
328
|
+
&& Math.Abs(_state[index - 1].animationEvent.time - item.animationEvent.time)
|
|
329
|
+
< 0.001f
|
|
330
|
+
&& GUILayout.Button("Move Up")
|
|
331
|
+
)
|
|
332
|
+
{
|
|
333
|
+
_state.RemoveAt(index);
|
|
334
|
+
_state.Insert(index - 1, item);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (
|
|
338
|
+
index < _state.Count - 1
|
|
339
|
+
&& Math.Abs(_state[index + 1].animationEvent.time - item.animationEvent.time)
|
|
340
|
+
< 0.001f
|
|
341
|
+
&& GUILayout.Button("Move Down")
|
|
342
|
+
)
|
|
343
|
+
{
|
|
344
|
+
_state.RemoveAt(index);
|
|
345
|
+
_state.Insert(index + 1, item);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (
|
|
349
|
+
0 <= index
|
|
350
|
+
&& index < _baseClipEvents.Count
|
|
351
|
+
&& !AnimationEventEqualityComparer.Instance.Equals(
|
|
352
|
+
item.animationEvent,
|
|
353
|
+
_baseClipEvents[index]
|
|
354
|
+
)
|
|
355
|
+
&& GUILayout.Button("Reset")
|
|
356
|
+
)
|
|
357
|
+
{
|
|
358
|
+
AnimationEventEqualityComparer.Instance.CopyInto(
|
|
359
|
+
item.animationEvent,
|
|
360
|
+
_baseClipEvents[index]
|
|
361
|
+
);
|
|
362
|
+
item.selectedType = null;
|
|
363
|
+
item.selectedMethod = null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (GUILayout.Button($"Remove Event at frame {frame}"))
|
|
367
|
+
{
|
|
368
|
+
_state.Remove(item);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
finally
|
|
373
|
+
{
|
|
374
|
+
EditorGUILayout.EndHorizontal();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup = FilterLookup(item);
|
|
378
|
+
|
|
379
|
+
TryPopulateTypeAndMethod(item, lookup);
|
|
380
|
+
|
|
381
|
+
List<Type> orderedTypes = lookup.Keys.OrderBy(type => type.FullName).Take(20).ToList();
|
|
382
|
+
if (item.selectedType != null && !orderedTypes.Contains(item.selectedType))
|
|
383
|
+
{
|
|
384
|
+
orderedTypes.Add(item.selectedType);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
string[] orderedTypeNames = orderedTypes.Select(type => type.FullName).ToArray();
|
|
388
|
+
|
|
389
|
+
SelectFrameTime(item, frame, frameRate);
|
|
390
|
+
|
|
391
|
+
SelectFunctionName(item);
|
|
392
|
+
|
|
393
|
+
if (!SelectTypes(item, orderedTypes, orderedTypeNames))
|
|
394
|
+
{
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (!SelectMethods(item, lookup))
|
|
399
|
+
{
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
RenderEventParameters(item);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private void SelectFrameTime(AnimationEventItem item, int frame, float frameRate)
|
|
407
|
+
{
|
|
408
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
409
|
+
float previousTime = animEvent.time;
|
|
410
|
+
if (_controlFrameTime)
|
|
411
|
+
{
|
|
412
|
+
float proposedFrameTime = EditorGUILayout.FloatField("FrameTime", animEvent.time);
|
|
413
|
+
animEvent.time = Mathf.Clamp(proposedFrameTime, 0, _currentClip.length);
|
|
414
|
+
}
|
|
415
|
+
else
|
|
416
|
+
{
|
|
417
|
+
int proposedFrame = EditorGUILayout.IntField("FrameIndex", frame);
|
|
418
|
+
animEvent.time = Mathf.Clamp(proposedFrame, 0, MaxFrameIndex) / frameRate;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
422
|
+
if (previousTime != animEvent.time)
|
|
423
|
+
{
|
|
424
|
+
item.texture = null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private void SelectFunctionName(AnimationEventItem item)
|
|
429
|
+
{
|
|
430
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
431
|
+
animEvent.functionName = EditorGUILayout.TextField(
|
|
432
|
+
"FunctionName",
|
|
433
|
+
animEvent.functionName ?? string.Empty
|
|
434
|
+
);
|
|
435
|
+
if (!_explicitMode)
|
|
436
|
+
{
|
|
437
|
+
item.search = EditorGUILayout.TextField("Search", item.search);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private void TryPopulateTypeAndMethod(
|
|
442
|
+
AnimationEventItem item,
|
|
443
|
+
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup
|
|
444
|
+
)
|
|
445
|
+
{
|
|
446
|
+
if (item.selectedType != null)
|
|
447
|
+
{
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
452
|
+
foreach (
|
|
453
|
+
KeyValuePair<Type, IReadOnlyList<MethodInfo>> entry in lookup.OrderBy(kvp =>
|
|
454
|
+
kvp.Key.FullName
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
{
|
|
458
|
+
foreach (MethodInfo method in entry.Value)
|
|
459
|
+
{
|
|
460
|
+
if (
|
|
461
|
+
string.Equals(method.Name, animEvent.functionName, StringComparison.Ordinal)
|
|
462
|
+
)
|
|
463
|
+
{
|
|
464
|
+
item.selectedType = entry.Key;
|
|
465
|
+
item.selectedMethod = method;
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private bool SelectTypes(
|
|
473
|
+
AnimationEventItem item,
|
|
474
|
+
IList<Type> orderedTypes,
|
|
475
|
+
string[] orderedTypeNames
|
|
476
|
+
)
|
|
477
|
+
{
|
|
478
|
+
int existingIndex = orderedTypes.IndexOf(item.selectedType);
|
|
479
|
+
int selectedTypeIndex = EditorGUILayout.Popup(
|
|
480
|
+
"TypeName",
|
|
481
|
+
existingIndex,
|
|
482
|
+
orderedTypeNames
|
|
483
|
+
);
|
|
484
|
+
item.selectedType = selectedTypeIndex < 0 ? null : orderedTypes[selectedTypeIndex];
|
|
485
|
+
if (existingIndex != selectedTypeIndex)
|
|
486
|
+
{
|
|
487
|
+
item.selectedMethod = null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return item.selectedType != null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private bool SelectMethods(
|
|
494
|
+
AnimationEventItem item,
|
|
495
|
+
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup
|
|
496
|
+
)
|
|
497
|
+
{
|
|
498
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
499
|
+
if (!lookup.TryGetValue(item.selectedType, out IReadOnlyList<MethodInfo> methods))
|
|
500
|
+
{
|
|
501
|
+
methods = new List<MethodInfo>(0);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (item.selectedMethod == null || !methods.Contains(item.selectedMethod))
|
|
505
|
+
{
|
|
506
|
+
foreach (MethodInfo method in methods)
|
|
507
|
+
{
|
|
508
|
+
if (
|
|
509
|
+
string.Equals(method.Name, animEvent.functionName, StringComparison.Ordinal)
|
|
510
|
+
)
|
|
511
|
+
{
|
|
512
|
+
item.selectedMethod = method;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (item.selectedMethod != null && !methods.Contains(item.selectedMethod))
|
|
518
|
+
{
|
|
519
|
+
methods = methods.Concat(Enumerables.Of(item.selectedMethod)).ToList();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
int selectedMethodIndex = EditorGUILayout.Popup(
|
|
524
|
+
"MethodName",
|
|
525
|
+
methods.ToList().IndexOf(item.selectedMethod),
|
|
526
|
+
methods.Select(method => method.Name).ToArray()
|
|
527
|
+
);
|
|
528
|
+
if (0 <= selectedMethodIndex)
|
|
529
|
+
{
|
|
530
|
+
item.selectedMethod = methods[selectedMethodIndex];
|
|
531
|
+
animEvent.functionName = item.selectedMethod.Name;
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private void RenderEventParameters(AnimationEventItem item)
|
|
539
|
+
{
|
|
540
|
+
AnimationEvent animEvent = item.animationEvent;
|
|
541
|
+
ParameterInfo[] arrayParameterInfo = item.selectedMethod.GetParameters();
|
|
542
|
+
if (arrayParameterInfo.Length == 1)
|
|
543
|
+
{
|
|
544
|
+
EditorGUI.indentLevel++;
|
|
545
|
+
|
|
546
|
+
Type paramType = arrayParameterInfo[0].ParameterType;
|
|
547
|
+
if (paramType == typeof(int))
|
|
548
|
+
{
|
|
549
|
+
animEvent.intParameter = EditorGUILayout.IntField(
|
|
550
|
+
"IntParameter",
|
|
551
|
+
animEvent.intParameter
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
else if (paramType.BaseType == typeof(Enum))
|
|
555
|
+
{
|
|
556
|
+
string[] enumNamesArray = Enum.GetNames(paramType);
|
|
557
|
+
List<string> enumNames = enumNamesArray.ToList();
|
|
558
|
+
string enumName = Enum.GetName(paramType, animEvent.intParameter);
|
|
559
|
+
|
|
560
|
+
int index = EditorGUILayout.Popup(
|
|
561
|
+
$"{paramType.Name}",
|
|
562
|
+
enumNames.IndexOf(enumName),
|
|
563
|
+
enumNamesArray
|
|
564
|
+
);
|
|
565
|
+
if (0 <= index)
|
|
566
|
+
{
|
|
567
|
+
animEvent.intParameter = (int)Enum.Parse(paramType, enumNames[index]);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
item.overrideEnumValues = EditorGUILayout.Toggle(
|
|
571
|
+
"Override",
|
|
572
|
+
item.overrideEnumValues
|
|
573
|
+
);
|
|
574
|
+
if (item.overrideEnumValues)
|
|
575
|
+
{
|
|
576
|
+
animEvent.intParameter = EditorGUILayout.IntField(
|
|
577
|
+
"IntParameter",
|
|
578
|
+
animEvent.intParameter
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else if (paramType == typeof(float))
|
|
583
|
+
{
|
|
584
|
+
animEvent.floatParameter = EditorGUILayout.FloatField(
|
|
585
|
+
"FloatParameter",
|
|
586
|
+
animEvent.floatParameter
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
else if (paramType == typeof(string))
|
|
590
|
+
{
|
|
591
|
+
animEvent.stringParameter = EditorGUILayout.TextField(
|
|
592
|
+
"StringParameter",
|
|
593
|
+
animEvent.stringParameter
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
else if (paramType == typeof(UnityEngine.Object))
|
|
597
|
+
{
|
|
598
|
+
animEvent.objectReferenceParameter = EditorGUILayout.ObjectField(
|
|
599
|
+
"ObjectReferenceParameter",
|
|
600
|
+
animEvent.objectReferenceParameter,
|
|
601
|
+
typeof(UnityEngine.Object),
|
|
602
|
+
true
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
EditorGUI.indentLevel--;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> FilterLookup(
|
|
611
|
+
AnimationEventItem item
|
|
612
|
+
)
|
|
613
|
+
{
|
|
614
|
+
IReadOnlyDictionary<Type, IReadOnlyList<MethodInfo>> lookup;
|
|
615
|
+
if (!_explicitMode)
|
|
616
|
+
{
|
|
617
|
+
if (
|
|
618
|
+
!_lastSeenSearch.TryGetValue(item, out string lastSearch)
|
|
619
|
+
|| !string.Equals(
|
|
620
|
+
lastSearch,
|
|
621
|
+
item.search,
|
|
622
|
+
StringComparison.InvariantCultureIgnoreCase
|
|
623
|
+
)
|
|
624
|
+
|| !_lookups.TryGetValue(item, out lookup)
|
|
625
|
+
)
|
|
626
|
+
{
|
|
627
|
+
Dictionary<Type, List<MethodInfo>> filtered = Lookup.ToDictionary(
|
|
628
|
+
kvp => kvp.Key,
|
|
629
|
+
kvp => kvp.Value.ToList()
|
|
630
|
+
);
|
|
631
|
+
List<string> searchTerms = item
|
|
632
|
+
.search.Split(" ")
|
|
633
|
+
.Select(searchTerm => searchTerm.Trim().ToLowerInvariant())
|
|
634
|
+
.Where(trimmed => !string.IsNullOrEmpty(trimmed) && trimmed != "*")
|
|
635
|
+
.ToList();
|
|
636
|
+
|
|
637
|
+
if (0 < searchTerms.Count)
|
|
638
|
+
{
|
|
639
|
+
foreach (KeyValuePair<Type, List<MethodInfo>> entry in filtered.ToList())
|
|
640
|
+
{
|
|
641
|
+
foreach (string searchTerm in searchTerms)
|
|
642
|
+
{
|
|
643
|
+
if (entry.Key.FullName.ToLowerInvariant().Contains(searchTerm))
|
|
644
|
+
{
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (
|
|
649
|
+
entry.Value.Any(methodInfo =>
|
|
650
|
+
methodInfo.Name.ToLowerInvariant().Contains(searchTerm)
|
|
651
|
+
)
|
|
652
|
+
)
|
|
653
|
+
{
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
_ = filtered.Remove(entry.Key);
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
_lookups[item] = lookup = filtered.ToDictionary(
|
|
664
|
+
kvp => kvp.Key,
|
|
665
|
+
kvp => (IReadOnlyList<MethodInfo>)kvp.Value
|
|
666
|
+
);
|
|
667
|
+
_lastSeenSearch[item] = item.search;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else
|
|
671
|
+
{
|
|
672
|
+
lookup = Lookup;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return lookup;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private void DrawSpritePreview(AnimationEventItem item)
|
|
679
|
+
{
|
|
680
|
+
SetupPreviewData(item);
|
|
681
|
+
|
|
682
|
+
string spriteName = item.sprite == null ? string.Empty : item.sprite.name;
|
|
683
|
+
if (item.texture != null)
|
|
684
|
+
{
|
|
685
|
+
GUILayout.Label(item.texture);
|
|
686
|
+
}
|
|
687
|
+
else if (!item.isTextureReadable && !string.IsNullOrEmpty(spriteName))
|
|
688
|
+
{
|
|
689
|
+
EditorGUILayout.BeginHorizontal();
|
|
690
|
+
try
|
|
691
|
+
{
|
|
692
|
+
GUILayout.Label($"Sprite '{spriteName}' required \"Read/Write\" enabled");
|
|
693
|
+
if (item.sprite != null && GUILayout.Button("Fix"))
|
|
694
|
+
{
|
|
695
|
+
string assetPath = AssetDatabase.GetAssetPath(item.sprite.texture);
|
|
696
|
+
if (string.IsNullOrEmpty(assetPath))
|
|
697
|
+
{
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
TextureImporter tImporter =
|
|
702
|
+
AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
703
|
+
if (tImporter == null)
|
|
704
|
+
{
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
tImporter.isReadable = true;
|
|
709
|
+
EditorUtility.SetDirty(tImporter);
|
|
710
|
+
tImporter.SaveAndReimport();
|
|
711
|
+
EditorUtility.SetDirty(item.sprite);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
finally
|
|
715
|
+
{
|
|
716
|
+
EditorGUILayout.EndHorizontal();
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else if (item.isInvalidTextureRect && !string.IsNullOrEmpty(spriteName))
|
|
720
|
+
{
|
|
721
|
+
GUILayout.Label($"Sprite '{spriteName}' is packed too tightly inside its texture");
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
private void SetupPreviewData(AnimationEventItem item)
|
|
726
|
+
{
|
|
727
|
+
if (item.texture != null)
|
|
728
|
+
{
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (TryFindSpriteForEvent(item, out Sprite currentSprite))
|
|
733
|
+
{
|
|
734
|
+
item.sprite = currentSprite;
|
|
735
|
+
item.isTextureReadable = currentSprite.texture.isReadable;
|
|
736
|
+
item.isInvalidTextureRect = false;
|
|
737
|
+
if (item.isTextureReadable)
|
|
738
|
+
{
|
|
739
|
+
Rect? maybeTextureRect = null;
|
|
740
|
+
try
|
|
741
|
+
{
|
|
742
|
+
maybeTextureRect = currentSprite.textureRect;
|
|
743
|
+
}
|
|
744
|
+
catch
|
|
745
|
+
{
|
|
746
|
+
item.isInvalidTextureRect = true;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (maybeTextureRect != null)
|
|
750
|
+
{
|
|
751
|
+
Rect textureRect = maybeTextureRect.Value;
|
|
752
|
+
item.texture = CopyTexture(textureRect, currentSprite.texture);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else
|
|
757
|
+
{
|
|
758
|
+
item.sprite = null;
|
|
759
|
+
item.isTextureReadable = false;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
private bool TryFindSpriteForEvent(AnimationEventItem item, out Sprite sprite)
|
|
764
|
+
{
|
|
765
|
+
sprite = null;
|
|
766
|
+
foreach (
|
|
767
|
+
ObjectReferenceKeyframe keyFrame in _referenceCurve
|
|
768
|
+
?? Enumerable.Empty<ObjectReferenceKeyframe>()
|
|
769
|
+
)
|
|
770
|
+
{
|
|
771
|
+
if (keyFrame.time <= item.animationEvent.time)
|
|
772
|
+
{
|
|
773
|
+
Sprite frameSprite = keyFrame.value as Sprite;
|
|
774
|
+
if (frameSprite == null)
|
|
775
|
+
{
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
sprite = frameSprite;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return sprite != null;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return sprite != null;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
private Texture2D CopyTexture(Rect textureRect, Texture2D sourceTexture)
|
|
790
|
+
{
|
|
791
|
+
int width = (int)Math.Ceiling(textureRect.width);
|
|
792
|
+
int height = (int)Math.Ceiling(textureRect.height);
|
|
793
|
+
Texture2D texture = new(width, height)
|
|
794
|
+
{
|
|
795
|
+
wrapMode = TextureWrapMode.Clamp,
|
|
796
|
+
filterMode = FilterMode.Point,
|
|
797
|
+
};
|
|
798
|
+
Vector2 offset = textureRect.position;
|
|
799
|
+
int offsetX = (int)Math.Ceiling(offset.x);
|
|
800
|
+
int offsetY = (int)Math.Ceiling(offset.y);
|
|
801
|
+
for (int x = 0; x < width; ++x)
|
|
802
|
+
{
|
|
803
|
+
for (int y = 0; y < height; ++y)
|
|
804
|
+
{
|
|
805
|
+
Color sourcePixel = sourceTexture.GetPixel(offsetX + x, offsetY + y);
|
|
806
|
+
texture.SetPixel(x, y, sourcePixel);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
texture.Apply();
|
|
811
|
+
return texture;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
private void RefreshAnimationEvents()
|
|
815
|
+
{
|
|
816
|
+
_state.Clear();
|
|
817
|
+
_baseClipEvents.Clear();
|
|
818
|
+
_lookups.Clear();
|
|
819
|
+
_lastSeenSearch.Clear();
|
|
820
|
+
if (_currentClip == null)
|
|
821
|
+
{
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
for (int i = 0; i < _currentClip.events.Length; i++)
|
|
826
|
+
{
|
|
827
|
+
AnimationEvent animEvent = _currentClip.events[i];
|
|
828
|
+
_state.Add(new AnimationEventItem(animEvent) { originalIndex = i });
|
|
829
|
+
_baseClipEvents.Add(AnimationEventEqualityComparer.Instance.Copy(animEvent));
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
_selectedFrameIndex = MaxFrameIndex;
|
|
833
|
+
_referenceCurve = AnimationUtility
|
|
834
|
+
.GetObjectReferenceCurve(
|
|
835
|
+
_currentClip,
|
|
836
|
+
EditorCurveBinding.PPtrCurve("", typeof(SpriteRenderer), "m_Sprite")
|
|
837
|
+
)
|
|
838
|
+
.ToList();
|
|
839
|
+
_referenceCurve.Sort(
|
|
840
|
+
(lhs, rhs) =>
|
|
841
|
+
{
|
|
842
|
+
int comparison = lhs.time.CompareTo(rhs.time);
|
|
843
|
+
if (comparison != 0)
|
|
844
|
+
{
|
|
845
|
+
return comparison;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
string lhsName =
|
|
849
|
+
lhs.value == null ? string.Empty : lhs.value.name ?? string.Empty;
|
|
850
|
+
string rhsName =
|
|
851
|
+
rhs.value == null ? string.Empty : rhs.value.name ?? string.Empty;
|
|
852
|
+
return string.Compare(lhsName, rhsName, StringComparison.OrdinalIgnoreCase);
|
|
853
|
+
}
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
private void SaveAnimation()
|
|
858
|
+
{
|
|
859
|
+
if (_currentClip != null)
|
|
860
|
+
{
|
|
861
|
+
AnimationUtility.SetAnimationEvents(
|
|
862
|
+
_currentClip,
|
|
863
|
+
_state.Select(item => item.animationEvent).ToArray()
|
|
864
|
+
);
|
|
865
|
+
EditorUtility.SetDirty(_currentClip);
|
|
866
|
+
AssetDatabase.SaveAssetIfDirty(_currentClip);
|
|
867
|
+
_baseClipEvents.Clear();
|
|
868
|
+
foreach (AnimationEventItem item in _state)
|
|
869
|
+
{
|
|
870
|
+
_baseClipEvents.Add(
|
|
871
|
+
AnimationEventEqualityComparer.Instance.Copy(item.animationEvent)
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
private void DrawGuiLine(int height = 1, Color? color = null)
|
|
878
|
+
{
|
|
879
|
+
Rect rect = EditorGUILayout.GetControlRect(false, height);
|
|
880
|
+
rect.height = height;
|
|
881
|
+
int minusWidth = EditorGUI.indentLevel * 16;
|
|
882
|
+
rect.xMin += minusWidth;
|
|
883
|
+
EditorGUI.DrawRect(rect, color ?? new Color(0.5f, 0.5f, 0.5f, 1f));
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
#endif
|
|
887
|
+
}
|