com.wallstop-studios.unity-helpers 2.0.0-rc69 → 2.0.0-rc71

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.
Files changed (45) hide show
  1. package/Editor/AnimationCopier.cs +875 -93
  2. package/Editor/AnimationCreator.cs +840 -137
  3. package/Editor/AnimationEventEditor.cs +4 -4
  4. package/Editor/AnimatorControllerCopier.cs +3 -3
  5. package/Editor/Extensions/UnityExtensions.cs +26 -0
  6. package/Editor/Extensions/UnityExtensions.cs.meta +3 -0
  7. package/Editor/Extensions.meta +3 -0
  8. package/Editor/FitTextureSizeWindow.cs +371 -0
  9. package/Editor/PrefabChecker.cs +722 -0
  10. package/Editor/SpriteAtlasGenerator.cs +598 -0
  11. package/Editor/SpriteAtlasGenerator.cs.meta +3 -0
  12. package/Editor/SpriteCropper.cs +407 -0
  13. package/Editor/SpriteCropper.cs.meta +3 -0
  14. package/Editor/SpriteSettingsApplier.cs +756 -92
  15. package/Editor/TextureResizerWizard.cs +3 -3
  16. package/Editor/TextureSettingsApplier.cs +9 -9
  17. package/Editor/WShowIfPropertyDrawer.cs +2 -2
  18. package/Runtime/Core/Attributes/EnumDisplayNameAttribute.cs +15 -0
  19. package/Runtime/Core/Attributes/EnumDisplayNameAttribute.cs.meta +3 -0
  20. package/Runtime/Core/Extension/EnumExtensions.cs +176 -1
  21. package/Runtime/Core/Extension/UnityExtensions.cs +1 -1
  22. package/Runtime/Tags/AttributeUtilities.cs +8 -7
  23. package/Runtime/Tags/EffectHandler.cs +2 -1
  24. package/Tests/Runtime/DataStructures/BalancedKDTreeTests.cs +1 -1
  25. package/Tests/Runtime/DataStructures/CyclicBufferTests.cs +1 -1
  26. package/Tests/Runtime/DataStructures/QuadTreeTests.cs +1 -1
  27. package/Tests/Runtime/DataStructures/UnbalancedKDTreeTests.cs +1 -1
  28. package/Tests/Runtime/Extensions/DictionaryExtensionTests.cs +1 -1
  29. package/Tests/Runtime/Extensions/EnumExtensionTests.cs +1 -1
  30. package/Tests/Runtime/Extensions/IListExtensionTests.cs +1 -1
  31. package/Tests/Runtime/Extensions/LoggingExtensionTests.cs +1 -1
  32. package/Tests/Runtime/Extensions/RandomExtensionTests.cs +1 -1
  33. package/Tests/Runtime/Extensions/StringExtensionTests.cs +1 -1
  34. package/Tests/Runtime/Helper/WallMathTests.cs +1 -1
  35. package/Tests/Runtime/Performance/KDTreePerformanceTests.cs +1 -1
  36. package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs +1 -1
  37. package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs +1 -1
  38. package/Tests/Runtime/Performance/UnbalancedKDTreeTests.cs +1 -1
  39. package/Tests/Runtime/Random/RandomTestBase.cs +2 -2
  40. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +1 -1
  41. package/package.json +1 -1
  42. package/Editor/FitTextureSizeWizard.cs +0 -147
  43. package/Editor/PrefabCheckWizard.cs +0 -167
  44. /package/Editor/{FitTextureSizeWizard.cs.meta → FitTextureSizeWindow.cs.meta} +0 -0
  45. /package/Editor/{PrefabCheckWizard.cs.meta → PrefabChecker.cs.meta} +0 -0
@@ -1,15 +1,15 @@
1
- namespace WallstopStudios.UnityHelpers.Editor
1
+ // ReSharper disable CompareOfFloatsByEqualityOperator
2
+ namespace WallstopStudios.UnityHelpers.Editor
2
3
  {
3
4
  #if UNITY_EDITOR
5
+ using Core.Attributes;
6
+ using Core.Extension;
4
7
  using System;
5
8
  using System.Collections.Generic;
6
9
  using System.IO;
7
10
  using System.Linq;
8
11
  using UnityEditor;
9
12
  using UnityEngine;
10
- using Core.Attributes;
11
- using Core.Extension;
12
- using Core.Helper;
13
13
  using Object = UnityEngine.Object;
14
14
 
15
15
  [Serializable]
@@ -66,120 +66,658 @@
66
66
  [WShowIf(nameof(applyCrunchCompression))]
67
67
  public bool useCrunchCompression;
68
68
 
69
+ public bool applyCompression;
70
+
71
+ [WShowIf(nameof(applyCompression))]
72
+ [SerializeField]
73
+ public TextureImporterCompression compressionLevel = TextureImporterCompression.Compressed;
74
+
69
75
  public string name = string.Empty;
70
76
  }
71
77
 
72
- public sealed class SpriteSettingsApplier : ScriptableWizard
78
+ [CustomPropertyDrawer(typeof(SpriteSettings))]
79
+ public class SpriteSettingsDrawer : PropertyDrawer
80
+ {
81
+ private const float CheckboxWidth = 18f;
82
+ private const float HorizontalSpacing = 5f;
83
+
84
+ private readonly (string apply, string val, string label)[] _settingPairs =
85
+ {
86
+ (
87
+ nameof(SpriteSettings.applyPixelsPerUnit),
88
+ nameof(SpriteSettings.pixelsPerUnit),
89
+ "Pixels Per Unit"
90
+ ),
91
+ (nameof(SpriteSettings.applyPivot), nameof(SpriteSettings.pivot), "Pivot"),
92
+ (
93
+ nameof(SpriteSettings.applySpriteMode),
94
+ nameof(SpriteSettings.spriteMode),
95
+ "Sprite Mode"
96
+ ),
97
+ (
98
+ nameof(SpriteSettings.applyGenerateMipMaps),
99
+ nameof(SpriteSettings.generateMipMaps),
100
+ "Generate Mip Maps"
101
+ ),
102
+ (
103
+ nameof(SpriteSettings.applyAlphaIsTransparency),
104
+ nameof(SpriteSettings.alphaIsTransparency),
105
+ "Alpha Is Transparency"
106
+ ),
107
+ (
108
+ nameof(SpriteSettings.applyReadWriteEnabled),
109
+ nameof(SpriteSettings.readWriteEnabled),
110
+ "Read/Write Enabled"
111
+ ),
112
+ (
113
+ nameof(SpriteSettings.applyExtrudeEdges),
114
+ nameof(SpriteSettings.extrudeEdges),
115
+ "Extrude Edges"
116
+ ),
117
+ (nameof(SpriteSettings.applyWrapMode), nameof(SpriteSettings.wrapMode), "Wrap Mode"),
118
+ (
119
+ nameof(SpriteSettings.applyFilterMode),
120
+ nameof(SpriteSettings.filterMode),
121
+ "Filter Mode"
122
+ ),
123
+ (
124
+ nameof(SpriteSettings.applyCrunchCompression),
125
+ nameof(SpriteSettings.useCrunchCompression),
126
+ "Use Crunch Compression"
127
+ ),
128
+ (
129
+ nameof(SpriteSettings.applyCompression),
130
+ nameof(SpriteSettings.compressionLevel),
131
+ "Compression Level"
132
+ ),
133
+ };
134
+
135
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
136
+ {
137
+ EditorGUI.BeginProperty(position, label, property);
138
+
139
+ SerializedProperty nameProp = property.FindPropertyRelative(
140
+ nameof(SpriteSettings.name)
141
+ );
142
+
143
+ Rect currentRect = new(
144
+ position.x,
145
+ position.y,
146
+ position.width,
147
+ EditorGUIUtility.singleLineHeight
148
+ );
149
+ EditorGUI.PropertyField(
150
+ currentRect,
151
+ nameProp,
152
+ new GUIContent("Profile Name (Optional)")
153
+ );
154
+
155
+ currentRect.y +=
156
+ EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
157
+
158
+ foreach ((string apply, string val, string label) pair in _settingPairs)
159
+ {
160
+ SerializedProperty applyProp = property.FindPropertyRelative(pair.apply);
161
+ SerializedProperty valueProp = property.FindPropertyRelative(pair.val);
162
+
163
+ if (applyProp == null || valueProp == null)
164
+ {
165
+ EditorGUI.LabelField(currentRect, $"Error finding properties for {pair.label}");
166
+ currentRect.y +=
167
+ EditorGUIUtility.singleLineHeight
168
+ + EditorGUIUtility.standardVerticalSpacing;
169
+ continue;
170
+ }
171
+
172
+ float labelLineHeight = EditorGUIUtility.singleLineHeight;
173
+ Rect labelLineRect = new(
174
+ position.x,
175
+ currentRect.y,
176
+ position.width,
177
+ labelLineHeight
178
+ );
179
+ Rect checkboxRect = new(
180
+ labelLineRect.x + labelLineRect.width - CheckboxWidth,
181
+ labelLineRect.y,
182
+ CheckboxWidth,
183
+ labelLineHeight
184
+ );
185
+ Rect labelRect = new(
186
+ labelLineRect.x,
187
+ labelLineRect.y,
188
+ labelLineRect.width - CheckboxWidth - HorizontalSpacing,
189
+ labelLineHeight
190
+ );
191
+
192
+ EditorGUI.LabelField(labelRect, pair.label);
193
+ EditorGUI.PropertyField(checkboxRect, applyProp, GUIContent.none);
194
+ currentRect.y += labelLineHeight + EditorGUIUtility.standardVerticalSpacing;
195
+
196
+ if (applyProp.boolValue)
197
+ {
198
+ float valuePropHeight = EditorGUI.GetPropertyHeight(valueProp, true);
199
+ Rect valueRect = new(
200
+ position.x,
201
+ currentRect.y,
202
+ position.width,
203
+ valuePropHeight
204
+ );
205
+
206
+ EditorGUI.indentLevel++;
207
+ EditorGUI.PropertyField(valueRect, valueProp, GUIContent.none, true);
208
+ EditorGUI.indentLevel--;
209
+
210
+ currentRect.y += valueRect.height + EditorGUIUtility.standardVerticalSpacing;
211
+ }
212
+ }
213
+
214
+ EditorGUI.EndProperty();
215
+ }
216
+
217
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
218
+ {
219
+ float totalHeight = 0f;
220
+
221
+ SerializedProperty nameProp = property.FindPropertyRelative(
222
+ nameof(SpriteSettings.name)
223
+ );
224
+ if (nameProp != null)
225
+ {
226
+ totalHeight +=
227
+ EditorGUI.GetPropertyHeight(nameProp)
228
+ + EditorGUIUtility.standardVerticalSpacing;
229
+ }
230
+
231
+ foreach ((string apply, string val, string label) pair in _settingPairs)
232
+ {
233
+ SerializedProperty applyProp = property.FindPropertyRelative(pair.apply);
234
+ SerializedProperty valueProp = property.FindPropertyRelative(pair.val);
235
+
236
+ if (applyProp == null || valueProp == null)
237
+ {
238
+ continue;
239
+ }
240
+
241
+ totalHeight +=
242
+ EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
243
+
244
+ if (applyProp.boolValue)
245
+ {
246
+ totalHeight +=
247
+ EditorGUI.GetPropertyHeight(valueProp, true)
248
+ + EditorGUIUtility.standardVerticalSpacing;
249
+ }
250
+ }
251
+
252
+ if (totalHeight > 0)
253
+ {
254
+ totalHeight -= EditorGUIUtility.standardVerticalSpacing;
255
+ }
256
+
257
+ return totalHeight;
258
+ }
259
+ }
260
+
261
+ public sealed class SpriteSettingsApplierWindow : EditorWindow
73
262
  {
74
263
  public List<Sprite> sprites = new();
75
264
  public List<string> spriteFileExtensions = new() { ".png" };
76
-
77
- [Tooltip(
78
- "Drag various sprite settings here, where the name property matches a sprite asset name. The first settings with an empty or matching name will be applied to each and every sprite."
79
- )]
80
265
  public List<SpriteSettings> spriteSettings = new() { new SpriteSettings() };
81
-
82
- [Tooltip(
83
- "Drag a folder from Unity here to apply the configuration to all settings under it. No sprites are modified if no directories are provided."
84
- )]
85
266
  public List<Object> directories = new();
86
267
 
87
- [MenuItem("Tools/Unity Helpers/Sprite Settings Applier", priority = -2)]
88
- public static void CreateAnimation()
268
+ private SerializedObject _serializedObject;
269
+ private SerializedProperty _spritesProp;
270
+ private SerializedProperty _spriteFileExtensionsProp;
271
+ private SerializedProperty _spriteSettingsProp;
272
+ private SerializedProperty _directoriesProp;
273
+
274
+ private Vector2 _scrollPosition;
275
+ private int _totalSpritesToProcess = -1;
276
+ private int _spritesThatWillChange = -1;
277
+
278
+ [MenuItem("Tools/Wallstop Studios/Unity Helpers/Sprite Settings Applier", priority = -2)]
279
+ public static void ShowWindow()
280
+ {
281
+ SpriteSettingsApplierWindow window = GetWindow<SpriteSettingsApplierWindow>(
282
+ "Sprite Settings Applier"
283
+ );
284
+ window.minSize = new Vector2(400, 300);
285
+ window.Show();
286
+ }
287
+
288
+ private void OnEnable()
289
+ {
290
+ _serializedObject = new SerializedObject(this);
291
+ _spritesProp = _serializedObject.FindProperty(nameof(sprites));
292
+ _spriteFileExtensionsProp = _serializedObject.FindProperty(
293
+ nameof(spriteFileExtensions)
294
+ );
295
+ _spriteSettingsProp = _serializedObject.FindProperty(nameof(spriteSettings));
296
+ _directoriesProp = _serializedObject.FindProperty(nameof(directories));
297
+ }
298
+
299
+ private void OnGUI()
300
+ {
301
+ _serializedObject.Update();
302
+
303
+ _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
304
+
305
+ EditorGUILayout.LabelField("Sprite Sources", EditorStyles.boldLabel);
306
+ EditorGUILayout.PropertyField(_spritesProp, new GUIContent("Specific Sprites"), true);
307
+
308
+ EditorGUILayout.Space();
309
+
310
+ EditorGUILayout.LabelField("Directory Sources", EditorStyles.boldLabel);
311
+ EditorGUILayout.PropertyField(
312
+ _directoriesProp,
313
+ new GUIContent("Scan Directories"),
314
+ true
315
+ );
316
+ if (GUILayout.Button("Add Directory via Browser"))
317
+ {
318
+ AddDirectory();
319
+ }
320
+
321
+ EditorGUILayout.Space();
322
+ EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
323
+ EditorGUILayout.PropertyField(
324
+ _spriteFileExtensionsProp,
325
+ new GUIContent("Sprite File Extensions"),
326
+ true
327
+ );
328
+ EditorGUILayout.PropertyField(
329
+ _spriteSettingsProp,
330
+ new GUIContent("Sprite Settings Profiles"),
331
+ true
332
+ );
333
+
334
+ EditorGUILayout.Space();
335
+ EditorGUILayout.LabelField("Actions", EditorStyles.boldLabel);
336
+
337
+ if (GUILayout.Button("Calculate Stats"))
338
+ {
339
+ CalculateStats();
340
+ }
341
+
342
+ if (_totalSpritesToProcess >= 0 && _spritesThatWillChange >= 0)
343
+ {
344
+ EditorGUILayout.LabelField($"Sprites to process: {_totalSpritesToProcess}");
345
+ EditorGUILayout.LabelField($"Sprites that will change: {_spritesThatWillChange}");
346
+ }
347
+ else
348
+ {
349
+ EditorGUILayout.LabelField("Press 'Calculate Stats' to see processing details.");
350
+ }
351
+
352
+ EditorGUILayout.Space();
353
+
354
+ if (GUILayout.Button("Apply Settings to Sprites"))
355
+ {
356
+ ApplySettings();
357
+ }
358
+
359
+ EditorGUILayout.EndScrollView();
360
+
361
+ _serializedObject.ApplyModifiedProperties();
362
+ }
363
+
364
+ private void AddDirectory()
89
365
  {
90
- _ = DisplayWizard<SpriteSettingsApplier>("Sprite Settings Directory Applier", "Set");
366
+ string path = EditorUtility.OpenFolderPanel("Select Directory", "Assets", "");
367
+ if (string.IsNullOrWhiteSpace(path))
368
+ {
369
+ return;
370
+ }
371
+
372
+ if (path.StartsWith(Application.dataPath, StringComparison.Ordinal))
373
+ {
374
+ string relativePath = "Assets" + path.Substring(Application.dataPath.Length);
375
+ Object folderAsset = AssetDatabase.LoadAssetAtPath<Object>(relativePath);
376
+ if (folderAsset != null)
377
+ {
378
+ _directoriesProp.serializedObject.Update();
379
+ bool alreadyExists = false;
380
+ for (int i = 0; i < _directoriesProp.arraySize; i++)
381
+ {
382
+ if (
383
+ _directoriesProp.GetArrayElementAtIndex(i).objectReferenceValue
384
+ == folderAsset
385
+ )
386
+ {
387
+ alreadyExists = true;
388
+ break;
389
+ }
390
+ }
391
+
392
+ if (!alreadyExists)
393
+ {
394
+ int newIndex = _directoriesProp.arraySize;
395
+ _directoriesProp.InsertArrayElementAtIndex(newIndex);
396
+ _directoriesProp.GetArrayElementAtIndex(newIndex).objectReferenceValue =
397
+ folderAsset;
398
+ this.Log($"Added directory: {relativePath}");
399
+ }
400
+ else
401
+ {
402
+ this.LogWarn($"Directory already in list: {relativePath}");
403
+ }
404
+ _directoriesProp.serializedObject.ApplyModifiedProperties();
405
+ }
406
+ else
407
+ {
408
+ this.LogError(
409
+ $"Could not load asset at path: {relativePath}. Is it a valid folder within Assets?"
410
+ );
411
+ }
412
+ }
413
+ else
414
+ {
415
+ this.LogError(
416
+ $"Selected folder must be inside the project's Assets folder. Path selected: {path}"
417
+ );
418
+ }
91
419
  }
92
420
 
93
- private void OnWizardCreate()
421
+ private List<(string fullFilePath, string relativePath)> GetTargetSpritePaths()
94
422
  {
95
- HashSet<string> uniqueDirectories = new(StringComparer.OrdinalIgnoreCase);
423
+ HashSet<string> uniqueRelativePaths = new(StringComparer.OrdinalIgnoreCase);
424
+ List<Object> validDirectories = new();
425
+
426
+ for (int i = 0; i < _directoriesProp.arraySize; i++)
427
+ {
428
+ Object dir = _directoriesProp.GetArrayElementAtIndex(i).objectReferenceValue;
429
+ if (dir != null)
430
+ {
431
+ validDirectories.Add(dir);
432
+ }
433
+ }
434
+
435
+ HashSet<string> uniqueDirectoryPaths = new(StringComparer.OrdinalIgnoreCase);
96
436
  foreach (
97
- string assetPath in directories
98
- .Where(Objects.NotNull)
437
+ string assetPath in validDirectories
99
438
  .Select(AssetDatabase.GetAssetPath)
100
439
  .Where(assetPath => !string.IsNullOrWhiteSpace(assetPath))
101
440
  )
102
441
  {
103
- if (Directory.Exists(assetPath))
442
+ if (AssetDatabase.IsValidFolder(assetPath))
443
+ {
444
+ _ = uniqueDirectoryPaths.Add(assetPath);
445
+ }
446
+ else
104
447
  {
105
- _ = uniqueDirectories.Add(assetPath);
448
+ this.LogWarn($"Item '{assetPath}' is not a valid directory. Skipping.");
106
449
  }
107
450
  }
108
451
 
109
- HashSet<string> processedSpritePaths = new(StringComparer.OrdinalIgnoreCase);
110
- Queue<string> directoriesToCheck = new(uniqueDirectories);
111
- int spriteCount = 0;
112
- while (directoriesToCheck.TryDequeue(out string directoryPath))
452
+ List<(string fullFilePath, string relativePath)> filePaths = new();
453
+ Queue<string> directoriesToCheck = new(uniqueDirectoryPaths);
454
+ HashSet<string> processedFullPaths = new(StringComparer.OrdinalIgnoreCase);
455
+
456
+ while (directoriesToCheck.TryDequeue(out string relativeDirectoryPath))
113
457
  {
114
- foreach (string fullFilePath in Directory.EnumerateFiles(directoryPath))
458
+ string fullDirectoryPath = Path.GetFullPath(relativeDirectoryPath);
459
+
460
+ if (!Directory.Exists(fullDirectoryPath))
115
461
  {
116
- if (
117
- !spriteFileExtensions.Contains(
118
- Path.GetExtension(fullFilePath),
119
- StringComparer.OrdinalIgnoreCase
462
+ this.LogWarn($"Directory path does not exist: {fullDirectoryPath}. Skipping.");
463
+ continue;
464
+ }
465
+
466
+ try
467
+ {
468
+ foreach (string fullFilePath in Directory.EnumerateFiles(fullDirectoryPath))
469
+ {
470
+ string fileExtension = Path.GetExtension(fullFilePath);
471
+ bool extensionMatch = false;
472
+ for (int i = 0; i < _spriteFileExtensionsProp.arraySize; i++)
473
+ {
474
+ if (
475
+ string.Equals(
476
+ fileExtension,
477
+ _spriteFileExtensionsProp.GetArrayElementAtIndex(i).stringValue,
478
+ StringComparison.OrdinalIgnoreCase
479
+ )
480
+ )
481
+ {
482
+ extensionMatch = true;
483
+ break;
484
+ }
485
+ }
486
+
487
+ if (!extensionMatch)
488
+ {
489
+ continue;
490
+ }
491
+
492
+ if (processedFullPaths.Add(fullFilePath))
493
+ {
494
+ string relativeFilePath =
495
+ "Assets"
496
+ + fullFilePath
497
+ .Substring(Application.dataPath.Length)
498
+ .Replace("\\", "/");
499
+ if (uniqueRelativePaths.Add(relativeFilePath))
500
+ {
501
+ filePaths.Add((fullFilePath, relativeFilePath));
502
+ }
503
+ }
504
+ }
505
+
506
+ foreach (
507
+ string subDirectoryFullPath in Directory.EnumerateDirectories(
508
+ fullDirectoryPath
120
509
  )
121
510
  )
122
511
  {
123
- continue;
512
+ string relativeSubDirectory =
513
+ "Assets"
514
+ + subDirectoryFullPath
515
+ .Substring(Application.dataPath.Length)
516
+ .Replace("\\", "/");
517
+ directoriesToCheck.Enqueue(relativeSubDirectory);
124
518
  }
125
-
126
- int index = fullFilePath.LastIndexOf(
127
- directoryPath,
128
- StringComparison.OrdinalIgnoreCase
519
+ }
520
+ catch (Exception e)
521
+ {
522
+ this.LogError(
523
+ $"Error enumerating directory '{fullDirectoryPath}': {e.Message}"
129
524
  );
130
- if (index < 0)
525
+ }
526
+ }
527
+
528
+ for (int i = 0; i < _spritesProp.arraySize; i++)
529
+ {
530
+ Sprite sprite =
531
+ _spritesProp.GetArrayElementAtIndex(i).objectReferenceValue as Sprite;
532
+ if (sprite == null)
533
+ {
534
+ continue;
535
+ }
536
+
537
+ string relativePath = AssetDatabase.GetAssetPath(sprite);
538
+ if (!string.IsNullOrWhiteSpace(relativePath))
539
+ {
540
+ if (uniqueRelativePaths.Add(relativePath))
541
+ {
542
+ string fullPath = Path.GetFullPath(relativePath);
543
+ if (processedFullPaths.Add(fullPath))
544
+ {
545
+ filePaths.Add((fullPath, relativePath));
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ return filePaths;
552
+ }
553
+
554
+ private void CalculateStats()
555
+ {
556
+ _totalSpritesToProcess = 0;
557
+ _spritesThatWillChange = 0;
558
+ List<(string fullFilePath, string relativePath)> targetFiles = GetTargetSpritePaths();
559
+
560
+ _totalSpritesToProcess = targetFiles.Count;
561
+
562
+ List<SpriteSettings> currentSettings = new();
563
+ for (int i = 0; i < _spriteSettingsProp.arraySize; i++)
564
+ {
565
+ if (_serializedObject.targetObject is SpriteSettingsApplierWindow windowInstance)
566
+ {
567
+ if (i < windowInstance.spriteSettings.Count)
131
568
  {
132
- continue;
569
+ currentSettings.Add(windowInstance.spriteSettings[i]);
133
570
  }
571
+ }
572
+ }
573
+
574
+ if (currentSettings.Count != _spriteSettingsProp.arraySize)
575
+ {
576
+ this.LogWarn(
577
+ $"Mismatch between SerializedProperty size and actual list size for spriteSettings. Stats might be inaccurate."
578
+ );
579
+ }
580
+
581
+ for (int i = 0; i < targetFiles.Count; i++)
582
+ {
583
+ (string _, string relativePath) = targetFiles[i];
584
+ EditorUtility.DisplayProgressBar(
585
+ "Calculating Stats",
586
+ $"Checking '{Path.GetFileName(relativePath)}' ({i + 1}/{_totalSpritesToProcess})",
587
+ (float)(i + 1) / _totalSpritesToProcess
588
+ );
589
+
590
+ if (WillTextureSettingsChange(relativePath, currentSettings))
591
+ {
592
+ _spritesThatWillChange++;
593
+ }
594
+ }
595
+
596
+ EditorUtility.ClearProgressBar();
597
+ this.Log(
598
+ $"Calculation complete. Sprites to process: {_totalSpritesToProcess}, Sprites that will change: {_spritesThatWillChange}"
599
+ );
600
+ }
134
601
 
135
- string filePath = fullFilePath.Substring(index);
602
+ private void ApplySettings()
603
+ {
604
+ List<(string fullFilePath, string relativePath)> targetFiles = GetTargetSpritePaths();
605
+ int spriteCount = 0;
606
+ List<TextureImporter> updatedImporters = new();
607
+
608
+ List<SpriteSettings> currentSettings;
609
+ if (_serializedObject.targetObject is SpriteSettingsApplierWindow windowInstance)
610
+ {
611
+ currentSettings = windowInstance.spriteSettings;
612
+ }
613
+ else
614
+ {
615
+ this.LogError(
616
+ $"Cannot access spriteSettings list from target object. Aborting apply."
617
+ );
618
+ return;
619
+ }
620
+
621
+ if (targetFiles.Count == 0)
622
+ {
623
+ this.LogWarn($"No sprites found to process based on current configuration.");
624
+ return;
625
+ }
626
+
627
+ AssetDatabase.StartAssetEditing();
628
+ try
629
+ {
630
+ foreach ((string _, string filePath) in targetFiles)
631
+ {
136
632
  if (
137
- processedSpritePaths.Add(fullFilePath) && TryUpdateTextureSettings(filePath)
633
+ TryUpdateTextureSettings(
634
+ filePath,
635
+ currentSettings,
636
+ out TextureImporter textureImporter
637
+ )
138
638
  )
139
639
  {
140
- ++spriteCount;
640
+ if (textureImporter != null)
641
+ {
642
+ updatedImporters.Add(textureImporter);
643
+ ++spriteCount;
644
+ }
141
645
  }
142
646
  }
143
647
 
144
- foreach (string subDirectory in Directory.EnumerateDirectories(directoryPath))
648
+ for (int i = 0; i < updatedImporters.Count; i++)
145
649
  {
146
- int index = subDirectory.LastIndexOf(
147
- directoryPath,
148
- StringComparison.OrdinalIgnoreCase
650
+ TextureImporter importer = updatedImporters[i];
651
+ EditorUtility.DisplayProgressBar(
652
+ "Updating Sprite Settings",
653
+ $"Processing '{Path.GetFileName(importer.assetPath)}' ({i + 1}/{updatedImporters.Count})",
654
+ (float)(i + 1) / updatedImporters.Count
149
655
  );
150
- if (index < 0)
656
+ try
151
657
  {
152
- continue;
658
+ importer.SaveAndReimport();
153
659
  }
154
-
155
- directoriesToCheck.Enqueue(subDirectory.Substring(index));
660
+ catch (Exception ex)
661
+ {
662
+ this.LogError(
663
+ $"Failed to save and reimport asset '{importer.assetPath}': {ex.Message}"
664
+ );
665
+ }
666
+ }
667
+ }
668
+ finally
669
+ {
670
+ AssetDatabase.StopAssetEditing();
671
+ EditorUtility.ClearProgressBar();
672
+ this.Log($"Processed {spriteCount} sprites.");
673
+ if (0 < spriteCount)
674
+ {
675
+ AssetDatabase.SaveAssets();
676
+ AssetDatabase.Refresh();
677
+ this.Log($"Asset database saved and refreshed.");
678
+ }
679
+ else
680
+ {
681
+ this.Log($"No sprites required changes.");
156
682
  }
683
+
684
+ _totalSpritesToProcess = -1;
685
+ _spritesThatWillChange = -1;
157
686
  }
687
+ }
158
688
 
159
- foreach (
160
- string filePath in sprites
161
- .Where(Objects.NotNull)
162
- .Select(AssetDatabase.GetAssetPath)
163
- .Where(path => !string.IsNullOrWhiteSpace(path))
164
- )
689
+ private static SpriteSettings FindMatchingSettings(
690
+ string filePath,
691
+ List<SpriteSettings> settingsProfiles
692
+ )
693
+ {
694
+ string fileName = Path.GetFileName(filePath);
695
+
696
+ foreach (SpriteSettings settings in settingsProfiles)
165
697
  {
166
698
  if (
167
- processedSpritePaths.Add(Application.dataPath + filePath)
168
- && TryUpdateTextureSettings(filePath)
699
+ !string.IsNullOrWhiteSpace(settings.name)
700
+ && fileName.Contains(settings.name, StringComparison.OrdinalIgnoreCase)
169
701
  )
170
702
  {
171
- ++spriteCount;
703
+ return settings;
172
704
  }
173
705
  }
174
706
 
175
- this.Log($"Processed {spriteCount} sprites.");
176
- if (0 < spriteCount)
707
+ foreach (SpriteSettings settings in settingsProfiles)
177
708
  {
178
- AssetDatabase.Refresh();
709
+ if (string.IsNullOrWhiteSpace(settings.name))
710
+ {
711
+ return settings;
712
+ }
179
713
  }
714
+ return null;
180
715
  }
181
716
 
182
- private bool TryUpdateTextureSettings(string filePath)
717
+ private bool WillTextureSettingsChange(
718
+ string filePath,
719
+ List<SpriteSettings> settingsProfiles
720
+ )
183
721
  {
184
722
  if (string.IsNullOrWhiteSpace(filePath))
185
723
  {
@@ -189,100 +727,226 @@
189
727
  TextureImporter textureImporter = AssetImporter.GetAtPath(filePath) as TextureImporter;
190
728
  if (textureImporter == null)
191
729
  {
730
+ this.LogWarn($"Could not get TextureImporter for asset: {filePath}");
192
731
  return false;
193
732
  }
194
733
 
195
- SpriteSettings spriteData = spriteSettings.Find(settings =>
196
- string.IsNullOrWhiteSpace(settings.name)
197
- || filePath.Contains(settings.name, StringComparison.OrdinalIgnoreCase)
198
- );
734
+ SpriteSettings spriteData = FindMatchingSettings(filePath, settingsProfiles);
199
735
  if (spriteData == null)
200
736
  {
737
+ this.LogWarn($"No matching SpriteSettings profile found for: {filePath}");
201
738
  return false;
202
739
  }
203
740
 
204
741
  bool changed = false;
205
742
  if (spriteData.applyPixelsPerUnit)
206
743
  {
207
- // ReSharper disable once CompareOfFloatsByEqualityOperator
208
744
  changed |= textureImporter.spritePixelsPerUnit != spriteData.pixelsPerUnit;
209
- textureImporter.spritePixelsPerUnit = spriteData.pixelsPerUnit;
210
745
  }
211
746
 
212
747
  if (spriteData.applyPivot)
213
748
  {
214
749
  changed |= textureImporter.spritePivot != spriteData.pivot;
215
- textureImporter.spritePivot = spriteData.pivot;
216
750
  }
217
751
 
218
752
  if (spriteData.applyGenerateMipMaps)
219
753
  {
220
754
  changed |= textureImporter.mipmapEnabled != spriteData.generateMipMaps;
221
- textureImporter.mipmapEnabled = spriteData.generateMipMaps;
222
755
  }
223
756
 
224
757
  if (spriteData.applyCrunchCompression)
225
758
  {
226
759
  changed |= textureImporter.crunchedCompression != spriteData.useCrunchCompression;
227
- textureImporter.crunchedCompression = spriteData.useCrunchCompression;
228
760
  }
229
761
 
230
- bool changedSettings = false;
762
+ if (spriteData.applyCompression)
763
+ {
764
+ changed |= textureImporter.textureCompression != spriteData.compressionLevel;
765
+ }
766
+
231
767
  TextureImporterSettings settings = new();
232
768
  textureImporter.ReadTextureSettings(settings);
233
769
  if (spriteData.applyPivot)
234
770
  {
235
- changedSettings |= settings.spriteAlignment != (int)SpriteAlignment.Custom;
236
- settings.spriteAlignment = (int)SpriteAlignment.Custom;
771
+ changed |= settings.spriteAlignment != (int)SpriteAlignment.Custom;
237
772
  }
238
773
 
239
774
  if (spriteData.applyAlphaIsTransparency)
240
775
  {
241
- changedSettings |= settings.alphaIsTransparency != spriteData.alphaIsTransparency;
242
- settings.alphaIsTransparency = spriteData.alphaIsTransparency;
776
+ changed |= settings.alphaIsTransparency != spriteData.alphaIsTransparency;
243
777
  }
244
778
 
245
779
  if (spriteData.applyReadWriteEnabled)
246
780
  {
247
- changedSettings |= settings.readable != spriteData.readWriteEnabled;
248
- settings.readable = spriteData.readWriteEnabled;
781
+ changed |= settings.readable != spriteData.readWriteEnabled;
249
782
  }
250
783
 
251
784
  if (spriteData.applySpriteMode)
252
785
  {
253
- changedSettings |= settings.spriteMode != (int)spriteData.spriteMode;
254
- settings.spriteMode = (int)spriteData.spriteMode;
786
+ changed |= settings.spriteMode != (int)spriteData.spriteMode;
255
787
  }
256
788
 
257
789
  if (spriteData.applyExtrudeEdges)
258
790
  {
259
- changedSettings |= settings.spriteExtrude != spriteData.extrudeEdges;
260
- settings.spriteExtrude = spriteData.extrudeEdges;
791
+ changed |= settings.spriteExtrude != spriteData.extrudeEdges;
261
792
  }
262
793
 
263
794
  if (spriteData.applyWrapMode)
264
795
  {
265
- changedSettings |= settings.wrapMode != spriteData.wrapMode;
266
- settings.wrapMode = spriteData.wrapMode;
796
+ changed |= settings.wrapMode != spriteData.wrapMode;
267
797
  }
268
798
 
269
799
  if (spriteData.applyFilterMode)
270
800
  {
271
- changedSettings |= settings.filterMode != spriteData.filterMode;
272
- settings.filterMode = spriteData.filterMode;
801
+ changed |= settings.filterMode != spriteData.filterMode;
273
802
  }
274
803
 
275
- if (changedSettings)
804
+ return changed;
805
+ }
806
+
807
+ private bool TryUpdateTextureSettings(
808
+ string filePath,
809
+ List<SpriteSettings> settingsProfiles,
810
+ out TextureImporter textureImporter
811
+ )
812
+ {
813
+ textureImporter = default;
814
+ if (string.IsNullOrWhiteSpace(filePath))
276
815
  {
277
- textureImporter.SetTextureSettings(settings);
816
+ return false;
817
+ }
818
+
819
+ textureImporter = AssetImporter.GetAtPath(filePath) as TextureImporter;
820
+ if (textureImporter == null)
821
+ {
822
+ this.LogWarn($"Could not get TextureImporter for asset: {filePath}");
823
+ return false;
278
824
  }
279
- changed |= changedSettings;
280
- if (changed)
825
+
826
+ SpriteSettings spriteData = FindMatchingSettings(filePath, settingsProfiles);
827
+
828
+ if (spriteData == null)
281
829
  {
282
- textureImporter.SaveAndReimport();
830
+ this.LogWarn($"No matching SpriteSettings profile found for: {filePath}");
831
+ return false;
283
832
  }
284
833
 
285
- return changed;
834
+ bool changed = false;
835
+ bool settingsChanged = false;
836
+ TextureImporterSettings settings = new();
837
+ textureImporter.ReadTextureSettings(settings);
838
+
839
+ if (spriteData.applyPixelsPerUnit)
840
+ {
841
+ if (textureImporter.spritePixelsPerUnit != spriteData.pixelsPerUnit)
842
+ {
843
+ textureImporter.spritePixelsPerUnit = spriteData.pixelsPerUnit;
844
+ changed = true;
845
+ }
846
+ }
847
+
848
+ if (spriteData.applyPivot)
849
+ {
850
+ if (textureImporter.spritePivot != spriteData.pivot)
851
+ {
852
+ textureImporter.spritePivot = spriteData.pivot;
853
+ changed = true;
854
+ }
855
+
856
+ if (settings.spriteAlignment != (int)SpriteAlignment.Custom)
857
+ {
858
+ settings.spriteAlignment = (int)SpriteAlignment.Custom;
859
+ settingsChanged = true;
860
+ }
861
+ }
862
+
863
+ if (spriteData.applyGenerateMipMaps)
864
+ {
865
+ if (textureImporter.mipmapEnabled != spriteData.generateMipMaps)
866
+ {
867
+ textureImporter.mipmapEnabled = spriteData.generateMipMaps;
868
+ changed = true;
869
+ }
870
+ }
871
+
872
+ if (spriteData.applyCrunchCompression)
873
+ {
874
+ if (textureImporter.crunchedCompression != spriteData.useCrunchCompression)
875
+ {
876
+ textureImporter.crunchedCompression = spriteData.useCrunchCompression;
877
+ changed = true;
878
+ }
879
+ }
880
+
881
+ if (spriteData.applyCompression)
882
+ {
883
+ if (textureImporter.textureCompression != spriteData.compressionLevel)
884
+ {
885
+ textureImporter.textureCompression = spriteData.compressionLevel;
886
+ changed = true;
887
+ }
888
+ }
889
+
890
+ if (spriteData.applyAlphaIsTransparency)
891
+ {
892
+ if (settings.alphaIsTransparency != spriteData.alphaIsTransparency)
893
+ {
894
+ settings.alphaIsTransparency = spriteData.alphaIsTransparency;
895
+ settingsChanged = true;
896
+ }
897
+ }
898
+
899
+ if (spriteData.applyReadWriteEnabled)
900
+ {
901
+ if (settings.readable != spriteData.readWriteEnabled)
902
+ {
903
+ settings.readable = spriteData.readWriteEnabled;
904
+ settingsChanged = true;
905
+ }
906
+ }
907
+
908
+ if (spriteData.applySpriteMode)
909
+ {
910
+ if (settings.spriteMode != (int)spriteData.spriteMode)
911
+ {
912
+ settings.spriteMode = (int)spriteData.spriteMode;
913
+ settingsChanged = true;
914
+ }
915
+ }
916
+
917
+ if (spriteData.applyExtrudeEdges)
918
+ {
919
+ if (settings.spriteExtrude != spriteData.extrudeEdges)
920
+ {
921
+ settings.spriteExtrude = spriteData.extrudeEdges;
922
+ settingsChanged = true;
923
+ }
924
+ }
925
+
926
+ if (spriteData.applyWrapMode)
927
+ {
928
+ if (settings.wrapMode != spriteData.wrapMode)
929
+ {
930
+ settings.wrapMode = spriteData.wrapMode;
931
+ settingsChanged = true;
932
+ }
933
+ }
934
+
935
+ if (spriteData.applyFilterMode)
936
+ {
937
+ if (settings.filterMode != spriteData.filterMode)
938
+ {
939
+ settings.filterMode = spriteData.filterMode;
940
+ settingsChanged = true;
941
+ }
942
+ }
943
+
944
+ if (settingsChanged)
945
+ {
946
+ textureImporter.SetTextureSettings(settings);
947
+ }
948
+
949
+ return changed || settingsChanged;
286
950
  }
287
951
  }
288
952
  #endif