com.wallstop-studios.unity-helpers 2.0.0-rc76.5 → 2.0.0-rc76.7
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/Editor/AssetProcessors/SpriteLabelProcessor.cs +133 -0
- package/Editor/AssetProcessors/SpriteLabelProcessor.cs.meta +3 -0
- package/Editor/AssetProcessors.meta +3 -0
- package/Editor/CustomDrawers/StringInListeDrawer.cs +104 -25
- package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +12 -4
- package/Editor/CustomEditors/PersistentDirectoryGUI.cs +4 -1
- package/Editor/CustomEditors/SourceFolderEntryDrawer.cs +189 -52
- package/Editor/Extensions/SerializedPropertyExtensions.cs +11 -0
- package/Editor/Sprites/ScriptableSpriteAtlas.cs +71 -1
- package/Editor/Sprites/ScriptableSpriteAtlasEditor.cs +94 -34
- package/Editor/Sprites/SpriteSheetAnimationCreator.cs +1530 -0
- package/Editor/Sprites/SpriteSheetAnimationCreator.cs.meta +3 -0
- package/Runtime/Core/Attributes/WShowIfAttribute.cs +9 -1
- package/Runtime/Core/Extension/IListExtensions.cs +6 -0
- package/Runtime/Core/Helper/Helpers.cs +31 -3
- package/Runtime/Core/Helper/StringInList.cs +22 -3
- package/Runtime/Utils/UnityObjectNameComparer.cs +46 -1
- package/package.json +3 -1
|
@@ -0,0 +1,1530 @@
|
|
|
1
|
+
// ReSharper disable HeapView.CanAvoidClosure
|
|
2
|
+
namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
3
|
+
{
|
|
4
|
+
#if UNITY_EDITOR
|
|
5
|
+
using System;
|
|
6
|
+
using System.Collections.Generic;
|
|
7
|
+
using System.Diagnostics;
|
|
8
|
+
using System.IO;
|
|
9
|
+
using System.Linq;
|
|
10
|
+
using System.Runtime.Serialization;
|
|
11
|
+
using Core.Extension;
|
|
12
|
+
using Core.Helper;
|
|
13
|
+
using UnityEditor;
|
|
14
|
+
using UnityEditor.UIElements;
|
|
15
|
+
using UnityEngine;
|
|
16
|
+
using UnityEngine.UIElements;
|
|
17
|
+
using Object = UnityEngine.Object;
|
|
18
|
+
|
|
19
|
+
public sealed class SpriteSheetAnimationCreator : EditorWindow
|
|
20
|
+
{
|
|
21
|
+
private const float ThumbnailSize = 64f;
|
|
22
|
+
|
|
23
|
+
private Texture2D _selectedSpriteSheet;
|
|
24
|
+
private readonly List<Sprite> _availableSprites = new();
|
|
25
|
+
|
|
26
|
+
private readonly List<AnimationDefinition> _animationDefinitions = new();
|
|
27
|
+
|
|
28
|
+
private ObjectField _spriteSheetField;
|
|
29
|
+
private Button _refreshSpritesButton;
|
|
30
|
+
private Button _loadSpritesButton;
|
|
31
|
+
private ScrollView _spriteThumbnailsScrollView;
|
|
32
|
+
private VisualElement _spriteThumbnailsContainer;
|
|
33
|
+
private ListView _animationDefinitionsListView;
|
|
34
|
+
private Button _addAnimationDefinitionButton;
|
|
35
|
+
private Button _generateAnimationsButton;
|
|
36
|
+
|
|
37
|
+
private VisualElement _previewContainer;
|
|
38
|
+
private Image _previewImage;
|
|
39
|
+
private Label _previewFrameLabel;
|
|
40
|
+
private Button _playPreviewButton;
|
|
41
|
+
private Button _stopPreviewButton;
|
|
42
|
+
private Button _prevFrameButton;
|
|
43
|
+
private Button _nextFrameButton;
|
|
44
|
+
private Slider _previewScrubber;
|
|
45
|
+
|
|
46
|
+
private bool _isDraggingToSelectSprites;
|
|
47
|
+
private int _spriteSelectionDragStartIndex = -1;
|
|
48
|
+
private int _spriteSelectionDragCurrentIndex = -1;
|
|
49
|
+
private StyleColor _selectedThumbnailBackgroundColor = new(
|
|
50
|
+
new Color(0.2f, 0.5f, 0.8f, 0.4f)
|
|
51
|
+
);
|
|
52
|
+
private readonly StyleColor _defaultThumbnailBackgroundColor = new(StyleKeyword.Null);
|
|
53
|
+
|
|
54
|
+
private bool _isPreviewing;
|
|
55
|
+
private int _currentPreviewAnimDefIndex = -1;
|
|
56
|
+
private int _currentPreviewSpriteIndex;
|
|
57
|
+
private AnimationDefinition _currentPreviewDefinition;
|
|
58
|
+
private readonly EditorApplication.CallbackFunction _editorUpdateCallback;
|
|
59
|
+
private readonly Stopwatch _timer = Stopwatch.StartNew();
|
|
60
|
+
|
|
61
|
+
private TimeSpan? _lastTick;
|
|
62
|
+
|
|
63
|
+
[MenuItem("Tools/Wallstop Studios/Unity Helpers/Sprite Sheet Animation Creator")]
|
|
64
|
+
public static void ShowWindow()
|
|
65
|
+
{
|
|
66
|
+
SpriteSheetAnimationCreator window = GetWindow<SpriteSheetAnimationCreator>();
|
|
67
|
+
window.titleContent = new GUIContent("Sprite Animation Creator");
|
|
68
|
+
window.minSize = new Vector2(600, 700);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[Serializable]
|
|
72
|
+
[DataContract]
|
|
73
|
+
public sealed class AnimationDefinition
|
|
74
|
+
{
|
|
75
|
+
public string Name = "New Animation";
|
|
76
|
+
public bool loop;
|
|
77
|
+
public float cycleOffset;
|
|
78
|
+
public int StartSpriteIndex;
|
|
79
|
+
public int EndSpriteIndex;
|
|
80
|
+
public float DefaultFrameRate = 12f;
|
|
81
|
+
public AnimationCurve FrameRateCurve = AnimationCurve.Constant(0, 1, 12f);
|
|
82
|
+
public List<Sprite> SpritesToAnimate = new();
|
|
83
|
+
|
|
84
|
+
public TextField nameField;
|
|
85
|
+
public IntegerField startIndexField;
|
|
86
|
+
public IntegerField endIndexField;
|
|
87
|
+
public FloatField defaultFrameRateField;
|
|
88
|
+
public CurveField frameRateCurveField;
|
|
89
|
+
public Label spriteCountLabel;
|
|
90
|
+
public Button previewButton;
|
|
91
|
+
public Button removeButton;
|
|
92
|
+
public Toggle loopingField;
|
|
93
|
+
public FloatField cycleOffsetField;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public SpriteSheetAnimationCreator()
|
|
97
|
+
{
|
|
98
|
+
_editorUpdateCallback = OnEditorUpdate;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public void CreateGUI()
|
|
102
|
+
{
|
|
103
|
+
VisualElement root = rootVisualElement;
|
|
104
|
+
root.style.paddingLeft = 10;
|
|
105
|
+
root.style.paddingRight = 10;
|
|
106
|
+
root.style.paddingTop = 10;
|
|
107
|
+
root.style.paddingBottom = 10;
|
|
108
|
+
|
|
109
|
+
VisualElement topSection = new()
|
|
110
|
+
{
|
|
111
|
+
style = { flexDirection = FlexDirection.Row, marginBottom = 10 },
|
|
112
|
+
};
|
|
113
|
+
_spriteSheetField = new ObjectField("Sprite Sheet")
|
|
114
|
+
{
|
|
115
|
+
objectType = typeof(Texture2D),
|
|
116
|
+
allowSceneObjects = false,
|
|
117
|
+
style =
|
|
118
|
+
{
|
|
119
|
+
flexGrow = 1,
|
|
120
|
+
flexShrink = 0,
|
|
121
|
+
minHeight = 20,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
_spriteSheetField.RegisterValueChangedCallback(OnSpriteSheetSelected);
|
|
125
|
+
topSection.Add(_spriteSheetField);
|
|
126
|
+
|
|
127
|
+
_loadSpritesButton = new Button(() =>
|
|
128
|
+
{
|
|
129
|
+
string filePath = string.Empty;
|
|
130
|
+
if (_spriteSheetField.value != null)
|
|
131
|
+
{
|
|
132
|
+
filePath = AssetDatabase.GetAssetPath(_spriteSheetField.value);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (string.IsNullOrWhiteSpace(filePath))
|
|
136
|
+
{
|
|
137
|
+
filePath = Application.dataPath;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
string selectedPath = EditorUtility.OpenFilePanel(
|
|
141
|
+
"Select Sprite Sheet",
|
|
142
|
+
filePath,
|
|
143
|
+
"png,jpg,gif,bmp,psd"
|
|
144
|
+
);
|
|
145
|
+
if (string.IsNullOrWhiteSpace(selectedPath))
|
|
146
|
+
{
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
string relativePath = DirectoryHelper.AbsoluteToUnityRelativePath(selectedPath);
|
|
151
|
+
if (!string.IsNullOrWhiteSpace(relativePath))
|
|
152
|
+
{
|
|
153
|
+
Texture2D loadedTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(
|
|
154
|
+
relativePath
|
|
155
|
+
);
|
|
156
|
+
if (loadedTexture != null)
|
|
157
|
+
{
|
|
158
|
+
_spriteSheetField.value = loadedTexture;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
{
|
|
163
|
+
text = "Load Sprites",
|
|
164
|
+
style = { marginLeft = 5, minHeight = 20 },
|
|
165
|
+
};
|
|
166
|
+
topSection.Add(_loadSpritesButton);
|
|
167
|
+
|
|
168
|
+
_refreshSpritesButton = new Button(LoadAndDisplaySprites)
|
|
169
|
+
{
|
|
170
|
+
text = "Refresh Sprites",
|
|
171
|
+
style = { marginLeft = 5, minHeight = 20 },
|
|
172
|
+
};
|
|
173
|
+
topSection.Add(_refreshSpritesButton);
|
|
174
|
+
root.Add(topSection);
|
|
175
|
+
|
|
176
|
+
Label thumbnailsLabel = new(
|
|
177
|
+
"Available Sprites (Drag to select range for new animation):"
|
|
178
|
+
)
|
|
179
|
+
{
|
|
180
|
+
style =
|
|
181
|
+
{
|
|
182
|
+
unityFontStyleAndWeight = FontStyle.Bold,
|
|
183
|
+
marginTop = 5,
|
|
184
|
+
marginBottom = 5,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
root.Add(thumbnailsLabel);
|
|
188
|
+
_spriteThumbnailsScrollView = new ScrollView(ScrollViewMode.Horizontal)
|
|
189
|
+
{
|
|
190
|
+
style =
|
|
191
|
+
{
|
|
192
|
+
height = ThumbnailSize + 20 + 10,
|
|
193
|
+
minHeight = ThumbnailSize + 20 + 10,
|
|
194
|
+
borderTopWidth = 1,
|
|
195
|
+
borderBottomWidth = 1,
|
|
196
|
+
borderLeftWidth = 1,
|
|
197
|
+
borderRightWidth = 1,
|
|
198
|
+
borderBottomColor = Color.gray,
|
|
199
|
+
borderTopColor = Color.gray,
|
|
200
|
+
borderLeftColor = Color.gray,
|
|
201
|
+
borderRightColor = Color.gray,
|
|
202
|
+
paddingLeft = 5,
|
|
203
|
+
paddingRight = 5,
|
|
204
|
+
paddingTop = 5,
|
|
205
|
+
paddingBottom = 5,
|
|
206
|
+
marginBottom = 10,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
_spriteThumbnailsContainer = new VisualElement
|
|
210
|
+
{
|
|
211
|
+
style = { flexDirection = FlexDirection.Row },
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
_spriteThumbnailsContainer.RegisterCallback<PointerMoveEvent>(evt =>
|
|
215
|
+
{
|
|
216
|
+
if (
|
|
217
|
+
_isDraggingToSelectSprites
|
|
218
|
+
&& _spriteThumbnailsContainer.HasPointerCapture(evt.pointerId)
|
|
219
|
+
)
|
|
220
|
+
{
|
|
221
|
+
VisualElement currentElementOver = evt.target as VisualElement;
|
|
222
|
+
|
|
223
|
+
VisualElement thumbChild = currentElementOver;
|
|
224
|
+
while (thumbChild != null && thumbChild.parent != _spriteThumbnailsContainer)
|
|
225
|
+
{
|
|
226
|
+
thumbChild = thumbChild.parent;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (
|
|
230
|
+
thumbChild is { userData: int hoveredIndex }
|
|
231
|
+
&& _spriteSelectionDragCurrentIndex != hoveredIndex
|
|
232
|
+
)
|
|
233
|
+
{
|
|
234
|
+
_spriteSelectionDragCurrentIndex = hoveredIndex;
|
|
235
|
+
UpdateSpriteSelectionHighlight();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
_spriteThumbnailsContainer.RegisterCallback<PointerUpEvent>(
|
|
241
|
+
evt =>
|
|
242
|
+
{
|
|
243
|
+
if (
|
|
244
|
+
evt.button == 0
|
|
245
|
+
&& _isDraggingToSelectSprites
|
|
246
|
+
&& _spriteThumbnailsContainer.HasPointerCapture(evt.pointerId)
|
|
247
|
+
)
|
|
248
|
+
{
|
|
249
|
+
_spriteThumbnailsContainer.ReleasePointer(evt.pointerId);
|
|
250
|
+
_isDraggingToSelectSprites = false;
|
|
251
|
+
|
|
252
|
+
if (
|
|
253
|
+
_spriteSelectionDragStartIndex != -1
|
|
254
|
+
&& _spriteSelectionDragCurrentIndex != -1
|
|
255
|
+
)
|
|
256
|
+
{
|
|
257
|
+
int start = Mathf.Min(
|
|
258
|
+
_spriteSelectionDragStartIndex,
|
|
259
|
+
_spriteSelectionDragCurrentIndex
|
|
260
|
+
);
|
|
261
|
+
int end = Mathf.Max(
|
|
262
|
+
_spriteSelectionDragStartIndex,
|
|
263
|
+
_spriteSelectionDragCurrentIndex
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (start <= end)
|
|
267
|
+
{
|
|
268
|
+
CreateAnimationDefinitionFromSelection(start, end);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
ClearSpriteSelectionHighlight();
|
|
272
|
+
_spriteSelectionDragStartIndex = -1;
|
|
273
|
+
_spriteSelectionDragCurrentIndex = -1;
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
TrickleDown.TrickleDown
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
_spriteThumbnailsScrollView.Add(_spriteThumbnailsContainer);
|
|
280
|
+
root.Add(_spriteThumbnailsScrollView);
|
|
281
|
+
|
|
282
|
+
Label animDefsLabel = new("Animation Definitions:")
|
|
283
|
+
{
|
|
284
|
+
style =
|
|
285
|
+
{
|
|
286
|
+
unityFontStyleAndWeight = FontStyle.Bold,
|
|
287
|
+
marginTop = 10,
|
|
288
|
+
marginBottom = 5,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
root.Add(animDefsLabel);
|
|
292
|
+
|
|
293
|
+
_animationDefinitionsListView = new ListView(
|
|
294
|
+
_animationDefinitions,
|
|
295
|
+
130,
|
|
296
|
+
MakeAnimationDefinitionItem,
|
|
297
|
+
BindAnimationDefinitionItem
|
|
298
|
+
)
|
|
299
|
+
{
|
|
300
|
+
selectionType = SelectionType.None,
|
|
301
|
+
style = { flexGrow = 1, minHeight = 200 },
|
|
302
|
+
};
|
|
303
|
+
root.Add(_animationDefinitionsListView);
|
|
304
|
+
|
|
305
|
+
_addAnimationDefinitionButton = new Button(AddAnimationDefinition)
|
|
306
|
+
{
|
|
307
|
+
text = "Add Animation Definition",
|
|
308
|
+
style = { marginTop = 5 },
|
|
309
|
+
};
|
|
310
|
+
root.Add(_addAnimationDefinitionButton);
|
|
311
|
+
|
|
312
|
+
Label previewSectionLabel = new("Animation Preview:")
|
|
313
|
+
{
|
|
314
|
+
style =
|
|
315
|
+
{
|
|
316
|
+
unityFontStyleAndWeight = FontStyle.Bold,
|
|
317
|
+
marginTop = 15,
|
|
318
|
+
marginBottom = 5,
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
root.Add(previewSectionLabel);
|
|
322
|
+
|
|
323
|
+
_previewContainer = new VisualElement
|
|
324
|
+
{
|
|
325
|
+
style =
|
|
326
|
+
{
|
|
327
|
+
flexDirection = FlexDirection.Column,
|
|
328
|
+
alignItems = Align.Center,
|
|
329
|
+
borderTopWidth = 1,
|
|
330
|
+
borderBottomWidth = 1,
|
|
331
|
+
borderLeftWidth = 1,
|
|
332
|
+
borderRightWidth = 1,
|
|
333
|
+
borderBottomColor = Color.gray,
|
|
334
|
+
borderTopColor = Color.gray,
|
|
335
|
+
borderLeftColor = Color.gray,
|
|
336
|
+
borderRightColor = Color.gray,
|
|
337
|
+
paddingBottom = 10,
|
|
338
|
+
paddingTop = 10,
|
|
339
|
+
minHeight = 150,
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
_previewImage = new Image
|
|
343
|
+
{
|
|
344
|
+
scaleMode = ScaleMode.ScaleToFit,
|
|
345
|
+
style =
|
|
346
|
+
{
|
|
347
|
+
width = 128,
|
|
348
|
+
height = 128,
|
|
349
|
+
marginBottom = 10,
|
|
350
|
+
backgroundColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f)),
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
_previewContainer.Add(_previewImage);
|
|
354
|
+
|
|
355
|
+
_previewFrameLabel = new Label("Frame: -/- | FPS: -")
|
|
356
|
+
{
|
|
357
|
+
style = { alignSelf = Align.Center, marginBottom = 5 },
|
|
358
|
+
};
|
|
359
|
+
_previewContainer.Add(_previewFrameLabel);
|
|
360
|
+
|
|
361
|
+
_previewScrubber = new Slider(0, 1)
|
|
362
|
+
{
|
|
363
|
+
style =
|
|
364
|
+
{
|
|
365
|
+
minWidth = 200,
|
|
366
|
+
marginBottom = 5,
|
|
367
|
+
visibility = Visibility.Hidden,
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
_previewScrubber.RegisterValueChangedCallback(evt =>
|
|
371
|
+
{
|
|
372
|
+
if (
|
|
373
|
+
_currentPreviewDefinition != null
|
|
374
|
+
&& 0 < _currentPreviewDefinition.SpritesToAnimate.Count
|
|
375
|
+
)
|
|
376
|
+
{
|
|
377
|
+
int frame = Mathf.FloorToInt(
|
|
378
|
+
evt.newValue * (_currentPreviewDefinition.SpritesToAnimate.Count - 1)
|
|
379
|
+
);
|
|
380
|
+
SetPreviewFrame(frame);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
_previewContainer.Add(_previewScrubber);
|
|
384
|
+
|
|
385
|
+
VisualElement previewControls = new()
|
|
386
|
+
{
|
|
387
|
+
style = { flexDirection = FlexDirection.Row, justifyContent = Justify.Center },
|
|
388
|
+
};
|
|
389
|
+
_prevFrameButton = new Button(() => AdjustPreviewFrame(-1))
|
|
390
|
+
{
|
|
391
|
+
text = "◀",
|
|
392
|
+
style = { minWidth = 40 },
|
|
393
|
+
};
|
|
394
|
+
_playPreviewButton = new Button(PlayCurrentPreview)
|
|
395
|
+
{
|
|
396
|
+
text = "▶ Play",
|
|
397
|
+
style = { minWidth = 70 },
|
|
398
|
+
};
|
|
399
|
+
_stopPreviewButton = new Button(StopCurrentPreview)
|
|
400
|
+
{
|
|
401
|
+
text = "◼ Stop",
|
|
402
|
+
style = { minWidth = 70, display = DisplayStyle.None },
|
|
403
|
+
};
|
|
404
|
+
_nextFrameButton = new Button(() => AdjustPreviewFrame(1))
|
|
405
|
+
{
|
|
406
|
+
text = "▶",
|
|
407
|
+
style = { minWidth = 40 },
|
|
408
|
+
};
|
|
409
|
+
previewControls.Add(_prevFrameButton);
|
|
410
|
+
previewControls.Add(_playPreviewButton);
|
|
411
|
+
previewControls.Add(_stopPreviewButton);
|
|
412
|
+
previewControls.Add(_nextFrameButton);
|
|
413
|
+
_previewContainer.Add(previewControls);
|
|
414
|
+
root.Add(_previewContainer);
|
|
415
|
+
|
|
416
|
+
_generateAnimationsButton = new Button(GenerateAnimations)
|
|
417
|
+
{
|
|
418
|
+
text = "Generate Animation Files",
|
|
419
|
+
style = { marginTop = 15, height = 30 },
|
|
420
|
+
};
|
|
421
|
+
root.Add(_generateAnimationsButton);
|
|
422
|
+
|
|
423
|
+
if (_selectedSpriteSheet != null)
|
|
424
|
+
{
|
|
425
|
+
_spriteSheetField.SetValueWithoutNotify(_selectedSpriteSheet);
|
|
426
|
+
LoadAndDisplaySprites();
|
|
427
|
+
}
|
|
428
|
+
_animationDefinitionsListView.Rebuild();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private void OnEnable()
|
|
432
|
+
{
|
|
433
|
+
EditorApplication.update += _editorUpdateCallback;
|
|
434
|
+
|
|
435
|
+
string data = SessionState.GetString(GetType().FullName, "");
|
|
436
|
+
if (!string.IsNullOrEmpty(data))
|
|
437
|
+
{
|
|
438
|
+
JsonUtility.FromJsonOverwrite(data, this);
|
|
439
|
+
}
|
|
440
|
+
if (_selectedSpriteSheet != null)
|
|
441
|
+
{
|
|
442
|
+
EditorApplication.delayCall += () =>
|
|
443
|
+
{
|
|
444
|
+
if (_spriteSheetField != null)
|
|
445
|
+
{
|
|
446
|
+
_spriteSheetField.value = _selectedSpriteSheet;
|
|
447
|
+
}
|
|
448
|
+
LoadAndDisplaySprites();
|
|
449
|
+
_animationDefinitionsListView.Rebuild();
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private void OnDisable()
|
|
455
|
+
{
|
|
456
|
+
EditorApplication.update -= _editorUpdateCallback;
|
|
457
|
+
StopCurrentPreview();
|
|
458
|
+
|
|
459
|
+
string data = JsonUtility.ToJson(this);
|
|
460
|
+
SessionState.SetString(GetType().FullName, data);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private void OnEditorUpdate()
|
|
464
|
+
{
|
|
465
|
+
if (
|
|
466
|
+
!_isPreviewing
|
|
467
|
+
|| _currentPreviewDefinition is not { SpritesToAnimate: { Count: > 0 } }
|
|
468
|
+
)
|
|
469
|
+
{
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
_lastTick ??= _timer.Elapsed;
|
|
474
|
+
float targetFps = 0;
|
|
475
|
+
if (1 < _currentPreviewDefinition.SpritesToAnimate.Count)
|
|
476
|
+
{
|
|
477
|
+
_currentPreviewDefinition.FrameRateCurve.Evaluate(
|
|
478
|
+
_currentPreviewSpriteIndex
|
|
479
|
+
/ (_currentPreviewDefinition.SpritesToAnimate.Count - 1f)
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (targetFps <= 0)
|
|
484
|
+
{
|
|
485
|
+
targetFps = _currentPreviewDefinition.DefaultFrameRate;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (targetFps <= 0)
|
|
489
|
+
{
|
|
490
|
+
targetFps = 1;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
TimeSpan elapsed = _timer.Elapsed;
|
|
494
|
+
TimeSpan deltaTime = TimeSpan.FromMilliseconds(1000 / targetFps);
|
|
495
|
+
if (_lastTick + deltaTime > elapsed)
|
|
496
|
+
{
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
_lastTick += deltaTime;
|
|
501
|
+
int nextFrame = _currentPreviewSpriteIndex.WrappedIncrement(
|
|
502
|
+
_currentPreviewDefinition.SpritesToAnimate.Count
|
|
503
|
+
);
|
|
504
|
+
SetPreviewFrame(nextFrame);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private void UpdateSpriteSelectionHighlight()
|
|
508
|
+
{
|
|
509
|
+
if (
|
|
510
|
+
!_isDraggingToSelectSprites
|
|
511
|
+
|| _spriteSelectionDragStartIndex == -1
|
|
512
|
+
|| _spriteSelectionDragCurrentIndex == -1
|
|
513
|
+
)
|
|
514
|
+
{
|
|
515
|
+
ClearSpriteSelectionHighlight();
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
int minIdx = Mathf.Min(
|
|
520
|
+
_spriteSelectionDragStartIndex,
|
|
521
|
+
_spriteSelectionDragCurrentIndex
|
|
522
|
+
);
|
|
523
|
+
int maxIdx = Mathf.Max(
|
|
524
|
+
_spriteSelectionDragStartIndex,
|
|
525
|
+
_spriteSelectionDragCurrentIndex
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
for (int i = 0; i < _spriteThumbnailsContainer.childCount; i++)
|
|
529
|
+
{
|
|
530
|
+
VisualElement thumb = _spriteThumbnailsContainer.ElementAt(i);
|
|
531
|
+
if (thumb.userData is int thumbIndex)
|
|
532
|
+
{
|
|
533
|
+
if (thumbIndex >= minIdx && thumbIndex <= maxIdx)
|
|
534
|
+
{
|
|
535
|
+
thumb.style.backgroundColor = _selectedThumbnailBackgroundColor;
|
|
536
|
+
thumb.style.borderBottomColor =
|
|
537
|
+
_selectedThumbnailBackgroundColor.value * 1.5f;
|
|
538
|
+
thumb.style.borderTopColor = _selectedThumbnailBackgroundColor.value * 1.5f;
|
|
539
|
+
thumb.style.borderLeftColor =
|
|
540
|
+
_selectedThumbnailBackgroundColor.value * 1.5f;
|
|
541
|
+
thumb.style.borderRightColor =
|
|
542
|
+
_selectedThumbnailBackgroundColor.value * 1.5f;
|
|
543
|
+
}
|
|
544
|
+
else
|
|
545
|
+
{
|
|
546
|
+
thumb.style.backgroundColor = _defaultThumbnailBackgroundColor;
|
|
547
|
+
thumb.style.borderBottomColor = Color.clear;
|
|
548
|
+
thumb.style.borderTopColor = Color.clear;
|
|
549
|
+
thumb.style.borderLeftColor = Color.clear;
|
|
550
|
+
thumb.style.borderRightColor = Color.clear;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
private void ClearSpriteSelectionHighlight()
|
|
557
|
+
{
|
|
558
|
+
for (int i = 0; i < _spriteThumbnailsContainer.childCount; i++)
|
|
559
|
+
{
|
|
560
|
+
VisualElement thumb = _spriteThumbnailsContainer.ElementAt(i);
|
|
561
|
+
thumb.style.backgroundColor = _defaultThumbnailBackgroundColor;
|
|
562
|
+
thumb.style.borderBottomColor = Color.clear;
|
|
563
|
+
thumb.style.borderTopColor = Color.clear;
|
|
564
|
+
thumb.style.borderLeftColor = Color.clear;
|
|
565
|
+
thumb.style.borderRightColor = Color.clear;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private void CreateAnimationDefinitionFromSelection(
|
|
570
|
+
int startSpriteIndex,
|
|
571
|
+
int endSpriteIndex
|
|
572
|
+
)
|
|
573
|
+
{
|
|
574
|
+
if (
|
|
575
|
+
startSpriteIndex < 0
|
|
576
|
+
|| endSpriteIndex < 0
|
|
577
|
+
|| startSpriteIndex >= _availableSprites.Count
|
|
578
|
+
|| endSpriteIndex >= _availableSprites.Count
|
|
579
|
+
)
|
|
580
|
+
{
|
|
581
|
+
this.LogWarn(
|
|
582
|
+
$"Invalid sprite indices for new animation definition from selection."
|
|
583
|
+
);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
AnimationDefinition newDefinition = new()
|
|
588
|
+
{
|
|
589
|
+
Name =
|
|
590
|
+
_selectedSpriteSheet != null
|
|
591
|
+
? $"{_selectedSpriteSheet.name}_Anim_{_animationDefinitions.Count}"
|
|
592
|
+
: $"New_Animation_{_animationDefinitions.Count}",
|
|
593
|
+
StartSpriteIndex = startSpriteIndex,
|
|
594
|
+
EndSpriteIndex = endSpriteIndex,
|
|
595
|
+
DefaultFrameRate = 12f,
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
newDefinition.FrameRateCurve = AnimationCurve.Constant(
|
|
599
|
+
0,
|
|
600
|
+
1,
|
|
601
|
+
newDefinition.DefaultFrameRate
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
_animationDefinitions.Add(newDefinition);
|
|
605
|
+
UpdateSpritesForDefinition(newDefinition);
|
|
606
|
+
_currentPreviewAnimDefIndex = _animationDefinitions.Count - 1;
|
|
607
|
+
StartOrUpdateCurrentPreview(newDefinition);
|
|
608
|
+
_animationDefinitionsListView.Rebuild();
|
|
609
|
+
|
|
610
|
+
if (_animationDefinitionsListView.itemsSource.Count > 0)
|
|
611
|
+
{
|
|
612
|
+
_animationDefinitionsListView.ScrollToItem(_animationDefinitions.Count - 1);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
private void OnSpriteSheetSelected(ChangeEvent<Object> evt)
|
|
617
|
+
{
|
|
618
|
+
_selectedSpriteSheet = evt.newValue as Texture2D;
|
|
619
|
+
_animationDefinitions.Clear();
|
|
620
|
+
AddAnimationDefinition();
|
|
621
|
+
_animationDefinitionsListView.Rebuild();
|
|
622
|
+
LoadAndDisplaySprites();
|
|
623
|
+
StopCurrentPreview();
|
|
624
|
+
_previewImage.sprite = null;
|
|
625
|
+
_previewImage.style.backgroundImage = null;
|
|
626
|
+
_previewFrameLabel.text = "Frame: -/- | FPS: -";
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
private void LoadAndDisplaySprites()
|
|
630
|
+
{
|
|
631
|
+
if (_selectedSpriteSheet == null)
|
|
632
|
+
{
|
|
633
|
+
EditorUtility.DisplayDialog("Error", "No sprite sheet selected.", "OK");
|
|
634
|
+
_availableSprites.Clear();
|
|
635
|
+
UpdateSpriteThumbnails();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
string path = AssetDatabase.GetAssetPath(_selectedSpriteSheet);
|
|
640
|
+
if (string.IsNullOrEmpty(path))
|
|
641
|
+
{
|
|
642
|
+
EditorUtility.DisplayDialog("Error", "Selected texture is not an asset.", "OK");
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
|
|
647
|
+
if (importer == null)
|
|
648
|
+
{
|
|
649
|
+
EditorUtility.DisplayDialog(
|
|
650
|
+
"Error",
|
|
651
|
+
"Could not get TextureImporter for the selected texture.",
|
|
652
|
+
"OK"
|
|
653
|
+
);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
bool importSettingsChanged = false;
|
|
658
|
+
if (importer.textureType != TextureImporterType.Sprite)
|
|
659
|
+
{
|
|
660
|
+
importer.textureType = TextureImporterType.Sprite;
|
|
661
|
+
importSettingsChanged = true;
|
|
662
|
+
}
|
|
663
|
+
if (importer.spriteImportMode != SpriteImportMode.Multiple)
|
|
664
|
+
{
|
|
665
|
+
bool fix = EditorUtility.DisplayDialog(
|
|
666
|
+
"Sprite Mode",
|
|
667
|
+
"The selected texture is not in 'Sprite Mode: Multiple'. This is required to extract individual sprites.\n\nAttempt to change it automatically?",
|
|
668
|
+
"Yes, Change It",
|
|
669
|
+
"No, I'll Do It"
|
|
670
|
+
);
|
|
671
|
+
if (fix)
|
|
672
|
+
{
|
|
673
|
+
importer.spriteImportMode = SpriteImportMode.Multiple;
|
|
674
|
+
|
|
675
|
+
importSettingsChanged = true;
|
|
676
|
+
}
|
|
677
|
+
else
|
|
678
|
+
{
|
|
679
|
+
_availableSprites.Clear();
|
|
680
|
+
UpdateSpriteThumbnails();
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (importSettingsChanged)
|
|
686
|
+
{
|
|
687
|
+
EditorUtility.SetDirty(importer);
|
|
688
|
+
importer.SaveAndReimport();
|
|
689
|
+
AssetDatabase.Refresh();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
_availableSprites.Clear();
|
|
693
|
+
Object[] assets = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
|
|
694
|
+
foreach (Object asset in assets)
|
|
695
|
+
{
|
|
696
|
+
if (asset is Sprite sprite && sprite != null)
|
|
697
|
+
{
|
|
698
|
+
_availableSprites.Add(sprite);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
_availableSprites.SortByName();
|
|
703
|
+
|
|
704
|
+
if (_availableSprites.Count == 0)
|
|
705
|
+
{
|
|
706
|
+
EditorUtility.DisplayDialog(
|
|
707
|
+
"No Sprites",
|
|
708
|
+
"No sprites found in the selected Texture. Ensure it's sliced correctly in the Sprite Editor.",
|
|
709
|
+
"OK"
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
UpdateSpriteThumbnails();
|
|
714
|
+
UpdateAllAnimationDefinitionSprites();
|
|
715
|
+
_animationDefinitionsListView.Rebuild();
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
private void UpdateSpriteThumbnails()
|
|
719
|
+
{
|
|
720
|
+
_spriteThumbnailsContainer.Clear();
|
|
721
|
+
if (_availableSprites.Count == 0)
|
|
722
|
+
{
|
|
723
|
+
_spriteThumbnailsContainer.Add(new Label("No sprites loaded or sheet not sliced."));
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
for (int i = 0; i < _availableSprites.Count; ++i)
|
|
728
|
+
{
|
|
729
|
+
Sprite sprite = _availableSprites[i];
|
|
730
|
+
VisualElement thumbContainer = new()
|
|
731
|
+
{
|
|
732
|
+
style =
|
|
733
|
+
{
|
|
734
|
+
alignItems = Align.Center,
|
|
735
|
+
marginRight = 5,
|
|
736
|
+
paddingBottom = 2,
|
|
737
|
+
borderBottomWidth = 1,
|
|
738
|
+
borderLeftWidth = 1,
|
|
739
|
+
borderRightWidth = 1,
|
|
740
|
+
borderTopWidth = 1,
|
|
741
|
+
borderBottomColor = Color.clear,
|
|
742
|
+
borderTopColor = Color.clear,
|
|
743
|
+
borderLeftColor = Color.clear,
|
|
744
|
+
borderRightColor = Color.clear,
|
|
745
|
+
},
|
|
746
|
+
};
|
|
747
|
+
Image img = new()
|
|
748
|
+
{
|
|
749
|
+
sprite = sprite,
|
|
750
|
+
scaleMode = ScaleMode.ScaleToFit,
|
|
751
|
+
style = { width = ThumbnailSize, height = ThumbnailSize },
|
|
752
|
+
};
|
|
753
|
+
thumbContainer.Add(img);
|
|
754
|
+
thumbContainer.Add(new Label($"{i}") { style = { fontSize = 9 } });
|
|
755
|
+
|
|
756
|
+
int currentIndex = i;
|
|
757
|
+
thumbContainer.userData = currentIndex;
|
|
758
|
+
|
|
759
|
+
thumbContainer.RegisterCallback<PointerDownEvent>(evt =>
|
|
760
|
+
{
|
|
761
|
+
if (evt.button == 0)
|
|
762
|
+
{
|
|
763
|
+
_isDraggingToSelectSprites = true;
|
|
764
|
+
_spriteSelectionDragStartIndex = currentIndex;
|
|
765
|
+
_spriteSelectionDragCurrentIndex = currentIndex;
|
|
766
|
+
UpdateSpriteSelectionHighlight();
|
|
767
|
+
|
|
768
|
+
_spriteThumbnailsContainer.CapturePointer(evt.pointerId);
|
|
769
|
+
evt.StopPropagation();
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
thumbContainer.RegisterCallback<PointerEnterEvent>(_ =>
|
|
774
|
+
{
|
|
775
|
+
if (
|
|
776
|
+
_isDraggingToSelectSprites
|
|
777
|
+
&& _spriteThumbnailsContainer.HasPointerCapture(PointerId.mousePointerId)
|
|
778
|
+
&& _spriteSelectionDragCurrentIndex != currentIndex
|
|
779
|
+
)
|
|
780
|
+
{
|
|
781
|
+
_spriteSelectionDragCurrentIndex = currentIndex;
|
|
782
|
+
UpdateSpriteSelectionHighlight();
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
_spriteThumbnailsContainer.Add(thumbContainer);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
private void UpdateAllAnimationDefinitionSprites()
|
|
790
|
+
{
|
|
791
|
+
foreach (AnimationDefinition def in _animationDefinitions)
|
|
792
|
+
{
|
|
793
|
+
UpdateSpritesForDefinition(def);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
private static VisualElement MakeAnimationDefinitionItem()
|
|
798
|
+
{
|
|
799
|
+
VisualElement container = new()
|
|
800
|
+
{
|
|
801
|
+
style =
|
|
802
|
+
{
|
|
803
|
+
flexDirection = FlexDirection.Column,
|
|
804
|
+
borderBottomWidth = 1,
|
|
805
|
+
borderBottomColor = Color.gray,
|
|
806
|
+
paddingBottom = 10,
|
|
807
|
+
paddingTop = 5,
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
VisualElement firstRow = new()
|
|
811
|
+
{
|
|
812
|
+
style =
|
|
813
|
+
{
|
|
814
|
+
flexDirection = FlexDirection.Row,
|
|
815
|
+
alignItems = Align.Center,
|
|
816
|
+
marginBottom = 3,
|
|
817
|
+
},
|
|
818
|
+
};
|
|
819
|
+
VisualElement secondRow = new()
|
|
820
|
+
{
|
|
821
|
+
style =
|
|
822
|
+
{
|
|
823
|
+
flexDirection = FlexDirection.Row,
|
|
824
|
+
alignItems = Align.Center,
|
|
825
|
+
marginBottom = 3,
|
|
826
|
+
},
|
|
827
|
+
};
|
|
828
|
+
VisualElement thirdRow = new()
|
|
829
|
+
{
|
|
830
|
+
style = { flexDirection = FlexDirection.Row, alignItems = Align.Center },
|
|
831
|
+
};
|
|
832
|
+
VisualElement fourthRow = new()
|
|
833
|
+
{
|
|
834
|
+
style = { flexDirection = FlexDirection.Row, alignItems = Align.Center },
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
TextField nameField = new("Name:")
|
|
838
|
+
{
|
|
839
|
+
style =
|
|
840
|
+
{
|
|
841
|
+
flexGrow = 1,
|
|
842
|
+
flexShrink = 1,
|
|
843
|
+
marginRight = 5,
|
|
844
|
+
},
|
|
845
|
+
};
|
|
846
|
+
Label spriteCountLabel = new("Sprites: 0")
|
|
847
|
+
{
|
|
848
|
+
style = { minWidth = 80, marginRight = 5 },
|
|
849
|
+
};
|
|
850
|
+
Button removeButton = new() { text = "Remove", style = { minWidth = 60 } };
|
|
851
|
+
|
|
852
|
+
firstRow.Add(nameField);
|
|
853
|
+
firstRow.Add(spriteCountLabel);
|
|
854
|
+
firstRow.Add(removeButton);
|
|
855
|
+
|
|
856
|
+
IntegerField startField = new("Start Idx:")
|
|
857
|
+
{
|
|
858
|
+
style =
|
|
859
|
+
{
|
|
860
|
+
flexGrow = 1,
|
|
861
|
+
flexShrink = 1,
|
|
862
|
+
marginRight = 5,
|
|
863
|
+
},
|
|
864
|
+
tooltip = "Index of the first sprite (from 'Available Sprites' above, 0-based).",
|
|
865
|
+
};
|
|
866
|
+
IntegerField endField = new("End Idx:")
|
|
867
|
+
{
|
|
868
|
+
style =
|
|
869
|
+
{
|
|
870
|
+
flexGrow = 1,
|
|
871
|
+
flexShrink = 1,
|
|
872
|
+
marginRight = 5,
|
|
873
|
+
},
|
|
874
|
+
tooltip = "Index of the last sprite (inclusive).",
|
|
875
|
+
};
|
|
876
|
+
Button previewButton = new() { text = "Preview This", style = { minWidth = 100 } };
|
|
877
|
+
|
|
878
|
+
secondRow.Add(startField);
|
|
879
|
+
secondRow.Add(endField);
|
|
880
|
+
secondRow.Add(previewButton);
|
|
881
|
+
|
|
882
|
+
FloatField fpsField = new("Default FPS:")
|
|
883
|
+
{
|
|
884
|
+
style =
|
|
885
|
+
{
|
|
886
|
+
flexGrow = 1,
|
|
887
|
+
flexShrink = 1,
|
|
888
|
+
marginRight = 5,
|
|
889
|
+
},
|
|
890
|
+
};
|
|
891
|
+
CurveField curveField = new("FPS Curve:") { style = { flexGrow = 1, flexShrink = 1 } };
|
|
892
|
+
|
|
893
|
+
thirdRow.Add(fpsField);
|
|
894
|
+
thirdRow.Add(curveField);
|
|
895
|
+
|
|
896
|
+
Toggle looping = new("Looping:")
|
|
897
|
+
{
|
|
898
|
+
style =
|
|
899
|
+
{
|
|
900
|
+
flexGrow = 1,
|
|
901
|
+
flexShrink = 1,
|
|
902
|
+
marginRight = 5,
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
FloatField cycleOffset = new("Cycle Offset:")
|
|
907
|
+
{
|
|
908
|
+
style =
|
|
909
|
+
{
|
|
910
|
+
flexGrow = 1,
|
|
911
|
+
flexShrink = 1,
|
|
912
|
+
marginRight = 5,
|
|
913
|
+
},
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
fourthRow.Add(looping);
|
|
917
|
+
fourthRow.Add(cycleOffset);
|
|
918
|
+
|
|
919
|
+
container.Add(firstRow);
|
|
920
|
+
container.Add(secondRow);
|
|
921
|
+
container.Add(thirdRow);
|
|
922
|
+
container.Add(fourthRow);
|
|
923
|
+
|
|
924
|
+
container.userData = new AnimationDefUITags
|
|
925
|
+
{
|
|
926
|
+
nameField = nameField,
|
|
927
|
+
startIndexField = startField,
|
|
928
|
+
endIndexField = endField,
|
|
929
|
+
defaultFrameRateField = fpsField,
|
|
930
|
+
frameRateCurveField = curveField,
|
|
931
|
+
spriteCountLabel = spriteCountLabel,
|
|
932
|
+
previewButton = previewButton,
|
|
933
|
+
removeButton = removeButton,
|
|
934
|
+
looping = looping,
|
|
935
|
+
cycleOffset = cycleOffset,
|
|
936
|
+
};
|
|
937
|
+
return container;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
private class AnimationDefUITags
|
|
941
|
+
{
|
|
942
|
+
public TextField nameField;
|
|
943
|
+
public IntegerField startIndexField;
|
|
944
|
+
public IntegerField endIndexField;
|
|
945
|
+
public FloatField defaultFrameRateField;
|
|
946
|
+
public CurveField frameRateCurveField;
|
|
947
|
+
public Label spriteCountLabel;
|
|
948
|
+
public Button previewButton;
|
|
949
|
+
public Button removeButton;
|
|
950
|
+
public Toggle looping;
|
|
951
|
+
public FloatField cycleOffset;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
private void BindAnimationDefinitionItem(VisualElement element, int index)
|
|
955
|
+
{
|
|
956
|
+
AnimationDefinition definition = _animationDefinitions[index];
|
|
957
|
+
if (element.userData is not AnimationDefUITags tags)
|
|
958
|
+
{
|
|
959
|
+
this.LogError(
|
|
960
|
+
$"Element UserData was not AnimationDefUITags, found: {element.userData?.GetType()}."
|
|
961
|
+
);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
definition.nameField?.UnregisterValueChangedCallback(
|
|
966
|
+
definition.nameField.userData as EventCallback<ChangeEvent<string>>
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
definition.startIndexField?.UnregisterValueChangedCallback(
|
|
970
|
+
definition.startIndexField.userData as EventCallback<ChangeEvent<int>>
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
definition.endIndexField?.UnregisterValueChangedCallback(
|
|
974
|
+
definition.endIndexField.userData as EventCallback<ChangeEvent<int>>
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
definition.defaultFrameRateField?.UnregisterValueChangedCallback(
|
|
978
|
+
definition.defaultFrameRateField.userData as EventCallback<ChangeEvent<float>>
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
definition.frameRateCurveField?.UnregisterValueChangedCallback(
|
|
982
|
+
definition.frameRateCurveField.userData
|
|
983
|
+
as EventCallback<ChangeEvent<AnimationCurve>>
|
|
984
|
+
);
|
|
985
|
+
|
|
986
|
+
if (definition.removeButton != null)
|
|
987
|
+
{
|
|
988
|
+
definition.removeButton.clicked -= (Action)definition.removeButton.userData;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (definition.previewButton != null)
|
|
992
|
+
{
|
|
993
|
+
definition.previewButton.clicked -= (Action)definition.previewButton.userData;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
definition.loopingField?.UnregisterValueChangedCallback(
|
|
997
|
+
(EventCallback<ChangeEvent<bool>>)definition.loopingField.userData
|
|
998
|
+
);
|
|
999
|
+
definition.cycleOffsetField?.UnregisterValueChangedCallback(
|
|
1000
|
+
(EventCallback<ChangeEvent<float>>)definition.cycleOffsetField.userData
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
definition.nameField = tags.nameField;
|
|
1004
|
+
definition.startIndexField = tags.startIndexField;
|
|
1005
|
+
definition.endIndexField = tags.endIndexField;
|
|
1006
|
+
definition.defaultFrameRateField = tags.defaultFrameRateField;
|
|
1007
|
+
definition.frameRateCurveField = tags.frameRateCurveField;
|
|
1008
|
+
definition.spriteCountLabel = tags.spriteCountLabel;
|
|
1009
|
+
definition.removeButton = tags.removeButton;
|
|
1010
|
+
definition.previewButton = tags.previewButton;
|
|
1011
|
+
definition.loopingField = tags.looping;
|
|
1012
|
+
definition.cycleOffsetField = tags.cycleOffset;
|
|
1013
|
+
|
|
1014
|
+
definition.nameField.SetValueWithoutNotify(definition.Name);
|
|
1015
|
+
EventCallback<ChangeEvent<string>> nameChangeCallback = evt =>
|
|
1016
|
+
{
|
|
1017
|
+
definition.Name = evt.newValue;
|
|
1018
|
+
};
|
|
1019
|
+
definition.nameField.RegisterValueChangedCallback(nameChangeCallback);
|
|
1020
|
+
definition.nameField.userData = nameChangeCallback;
|
|
1021
|
+
|
|
1022
|
+
definition.startIndexField.SetValueWithoutNotify(definition.StartSpriteIndex);
|
|
1023
|
+
EventCallback<ChangeEvent<int>> startChangeCallback = evt =>
|
|
1024
|
+
{
|
|
1025
|
+
definition.StartSpriteIndex = Mathf.Clamp(
|
|
1026
|
+
evt.newValue,
|
|
1027
|
+
0,
|
|
1028
|
+
_availableSprites.Count > 0 ? _availableSprites.Count - 1 : 0
|
|
1029
|
+
);
|
|
1030
|
+
if (
|
|
1031
|
+
definition.StartSpriteIndex > definition.EndSpriteIndex
|
|
1032
|
+
&& 0 < _availableSprites.Count
|
|
1033
|
+
)
|
|
1034
|
+
{
|
|
1035
|
+
definition.EndSpriteIndex = definition.StartSpriteIndex;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
definition.startIndexField.SetValueWithoutNotify(definition.StartSpriteIndex);
|
|
1039
|
+
UpdateSpritesForDefinition(definition);
|
|
1040
|
+
if (_currentPreviewAnimDefIndex == index)
|
|
1041
|
+
{
|
|
1042
|
+
StartOrUpdateCurrentPreview(definition);
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
definition.startIndexField.RegisterValueChangedCallback(startChangeCallback);
|
|
1046
|
+
definition.startIndexField.userData = startChangeCallback;
|
|
1047
|
+
|
|
1048
|
+
definition.endIndexField.SetValueWithoutNotify(definition.EndSpriteIndex);
|
|
1049
|
+
EventCallback<ChangeEvent<int>> endChangeCallback = evt =>
|
|
1050
|
+
{
|
|
1051
|
+
definition.EndSpriteIndex = Mathf.Clamp(
|
|
1052
|
+
evt.newValue,
|
|
1053
|
+
0,
|
|
1054
|
+
_availableSprites.Count > 0 ? _availableSprites.Count - 1 : 0
|
|
1055
|
+
);
|
|
1056
|
+
if (
|
|
1057
|
+
definition.EndSpriteIndex < definition.StartSpriteIndex
|
|
1058
|
+
&& 0 < _availableSprites.Count
|
|
1059
|
+
)
|
|
1060
|
+
{
|
|
1061
|
+
definition.StartSpriteIndex = definition.EndSpriteIndex;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
definition.endIndexField.SetValueWithoutNotify(definition.EndSpriteIndex);
|
|
1065
|
+
UpdateSpritesForDefinition(definition);
|
|
1066
|
+
if (_currentPreviewAnimDefIndex == index)
|
|
1067
|
+
{
|
|
1068
|
+
StartOrUpdateCurrentPreview(definition);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
definition.endIndexField.RegisterValueChangedCallback(endChangeCallback);
|
|
1072
|
+
definition.endIndexField.userData = endChangeCallback;
|
|
1073
|
+
|
|
1074
|
+
definition.defaultFrameRateField.SetValueWithoutNotify(definition.DefaultFrameRate);
|
|
1075
|
+
EventCallback<ChangeEvent<float>> fpsChangeCallback = evt =>
|
|
1076
|
+
{
|
|
1077
|
+
definition.DefaultFrameRate = Mathf.Max(0.1f, evt.newValue);
|
|
1078
|
+
definition.defaultFrameRateField.SetValueWithoutNotify(definition.DefaultFrameRate);
|
|
1079
|
+
|
|
1080
|
+
if (IsCurveConstant(definition.FrameRateCurve))
|
|
1081
|
+
{
|
|
1082
|
+
definition.FrameRateCurve = AnimationCurve.Constant(
|
|
1083
|
+
0,
|
|
1084
|
+
1,
|
|
1085
|
+
definition.DefaultFrameRate
|
|
1086
|
+
);
|
|
1087
|
+
definition.frameRateCurveField.SetValueWithoutNotify(definition.FrameRateCurve);
|
|
1088
|
+
}
|
|
1089
|
+
if (_currentPreviewAnimDefIndex == index)
|
|
1090
|
+
{
|
|
1091
|
+
StartOrUpdateCurrentPreview(definition);
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
definition.defaultFrameRateField.RegisterValueChangedCallback(fpsChangeCallback);
|
|
1095
|
+
definition.defaultFrameRateField.userData = fpsChangeCallback;
|
|
1096
|
+
|
|
1097
|
+
definition.frameRateCurveField.SetValueWithoutNotify(definition.FrameRateCurve);
|
|
1098
|
+
EventCallback<ChangeEvent<AnimationCurve>> curveChangeCallback = evt =>
|
|
1099
|
+
{
|
|
1100
|
+
definition.FrameRateCurve = evt.newValue;
|
|
1101
|
+
if (_currentPreviewAnimDefIndex == index)
|
|
1102
|
+
{
|
|
1103
|
+
StartOrUpdateCurrentPreview(definition);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
definition.frameRateCurveField.RegisterValueChangedCallback(curveChangeCallback);
|
|
1107
|
+
definition.frameRateCurveField.userData = curveChangeCallback;
|
|
1108
|
+
|
|
1109
|
+
Action removeAction = () => RemoveAnimationDefinition(index);
|
|
1110
|
+
definition.removeButton.clicked += removeAction;
|
|
1111
|
+
definition.removeButton.userData = removeAction;
|
|
1112
|
+
|
|
1113
|
+
Action previewAction = () =>
|
|
1114
|
+
{
|
|
1115
|
+
_currentPreviewAnimDefIndex = index;
|
|
1116
|
+
StartOrUpdateCurrentPreview(definition);
|
|
1117
|
+
};
|
|
1118
|
+
definition.previewButton.clicked += previewAction;
|
|
1119
|
+
definition.previewButton.userData = previewAction;
|
|
1120
|
+
|
|
1121
|
+
EventCallback<ChangeEvent<bool>> loopingChangeCallback = evt =>
|
|
1122
|
+
{
|
|
1123
|
+
definition.loop = evt.newValue;
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
definition.loopingField.RegisterValueChangedCallback(loopingChangeCallback);
|
|
1127
|
+
definition.loopingField.userData = loopingChangeCallback;
|
|
1128
|
+
|
|
1129
|
+
EventCallback<ChangeEvent<float>> cycleOffsetChangeCallback = evt =>
|
|
1130
|
+
{
|
|
1131
|
+
definition.cycleOffset = evt.newValue;
|
|
1132
|
+
};
|
|
1133
|
+
definition.cycleOffsetField.RegisterValueChangedCallback(cycleOffsetChangeCallback);
|
|
1134
|
+
definition.cycleOffsetField.userData = cycleOffsetChangeCallback;
|
|
1135
|
+
|
|
1136
|
+
UpdateSpritesForDefinition(definition);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
private static bool IsCurveConstant(AnimationCurve curve)
|
|
1140
|
+
{
|
|
1141
|
+
if (curve == null || curve.keys.Length < 2)
|
|
1142
|
+
{
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
float firstValue = curve.keys[0].value;
|
|
1147
|
+
for (int i = 1; i < curve.keys.Length; ++i)
|
|
1148
|
+
{
|
|
1149
|
+
if (!Mathf.Approximately(curve.keys[i].value, firstValue))
|
|
1150
|
+
{
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return true;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
private void UpdateSpritesForDefinition(AnimationDefinition def)
|
|
1158
|
+
{
|
|
1159
|
+
def.SpritesToAnimate.Clear();
|
|
1160
|
+
if (
|
|
1161
|
+
0 < _availableSprites.Count
|
|
1162
|
+
&& def.StartSpriteIndex <= def.EndSpriteIndex
|
|
1163
|
+
&& def.StartSpriteIndex < _availableSprites.Count
|
|
1164
|
+
&& def.EndSpriteIndex < _availableSprites.Count
|
|
1165
|
+
&& def.StartSpriteIndex >= 0
|
|
1166
|
+
&& def.EndSpriteIndex >= 0
|
|
1167
|
+
)
|
|
1168
|
+
{
|
|
1169
|
+
for (int i = def.StartSpriteIndex; i <= def.EndSpriteIndex; ++i)
|
|
1170
|
+
{
|
|
1171
|
+
def.SpritesToAnimate.Add(_availableSprites[i]);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (def.spriteCountLabel != null)
|
|
1175
|
+
{
|
|
1176
|
+
def.spriteCountLabel.text = $"Sprites: {def.SpritesToAnimate.Count}";
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
private void AddAnimationDefinition()
|
|
1181
|
+
{
|
|
1182
|
+
AnimationDefinition newDef = new();
|
|
1183
|
+
if (_selectedSpriteSheet != null)
|
|
1184
|
+
{
|
|
1185
|
+
newDef.Name = $"{_selectedSpriteSheet.name}_Anim_{_animationDefinitions.Count}";
|
|
1186
|
+
}
|
|
1187
|
+
if (0 < _availableSprites.Count)
|
|
1188
|
+
{
|
|
1189
|
+
if (0 < _animationDefinitions.Count)
|
|
1190
|
+
{
|
|
1191
|
+
int nextStartIndex = _animationDefinitions[^1].EndSpriteIndex + 1;
|
|
1192
|
+
if (_availableSprites.Count - 1 <= nextStartIndex)
|
|
1193
|
+
{
|
|
1194
|
+
nextStartIndex = 0;
|
|
1195
|
+
}
|
|
1196
|
+
newDef.StartSpriteIndex = nextStartIndex;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
newDef.EndSpriteIndex = _availableSprites.Count - 1;
|
|
1200
|
+
}
|
|
1201
|
+
newDef.FrameRateCurve = AnimationCurve.Constant(0, 1, newDef.DefaultFrameRate);
|
|
1202
|
+
_animationDefinitions.Add(newDef);
|
|
1203
|
+
UpdateSpritesForDefinition(newDef);
|
|
1204
|
+
_animationDefinitionsListView.Rebuild();
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
private void RemoveAnimationDefinition(int index)
|
|
1208
|
+
{
|
|
1209
|
+
if (index >= 0 && index < _animationDefinitions.Count)
|
|
1210
|
+
{
|
|
1211
|
+
if (_currentPreviewAnimDefIndex == index)
|
|
1212
|
+
{
|
|
1213
|
+
StopCurrentPreview();
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
_animationDefinitions.RemoveAt(index);
|
|
1217
|
+
_animationDefinitionsListView.Rebuild();
|
|
1218
|
+
if (_currentPreviewAnimDefIndex > index)
|
|
1219
|
+
{
|
|
1220
|
+
_currentPreviewAnimDefIndex--;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
private void StartOrUpdateCurrentPreview(AnimationDefinition def)
|
|
1226
|
+
{
|
|
1227
|
+
_currentPreviewDefinition = def;
|
|
1228
|
+
if (def == null || def.SpritesToAnimate.Count == 0)
|
|
1229
|
+
{
|
|
1230
|
+
StopCurrentPreview();
|
|
1231
|
+
_previewImage.sprite = null;
|
|
1232
|
+
_previewImage.style.backgroundImage = null;
|
|
1233
|
+
_previewFrameLabel.text = "Frame: -/- | FPS: -";
|
|
1234
|
+
_previewScrubber.style.visibility = Visibility.Hidden;
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
_previewScrubber.style.visibility = Visibility.Visible;
|
|
1239
|
+
_previewScrubber.highValue = def.SpritesToAnimate.Count > 1 ? 1f : 0f;
|
|
1240
|
+
_previewScrubber.SetValueWithoutNotify(0);
|
|
1241
|
+
|
|
1242
|
+
SetPreviewFrame(0);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
private void PlayCurrentPreview()
|
|
1246
|
+
{
|
|
1247
|
+
if (
|
|
1248
|
+
_currentPreviewDefinition == null
|
|
1249
|
+
|| _currentPreviewDefinition.SpritesToAnimate.Count == 0
|
|
1250
|
+
)
|
|
1251
|
+
{
|
|
1252
|
+
EditorUtility.DisplayDialog(
|
|
1253
|
+
"Preview Error",
|
|
1254
|
+
"No animation definition selected or definition has no sprites. Click 'Preview This' on an animation definition first.",
|
|
1255
|
+
"OK"
|
|
1256
|
+
);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
_isPreviewing = true;
|
|
1261
|
+
_playPreviewButton.style.display = DisplayStyle.None;
|
|
1262
|
+
_stopPreviewButton.style.display = DisplayStyle.Flex;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
private void StopCurrentPreview()
|
|
1266
|
+
{
|
|
1267
|
+
_isPreviewing = false;
|
|
1268
|
+
_lastTick = null;
|
|
1269
|
+
_playPreviewButton.style.display = DisplayStyle.Flex;
|
|
1270
|
+
_stopPreviewButton.style.display = DisplayStyle.None;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
private void SetPreviewFrame(int frameIndex)
|
|
1274
|
+
{
|
|
1275
|
+
if (_currentPreviewDefinition is not { SpritesToAnimate: { Count: > 0 } })
|
|
1276
|
+
{
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
_currentPreviewSpriteIndex = Mathf.Clamp(
|
|
1281
|
+
frameIndex,
|
|
1282
|
+
0,
|
|
1283
|
+
_currentPreviewDefinition.SpritesToAnimate.Count - 1
|
|
1284
|
+
);
|
|
1285
|
+
|
|
1286
|
+
Sprite spriteToShow = _currentPreviewDefinition.SpritesToAnimate[
|
|
1287
|
+
_currentPreviewSpriteIndex
|
|
1288
|
+
];
|
|
1289
|
+
_previewImage.sprite = spriteToShow;
|
|
1290
|
+
_previewImage.MarkDirtyRepaint();
|
|
1291
|
+
|
|
1292
|
+
float currentCurveTime = 0f;
|
|
1293
|
+
if (_currentPreviewDefinition.SpritesToAnimate.Count > 1)
|
|
1294
|
+
{
|
|
1295
|
+
currentCurveTime =
|
|
1296
|
+
(float)_currentPreviewSpriteIndex
|
|
1297
|
+
/ (_currentPreviewDefinition.SpritesToAnimate.Count - 1);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
float fpsAtCurrent = _currentPreviewDefinition.FrameRateCurve.Evaluate(
|
|
1301
|
+
currentCurveTime
|
|
1302
|
+
* _currentPreviewDefinition.FrameRateCurve.keys.LastOrDefault().time
|
|
1303
|
+
);
|
|
1304
|
+
if (fpsAtCurrent <= 0)
|
|
1305
|
+
{
|
|
1306
|
+
fpsAtCurrent = _currentPreviewDefinition.DefaultFrameRate;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
_previewFrameLabel.text =
|
|
1310
|
+
$"Frame: {_currentPreviewSpriteIndex + 1}/{_currentPreviewDefinition.SpritesToAnimate.Count} | FPS: {fpsAtCurrent:F1}";
|
|
1311
|
+
|
|
1312
|
+
if (_currentPreviewDefinition.SpritesToAnimate.Count > 1)
|
|
1313
|
+
{
|
|
1314
|
+
_previewScrubber.SetValueWithoutNotify(
|
|
1315
|
+
(float)_currentPreviewSpriteIndex
|
|
1316
|
+
/ (_currentPreviewDefinition.SpritesToAnimate.Count - 1)
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
else
|
|
1320
|
+
{
|
|
1321
|
+
_previewScrubber.SetValueWithoutNotify(0);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
private void AdjustPreviewFrame(int direction)
|
|
1326
|
+
{
|
|
1327
|
+
if (_currentPreviewDefinition is not { SpritesToAnimate: { Count: > 0 } })
|
|
1328
|
+
{
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
StopCurrentPreview();
|
|
1333
|
+
|
|
1334
|
+
int newFrame = _currentPreviewSpriteIndex + direction;
|
|
1335
|
+
|
|
1336
|
+
int count = _currentPreviewDefinition.SpritesToAnimate.Count;
|
|
1337
|
+
if (newFrame < 0)
|
|
1338
|
+
{
|
|
1339
|
+
newFrame = count - 1;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (newFrame >= count)
|
|
1343
|
+
{
|
|
1344
|
+
newFrame = 0;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
SetPreviewFrame(newFrame);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
private void GenerateAnimations()
|
|
1351
|
+
{
|
|
1352
|
+
if (_selectedSpriteSheet == null)
|
|
1353
|
+
{
|
|
1354
|
+
EditorUtility.DisplayDialog("Error", "No sprite sheet loaded.", "OK");
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
if (_animationDefinitions.Count == 0)
|
|
1358
|
+
{
|
|
1359
|
+
EditorUtility.DisplayDialog("Error", "No animation definitions created.", "OK");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
string sheetPath = AssetDatabase.GetAssetPath(_selectedSpriteSheet);
|
|
1364
|
+
string directory = Path.GetDirectoryName(sheetPath);
|
|
1365
|
+
string animationsFolder = EditorUtility.OpenFolderPanel(
|
|
1366
|
+
"Select Output Directory",
|
|
1367
|
+
directory,
|
|
1368
|
+
string.Empty
|
|
1369
|
+
);
|
|
1370
|
+
if (string.IsNullOrWhiteSpace(animationsFolder))
|
|
1371
|
+
{
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
if (!Directory.Exists(animationsFolder))
|
|
1376
|
+
{
|
|
1377
|
+
Directory.CreateDirectory(animationsFolder);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
if (!animationsFolder.StartsWith("Assets", StringComparison.OrdinalIgnoreCase))
|
|
1381
|
+
{
|
|
1382
|
+
animationsFolder = DirectoryHelper.AbsoluteToUnityRelativePath(animationsFolder);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
int createdCount = 0;
|
|
1386
|
+
foreach (AnimationDefinition definition in _animationDefinitions)
|
|
1387
|
+
{
|
|
1388
|
+
if (definition.SpritesToAnimate.Count == 0)
|
|
1389
|
+
{
|
|
1390
|
+
this.LogWarn($"Skipping animation '{definition.Name}' as it has no sprites.");
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
AnimationClip clip = new() { frameRate = 60 };
|
|
1395
|
+
|
|
1396
|
+
EditorCurveBinding spriteBinding = new()
|
|
1397
|
+
{
|
|
1398
|
+
type = typeof(SpriteRenderer),
|
|
1399
|
+
path = "",
|
|
1400
|
+
propertyName = "m_Sprite",
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
ObjectReferenceKeyframe[] keyframes = new ObjectReferenceKeyframe[
|
|
1404
|
+
definition.SpritesToAnimate.Count
|
|
1405
|
+
];
|
|
1406
|
+
float currentTime = 0f;
|
|
1407
|
+
AnimationCurve curve = definition.FrameRateCurve;
|
|
1408
|
+
if (curve == null || curve.keys.Length == 0)
|
|
1409
|
+
{
|
|
1410
|
+
this.LogWarn(
|
|
1411
|
+
$"Animation '{definition.Name}' has an invalid FrameRateCurve. Falling back to DefaultFrameRate."
|
|
1412
|
+
);
|
|
1413
|
+
curve = AnimationCurve.Constant(0, 1, definition.DefaultFrameRate);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (curve.keys.Length == 0)
|
|
1417
|
+
{
|
|
1418
|
+
curve.AddKey(0, definition.DefaultFrameRate);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
float curveDuration = curve.keys.LastOrDefault().time;
|
|
1422
|
+
if (curveDuration <= 0)
|
|
1423
|
+
{
|
|
1424
|
+
curveDuration = 1f;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
for (int i = 0; i < definition.SpritesToAnimate.Count; ++i)
|
|
1428
|
+
{
|
|
1429
|
+
keyframes[i] = new ObjectReferenceKeyframe
|
|
1430
|
+
{
|
|
1431
|
+
time = currentTime,
|
|
1432
|
+
value = definition.SpritesToAnimate[i],
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1435
|
+
if (i < definition.SpritesToAnimate.Count - 1)
|
|
1436
|
+
{
|
|
1437
|
+
float normalizedTimeForCurve =
|
|
1438
|
+
definition.SpritesToAnimate.Count > 1
|
|
1439
|
+
? (float)i / (definition.SpritesToAnimate.Count - 1)
|
|
1440
|
+
: 0;
|
|
1441
|
+
float timeForCurveEval = normalizedTimeForCurve * curveDuration;
|
|
1442
|
+
|
|
1443
|
+
float fps = curve.Evaluate(timeForCurveEval);
|
|
1444
|
+
if (fps <= 0)
|
|
1445
|
+
{
|
|
1446
|
+
fps = definition.DefaultFrameRate;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
if (fps <= 0)
|
|
1450
|
+
{
|
|
1451
|
+
fps = 1;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
currentTime += 1.0f / fps;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
AnimationUtility.SetObjectReferenceCurve(clip, spriteBinding, keyframes);
|
|
1459
|
+
|
|
1460
|
+
AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clip);
|
|
1461
|
+
settings.loopTime = definition.loop;
|
|
1462
|
+
settings.cycleOffset = definition.cycleOffset;
|
|
1463
|
+
AnimationUtility.SetAnimationClipSettings(clip, settings);
|
|
1464
|
+
|
|
1465
|
+
string animName = string.IsNullOrEmpty(definition.Name)
|
|
1466
|
+
? "UnnamedAnim"
|
|
1467
|
+
: definition.Name;
|
|
1468
|
+
|
|
1469
|
+
animName = Path.GetInvalidFileNameChars()
|
|
1470
|
+
.Aggregate(animName, (current, character) => current.Replace(character, '_'));
|
|
1471
|
+
string assetPath = Path.Combine(animationsFolder, $"{animName}.anim");
|
|
1472
|
+
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
|
1473
|
+
|
|
1474
|
+
AssetDatabase.CreateAsset(clip, assetPath);
|
|
1475
|
+
createdCount++;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
if (createdCount > 0)
|
|
1479
|
+
{
|
|
1480
|
+
AssetDatabase.SaveAssets();
|
|
1481
|
+
AssetDatabase.Refresh();
|
|
1482
|
+
EditorUtility.DisplayDialog(
|
|
1483
|
+
"Success",
|
|
1484
|
+
$"{createdCount} animation(s) created in:\n{animationsFolder}",
|
|
1485
|
+
"OK"
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
else
|
|
1489
|
+
{
|
|
1490
|
+
EditorUtility.DisplayDialog(
|
|
1491
|
+
"Finished",
|
|
1492
|
+
"No valid animations were generated.",
|
|
1493
|
+
"OK"
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
private static void OnRootDragUpdated(DragUpdatedEvent evt)
|
|
1499
|
+
{
|
|
1500
|
+
if (DragAndDrop.objectReferences.Any(obj => obj is Texture2D))
|
|
1501
|
+
{
|
|
1502
|
+
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
private void OnRootDragPerform(DragPerformEvent evt)
|
|
1507
|
+
{
|
|
1508
|
+
Texture2D draggedTexture =
|
|
1509
|
+
DragAndDrop.objectReferences.FirstOrDefault(obj => obj is Texture2D) as Texture2D;
|
|
1510
|
+
if (draggedTexture != null)
|
|
1511
|
+
{
|
|
1512
|
+
_spriteSheetField.value = draggedTexture;
|
|
1513
|
+
DragAndDrop.AcceptDrag();
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
public void OnBecameVisible()
|
|
1518
|
+
{
|
|
1519
|
+
rootVisualElement.RegisterCallback<DragUpdatedEvent>(OnRootDragUpdated);
|
|
1520
|
+
rootVisualElement.RegisterCallback<DragPerformEvent>(OnRootDragPerform);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
public void OnBecameInvisible()
|
|
1524
|
+
{
|
|
1525
|
+
rootVisualElement.UnregisterCallback<DragUpdatedEvent>(OnRootDragUpdated);
|
|
1526
|
+
rootVisualElement.UnregisterCallback<DragPerformEvent>(OnRootDragPerform);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
#endif
|
|
1530
|
+
}
|