com.wallstop-studios.unity-helpers 2.0.0 → 2.0.1

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 (133) hide show
  1. package/.github/workflows/format-on-demand.yml +2 -2
  2. package/.github/workflows/markdown-json.yml +1 -1
  3. package/.github/workflows/npm-publish.yml +1 -1
  4. package/.github/workflows/prettier-autofix.yml +4 -4
  5. package/.github/workflows/yaml-format-lint.yml +1 -1
  6. package/Docs/EFFECTS_SYSTEM.md +1316 -0
  7. package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
  8. package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +10 -8
  9. package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
  10. package/Docs/HELPER_UTILITIES.md +885 -0
  11. package/Docs/HELPER_UTILITIES.md.meta +7 -0
  12. package/{INDEX.md → Docs/INDEX.md} +107 -62
  13. package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
  14. package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
  15. package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +21 -3
  16. package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
  17. package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
  18. package/{SPATIAL_TREE_2D_PERFORMANCE.md → Docs/SPATIAL_TREE_2D_PERFORMANCE.md} +64 -64
  19. package/{SPATIAL_TREE_3D_PERFORMANCE.md → Docs/SPATIAL_TREE_3D_PERFORMANCE.md} +64 -64
  20. package/Docs/UTILITY_COMPONENTS.md +906 -0
  21. package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
  22. package/Docs/VISUAL_COMPONENTS.md +337 -0
  23. package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
  24. package/Editor/Sprites/AnimationCopier.cs +3 -3
  25. package/README.md +69 -62
  26. package/Runtime/AssemblyInfo.cs +2 -0
  27. package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
  28. package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
  29. package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
  30. package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
  31. package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
  32. package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
  33. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
  34. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
  35. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
  36. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
  37. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
  38. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
  39. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
  40. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
  41. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
  42. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
  43. package/Runtime/Integrations/Reflex.meta +8 -0
  44. package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -1
  45. package/Samples~/DI - Reflex/README.md +527 -0
  46. package/Samples~/DI - Reflex/README.md.meta +7 -0
  47. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
  48. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
  49. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
  50. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
  51. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
  52. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
  53. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
  54. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
  55. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
  56. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
  57. package/Samples~/DI - Reflex/Scripts.meta +8 -0
  58. package/Samples~/DI - Reflex.meta +8 -0
  59. package/Samples~/DI - VContainer/README.md +6 -5
  60. package/Samples~/DI - Zenject/README.md +6 -5
  61. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +29 -31
  62. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
  63. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
  64. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
  65. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
  66. package/Tests/Editor/Integrations/Reflex.meta +8 -0
  67. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +15 -16
  68. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +7 -13
  69. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +7 -11
  70. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +19 -21
  71. package/Tests/Editor/PersistentDirectorySettingsTests.cs +0 -1
  72. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +0 -1
  73. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -2
  74. package/Tests/Editor/Tools/ImageBlurToolTests.cs +1 -1
  75. package/Tests/Editor/Utils/CommonTestBase.cs +17 -0
  76. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +1 -1
  77. package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
  78. package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
  79. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
  80. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
  81. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
  82. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
  83. package/Tests/Runtime/Integrations/Reflex.meta +8 -0
  84. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +24 -29
  85. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +8 -3
  86. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +10 -20
  87. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
  88. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +1 -1
  89. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +1 -1
  90. package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
  91. package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
  92. package/Tests/Runtime/TestUtils/CommonTestBase.cs +11 -0
  93. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
  94. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
  95. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +4 -4
  96. package/Tests/TestUtils.meta +8 -0
  97. package/package.json +6 -1
  98. package/EFFECTS_SYSTEM.md +0 -242
  99. package/MATH_AND_EXTENSIONS.md +0 -316
  100. /package/{CHANGELOG.md → Docs/CHANGELOG.md} +0 -0
  101. /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
  102. /package/{CONTRIBUTING.md → Docs/CONTRIBUTING.md} +0 -0
  103. /package/{CONTRIBUTING.md.meta → Docs/CONTRIBUTING.md.meta} +0 -0
  104. /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
  105. /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
  106. /package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +0 -0
  107. /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
  108. /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
  109. /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
  110. /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
  111. /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
  112. /package/{HULLS.md → Docs/HULLS.md} +0 -0
  113. /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
  114. /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
  115. /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
  116. /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
  117. /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
  118. /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
  119. /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
  120. /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
  121. /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
  122. /package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +0 -0
  123. /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
  124. /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
  125. /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
  126. /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
  127. /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
  128. /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
  129. /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
  130. /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
  131. /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
  132. /package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +0 -0
  133. /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
@@ -0,0 +1,885 @@
1
+ # Helper Utilities Guide
2
+
3
+ ## TL;DR — Why Use These
4
+
5
+ Static helper classes and utilities that solve common programming problems without needing components on GameObjects. Use these for predictive aiming, path utilities, threading, hashing, formatting, and more.
6
+
7
+ ---
8
+
9
+ ## Contents
10
+
11
+ - [Gameplay Helpers](#gameplay-helpers) — Predictive aiming, spatial sampling, rotation
12
+ - [GameObject & Component Helpers](#gameobject--component-helpers) — Component discovery, hierarchy manipulation
13
+ - [Transform Helpers](#transform-helpers) — Hierarchy traversal
14
+ - [Threading](#threading) — Main thread dispatcher
15
+ - [Path & File Helpers](#path--file-helpers) — Path resolution, file operations
16
+ - [Scene Helpers](#scene-helpers) — Scene queries and loading
17
+ - [Advanced Utilities](#advanced-utilities) — Null checks, hashing, formatting
18
+
19
+ ---
20
+
21
+ <a id="gameplay-helpers"></a>
22
+
23
+ ## Gameplay Helpers
24
+
25
+ ### Predictive Aiming
26
+
27
+ **What it does:** Calculates where to aim when shooting at a moving target, accounting for projectile travel time.
28
+
29
+ **Problem it solves:** Shooting a bullet at where an enemy _is_ misses if they're moving. You need to aim at where they _will be_.
30
+
31
+ ```csharp
32
+ using WallstopStudios.UnityHelpers.Core.Helper;
33
+
34
+ Vector2 enemyPos = enemy.transform.position;
35
+ Vector2 enemyVelocity = enemy.GetComponent<Rigidbody2D>().velocity;
36
+ Vector2 turretPos = turret.transform.position;
37
+ float bulletSpeed = 20f;
38
+
39
+ Vector2? aimPosition = Helpers.PredictCurrentTarget(
40
+ enemyPos,
41
+ enemyVelocity,
42
+ turretPos,
43
+ bulletSpeed
44
+ );
45
+
46
+ if (aimPosition.HasValue)
47
+ {
48
+ // Aim at aimPosition to hit the moving target
49
+ Vector2 aimDirection = (aimPosition.Value - turretPos).normalized;
50
+ FireProjectile(aimDirection, bulletSpeed);
51
+ }
52
+ else
53
+ {
54
+ // Target is too fast, can't hit
55
+ }
56
+ ```
57
+
58
+ **When to use:**
59
+
60
+ - Turrets shooting at moving enemies
61
+ - AI aiming at moving players
62
+ - Predictive targeting systems
63
+ - Guided missiles
64
+
65
+ **When NOT to use:**
66
+
67
+ - Homing projectiles (use steering behaviors)
68
+ - Instant-hit weapons (use raycasts)
69
+ - Slow-moving or stationary targets (just aim directly)
70
+
71
+ ---
72
+
73
+ ### Spatial Sampling
74
+
75
+ **Get random points in circles/spheres:**
76
+
77
+ ```csharp
78
+ using WallstopStudios.UnityHelpers.Core.Helper;
79
+
80
+ // Random point inside circle (uniform distribution)
81
+ Vector2 spawnPoint = Helpers.GetRandomPointInCircle(center, radius);
82
+
83
+ // Random point inside sphere (uniform distribution)
84
+ Vector3 explosionPoint = Helpers.GetRandomPointInSphere(center, radius);
85
+ ```
86
+
87
+ **Use for:**
88
+
89
+ - Spawn points (enemies, pickups, particles)
90
+ - Explosion damage distribution
91
+ - Random movement destinations
92
+ - Scatter patterns
93
+
94
+ ---
95
+
96
+ ### Smooth Rotation Helpers
97
+
98
+ **Get rotation speed for smooth turning:**
99
+
100
+ ```csharp
101
+ using WallstopStudios.UnityHelpers.Core.Helper;
102
+
103
+ // Calculate how much to rotate this frame toward target
104
+ float currentAngle = transform.eulerAngles.z;
105
+ float targetAngle = GetTargetAngle();
106
+ float maxDegreesPerSecond = 180f;
107
+
108
+ float newAngle = Helpers.GetAngleWithSpeed(
109
+ currentAngle,
110
+ targetAngle,
111
+ maxDegreesPerSecond,
112
+ Time.deltaTime
113
+ );
114
+
115
+ transform.eulerAngles = new Vector3(0, 0, newAngle);
116
+ ```
117
+
118
+ **Handles:**
119
+
120
+ - Frame-rate independence
121
+ - Shortest rotation path (doesn't spin 270° when 90° is shorter)
122
+ - Angle wrapping (0-360°)
123
+
124
+ ---
125
+
126
+ ### Delayed Execution
127
+
128
+ **Execute code after delay or next frame:**
129
+
130
+ ```csharp
131
+ using WallstopStudios.UnityHelpers.Core.Helper;
132
+
133
+ // Execute after 2 seconds
134
+ Helpers.ExecuteFunctionAfterDelay(
135
+ monoBehaviour,
136
+ () => Debug.Log("Delayed!"),
137
+ delayInSeconds: 2f
138
+ );
139
+
140
+ // Execute next frame
141
+ Helpers.ExecuteFunctionNextFrame(
142
+ monoBehaviour,
143
+ () => Debug.Log("Next frame!")
144
+ );
145
+ ```
146
+
147
+ Uses coroutines under the hood.
148
+
149
+ ---
150
+
151
+ ### Repeating Execution with Jitter
152
+
153
+ **Run function repeatedly with random timing variance:**
154
+
155
+ ```csharp
156
+ using WallstopStudios.UnityHelpers.Core.Helper;
157
+
158
+ // Spawn enemy every 5-8 seconds
159
+ Helpers.StartFunctionAsCoroutine(
160
+ gameManager,
161
+ SpawnEnemy,
162
+ baseInterval: 5f,
163
+ intervalJitter: 3f // Random ±3 seconds
164
+ );
165
+
166
+ void SpawnEnemy()
167
+ {
168
+ Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
169
+ }
170
+ ```
171
+
172
+ **Use for:**
173
+
174
+ - Enemy spawning with variability
175
+ - Random event triggers
176
+ - Staggered updates to spread CPU load
177
+ - Natural-feeling timing
178
+
179
+ ---
180
+
181
+ ### Layer & Label Queries
182
+
183
+ ```csharp
184
+ using WallstopStudios.UnityHelpers.Core.Helper;
185
+
186
+ // Get all layer names (cached after first call)
187
+ string[] allLayers = Helpers.GetAllLayerNames();
188
+
189
+ // Get all sprite label names (editor only, cached)
190
+ string[] labels = Helpers.GetAllSpriteLabelNames();
191
+ ```
192
+
193
+ **Use for:**
194
+
195
+ - Populating dropdowns in editor tools
196
+ - Runtime layer/label validation
197
+ - Configuration systems
198
+
199
+ ---
200
+
201
+ ### Collider Syncing
202
+
203
+ **Update PolygonCollider2D to match sprite:**
204
+
205
+ ```csharp
206
+ using WallstopStudios.UnityHelpers.Core.Helper;
207
+
208
+ SpriteRenderer renderer = GetComponent<SpriteRenderer>();
209
+ PolygonCollider2D collider = GetComponent<PolygonCollider2D>();
210
+
211
+ Helpers.UpdateShapeToSprite(renderer, collider);
212
+ // Collider now matches sprite's physics shape
213
+ ```
214
+
215
+ ---
216
+
217
+ <a id="gameobject--component-helpers"></a>
218
+
219
+ ## GameObject & Component Helpers
220
+
221
+ ### Cached Component Lookup
222
+
223
+ **Fast tag-based component finding with caching:**
224
+
225
+ ```csharp
226
+ using WallstopStudios.UnityHelpers.Core.Helper;
227
+
228
+ // First call searches scene, subsequent calls use cache
229
+ Player player = Helpers.Find<Player>("Player");
230
+
231
+ // Clear cache manually if needed
232
+ Helpers.ClearInstance<Player>();
233
+
234
+ // Set cache manually (for dependency injection scenarios)
235
+ Helpers.SetInstance(playerInstance);
236
+ ```
237
+
238
+ **Performance:** First call = GameObject.FindWithTag, subsequent calls = O(1) dictionary lookup.
239
+
240
+ ---
241
+
242
+ ### Component Existence Checks
243
+
244
+ ```csharp
245
+ using WallstopStudios.UnityHelpers.Core.Helper;
246
+
247
+ // Check if component exists without allocating
248
+ bool hasRigidbody = Helpers.HasComponent<Rigidbody2D>(gameObject);
249
+
250
+ // Better than:
251
+ bool hasRigidbody = GetComponent<Rigidbody2D>() != null; // Allocates
252
+ ```
253
+
254
+ ---
255
+
256
+ ### Get-or-Add Pattern
257
+
258
+ ```csharp
259
+ using WallstopStudios.UnityHelpers.Core.Helper;
260
+
261
+ // Get existing component or add if missing
262
+ Rigidbody2D rb = Helpers.GetOrAddComponent<Rigidbody2D>(gameObject);
263
+ ```
264
+
265
+ ---
266
+
267
+ ### Hierarchical Enable/Disable
268
+
269
+ **Recursively enable/disable components:**
270
+
271
+ ```csharp
272
+ using WallstopStudios.UnityHelpers.Core.Helper;
273
+
274
+ // Enable all Collider2D components in children
275
+ Helpers.EnableRecursively<Collider2D>(rootObject, enable: true);
276
+
277
+ // Disable all renderers in hierarchy
278
+ Helpers.EnableRendererRecursively<SpriteRenderer>(rootObject, enable: false);
279
+ ```
280
+
281
+ **Use for:**
282
+
283
+ - Toggling collision for entire character rigs
284
+ - Hiding/showing complex prefabs
285
+ - Debug visualization toggles
286
+
287
+ ---
288
+
289
+ ### Bulk Child Destruction
290
+
291
+ ```csharp
292
+ using WallstopStudios.UnityHelpers.Core.Helper;
293
+
294
+ // Destroy all children (useful for clearing containers)
295
+ Helpers.DestroyAllChildrenGameObjects(parentTransform);
296
+ ```
297
+
298
+ **Use for:**
299
+
300
+ - Clearing inventory UI
301
+ - Resetting spawn containers
302
+ - Cleanup before repopulating
303
+
304
+ ---
305
+
306
+ ### Smart Destruction
307
+
308
+ **Editor/runtime aware destruction:**
309
+
310
+ ```csharp
311
+ using WallstopStudios.UnityHelpers.Core.Helper;
312
+
313
+ // Uses DestroyImmediate in editor, Destroy in play mode
314
+ Helpers.SmartDestroy(gameObject);
315
+
316
+ // Also handles assets correctly (won't destroy project assets)
317
+ ```
318
+
319
+ **Use in editor tools** to avoid "Destroying assets is not permitted" errors.
320
+
321
+ ---
322
+
323
+ ### Prefab Utilities
324
+
325
+ ```csharp
326
+ using WallstopStudios.UnityHelpers.Core.Helper;
327
+
328
+ // Check if GameObject is a prefab asset or instance
329
+ bool isPrefab = Helpers.IsPrefab(gameObject);
330
+
331
+ // Safely modify prefab (editor only)
332
+ #if UNITY_EDITOR
333
+ Helpers.ModifyAndSavePrefab(prefabAssetPath, prefab =>
334
+ {
335
+ // Modify prefab here
336
+ var component = prefab.AddComponent<MyComponent>();
337
+ component.value = 42;
338
+ // Changes saved automatically
339
+ });
340
+ #endif
341
+ ```
342
+
343
+ ---
344
+
345
+ <a id="transform-helpers"></a>
346
+
347
+ ## Transform Helpers
348
+
349
+ ### Hierarchy Traversal (Depth-First)
350
+
351
+ **Visit all children recursively:**
352
+
353
+ ```csharp
354
+ using WallstopStudios.UnityHelpers.Core.Helper;
355
+
356
+ // Depth-first traversal (visits deepest children first)
357
+ Helpers.IterateOverAllChildrenRecursively<SpriteRenderer>(rootTransform, renderer =>
358
+ {
359
+ renderer.color = Color.red;
360
+ });
361
+
362
+ // Buffered version (zero allocation)
363
+ using (var buffer = Buffers<Transform>.List.Get())
364
+ {
365
+ Helpers.IterateOverAllChildrenRecursively(rootTransform, buffer.Value);
366
+ foreach (Transform child in buffer.Value)
367
+ {
368
+ // Process children
369
+ }
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ ### Hierarchy Traversal (Breadth-First)
376
+
377
+ **Visit by depth level:**
378
+
379
+ ```csharp
380
+ using WallstopStudios.UnityHelpers.Core.Helper;
381
+
382
+ // Breadth-first traversal with depth limit
383
+ Helpers.IterateOverAllChildrenRecursivelyBreadthFirst(
384
+ rootTransform,
385
+ transform => Debug.Log(transform.name),
386
+ maxDepth: 3 // Only visit 3 levels deep
387
+ );
388
+ ```
389
+
390
+ **Use for:**
391
+
392
+ - Finding immediate area (not entire tree)
393
+ - Level-based operations
394
+ - Performance-sensitive searches
395
+
396
+ ---
397
+
398
+ ### Parent Traversal
399
+
400
+ **Walk up the hierarchy:**
401
+
402
+ ```csharp
403
+ using WallstopStudios.UnityHelpers.Core.Helper;
404
+
405
+ // Find component in parents
406
+ Helpers.IterateOverAllParentComponentsRecursively<Canvas>(transform, canvas =>
407
+ {
408
+ Debug.Log($"Found canvas: {canvas.name}");
409
+ });
410
+
411
+ // Get all parents (no component filter)
412
+ using (var buffer = Buffers<Transform>.List.Get())
413
+ {
414
+ Helpers.IterateOverAllParents(transform, buffer.Value);
415
+ // buffer contains all parent transforms up to root
416
+ }
417
+ ```
418
+
419
+ **Use for:**
420
+
421
+ - Finding UI Canvas parents
422
+ - Inheritance checking (is this under X?)
423
+ - Walking to root of hierarchy
424
+
425
+ ---
426
+
427
+ ### Direct Children/Parents
428
+
429
+ ```csharp
430
+ using WallstopStudios.UnityHelpers.Core.Helper;
431
+
432
+ // Get immediate children (non-recursive)
433
+ using (var buffer = Buffers<Transform>.List.Get())
434
+ {
435
+ Helpers.IterateOverAllChildren(transform, buffer.Value);
436
+ // Only direct children, no grandchildren
437
+ }
438
+ ```
439
+
440
+ ---
441
+
442
+ <a id="threading"></a>
443
+
444
+ ## Threading
445
+
446
+ ### UnityMainThreadDispatcher
447
+
448
+ **Execute code on Unity's main thread from background threads:**
449
+
450
+ **Problem it solves:** Unity APIs can only be called from the main thread. Background Tasks/threads can't directly manipulate GameObjects. This marshals callbacks back to the main thread.
451
+
452
+ ```csharp
453
+ using WallstopStudios.UnityHelpers.Core.Helper;
454
+ using System.Threading.Tasks;
455
+
456
+ async Task LoadDataInBackground()
457
+ {
458
+ // Background thread work
459
+ await Task.Run(() =>
460
+ {
461
+ // Expensive computation
462
+ var data = LoadFromDatabase();
463
+
464
+ // Need to update UI - marshal back to main thread
465
+ UnityMainThreadDispatcher.Instance.RunOnMainThread(() =>
466
+ {
467
+ // Safe to call Unity APIs here
468
+ uiText.text = data.ToString();
469
+ });
470
+ });
471
+ }
472
+ ```
473
+
474
+ **Async version with result:**
475
+
476
+ ```csharp
477
+ async Task<string> GetTextFromMainThread()
478
+ {
479
+ // Called from background thread, executes on main thread
480
+ string text = await UnityMainThreadDispatcher.Instance.Post(() =>
481
+ {
482
+ return uiText.text; // Safe to access Unity objects
483
+ });
484
+
485
+ return text;
486
+ }
487
+ ```
488
+
489
+ **Fire-and-forget on main thread:**
490
+
491
+ ```csharp
492
+ // From background thread
493
+ UnityMainThreadDispatcher.Instance.RunOnMainThread(() =>
494
+ {
495
+ Instantiate(prefab, position, rotation);
496
+ });
497
+ ```
498
+
499
+ **When to use:**
500
+
501
+ - Async file loading callbacks
502
+ - Network request callbacks
503
+ - Database query results
504
+ - Background computation results that update UI
505
+
506
+ **Important:**
507
+
508
+ - Works in both edit mode and play mode
509
+ - Actions queued during edit mode execute in next editor update
510
+ - Don't block the main thread with long operations
511
+
512
+ ---
513
+
514
+ <a id="path--file-helpers"></a>
515
+
516
+ ## Path & File Helpers
517
+
518
+ ### Path Sanitization
519
+
520
+ **Normalize path separators:**
521
+
522
+ ```csharp
523
+ using WallstopStudios.UnityHelpers.Core.Helper;
524
+
525
+ string windowsPath = @"Assets\Sprites\Player.png";
526
+ string unityPath = PathHelper.Sanitize(windowsPath);
527
+ // Result: "Assets/Sprites/Player.png"
528
+ ```
529
+
530
+ Unity prefers forward slashes. Use this for cross-platform paths.
531
+
532
+ ---
533
+
534
+ ### Directory Utilities
535
+
536
+ **Create directories safely:**
537
+
538
+ ```csharp
539
+ using WallstopStudios.UnityHelpers.Core.Helper;
540
+
541
+ #if UNITY_EDITOR
542
+ // Creates directory and updates AssetDatabase
543
+ DirectoryHelper.EnsureDirectoryExists("Assets/Generated/Data");
544
+ #endif
545
+ ```
546
+
547
+ **Find package root:**
548
+
549
+ ```csharp
550
+ // Walk hierarchy to find package.json
551
+ string packageRoot = DirectoryHelper.FindPackageRootPath();
552
+ // Returns path to package containing calling script
553
+ ```
554
+
555
+ **Use for:**
556
+
557
+ - Editor tools generating assets
558
+ - Finding package-relative paths
559
+ - Build scripts creating folders
560
+
561
+ ---
562
+
563
+ ### Path Conversion
564
+
565
+ **Convert between absolute and Unity-relative paths:**
566
+
567
+ ```csharp
568
+ using WallstopStudios.UnityHelpers.Core.Helper;
569
+
570
+ string absolute = "C:/Projects/MyGame/Assets/Textures/player.png";
571
+ string relative = DirectoryHelper.AbsoluteToUnityRelativePath(absolute);
572
+ // Result: "Assets/Textures/player.png"
573
+ ```
574
+
575
+ **Get calling script's directory:**
576
+
577
+ ```csharp
578
+ // Uses [CallerFilePath] magic
579
+ string scriptDir = DirectoryHelper.GetCallerScriptDirectory();
580
+ // Returns directory containing the calling .cs file
581
+ ```
582
+
583
+ ---
584
+
585
+ ### File Operations
586
+
587
+ **Initialize file if missing:**
588
+
589
+ ```csharp
590
+ using WallstopStudios.UnityHelpers.Core.Helper;
591
+
592
+ // Create config.json with default contents if it doesn't exist
593
+ FileHelper.InitializePath(
594
+ "Assets/config.json",
595
+ "{ \"version\": 1 }"
596
+ );
597
+ ```
598
+
599
+ **Async file copy:**
600
+
601
+ ```csharp
602
+ using System.Threading;
603
+
604
+ CancellationTokenSource cts = new CancellationTokenSource();
605
+
606
+ await FileHelper.CopyFileAsync(
607
+ "source.txt",
608
+ "destination.txt",
609
+ bufferSize: 81920, // 80KB buffer
610
+ cts.Token
611
+ );
612
+ ```
613
+
614
+ **Use for:**
615
+
616
+ - Large file operations without blocking
617
+ - Cancellable copy operations
618
+ - Streaming file operations
619
+
620
+ ---
621
+
622
+ <a id="scene-helpers"></a>
623
+
624
+ ## Scene Helpers
625
+
626
+ ### Scene Queries
627
+
628
+ **Check if scene is loaded:**
629
+
630
+ ```csharp
631
+ using WallstopStudios.UnityHelpers.Core.Helper;
632
+
633
+ bool loaded = SceneHelper.IsSceneLoaded("GameLevel");
634
+ // Checks by scene name or path
635
+ ```
636
+
637
+ **Get all scene paths (editor):**
638
+
639
+ ```csharp
640
+ #if UNITY_EDITOR
641
+ string[] allScenes = SceneHelper.GetAllScenePaths();
642
+ // Returns all .unity files in project
643
+
644
+ string[] buildScenes = SceneHelper.GetScenesInBuild();
645
+ // Returns only scenes in Build Settings
646
+ #endif
647
+ ```
648
+
649
+ ---
650
+
651
+ ### Temporary Scene Loading
652
+
653
+ **Load scene, extract data, auto-unload:**
654
+
655
+ ```csharp
656
+ using WallstopStudios.UnityHelpers.Core.Helper;
657
+
658
+ // RAII pattern - scene unloaded when disposed
659
+ using (var scope = SceneHelper.GetObjectOfTypeInScene<LevelConfig>("Scenes/LevelData"))
660
+ {
661
+ if (scope.HasObject)
662
+ {
663
+ LevelConfig config = scope.Object;
664
+ // Use config data
665
+ }
666
+ // Scene automatically unloaded here
667
+ }
668
+ ```
669
+
670
+ **Use for:**
671
+
672
+ - Extracting data from data-only scenes
673
+ - Editor tools reading scene contents
674
+ - Validation scripts
675
+ - Testing scene contents
676
+
677
+ ---
678
+
679
+ <a id="advanced-utilities"></a>
680
+
681
+ ## Advanced Utilities
682
+
683
+ ### Unity-Aware Null Checks
684
+
685
+ **The problem:** Unity's `==` operator overload can be slow, and destroyed UnityEngine.Objects return `true` for `== null` but `false` for `is null`.
686
+
687
+ ```csharp
688
+ using WallstopStudios.UnityHelpers.Core.Helper;
689
+
690
+ GameObject obj = GetMaybeDestroyedObject();
691
+
692
+ // Proper Unity null check
693
+ bool isNull = Objects.Null(obj);
694
+ bool notNull = Objects.NotNull(obj);
695
+ ```
696
+
697
+ Handles:
698
+
699
+ - Destroyed UnityEngine.Objects
700
+ - Actual null references
701
+ - Optimized checks for non-Unity types
702
+
703
+ ---
704
+
705
+ ### Deterministic Hashing
706
+
707
+ **Combine hash codes correctly:**
708
+
709
+ ```csharp
710
+ using WallstopStudios.UnityHelpers.Core.Helper;
711
+
712
+ public class CompositeKey
713
+ {
714
+ public string Name;
715
+ public int Level;
716
+ public Vector2 Position;
717
+
718
+ public override int GetHashCode()
719
+ {
720
+ // FNV-1a based hash combination
721
+ return Objects.HashCode(Name, Level, Position);
722
+ }
723
+ }
724
+ ```
725
+
726
+ Supports up to 11 parameters. Uses FNV-1a algorithm for good distribution.
727
+
728
+ **Hash entire collections:**
729
+
730
+ ```csharp
731
+ List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
732
+ int hash = Objects.EnumerableHashCode(numbers);
733
+ ```
734
+
735
+ **Use for:**
736
+
737
+ - Custom GetHashCode implementations
738
+ - Dictionary keys with multiple fields
739
+ - Networking determinism
740
+ - Save file hashing
741
+
742
+ ---
743
+
744
+ ### Formatting
745
+
746
+ **Human-readable byte counts:**
747
+
748
+ ```csharp
749
+ using WallstopStudios.UnityHelpers.Core.Helper;
750
+
751
+ long bytes = 1536000;
752
+ string formatted = FormattingHelpers.FormatBytes(bytes);
753
+ // Result: "1.46 MB"
754
+ ```
755
+
756
+ Auto-scales to B, KB, MB, GB, TB.
757
+
758
+ **Use for:**
759
+
760
+ - File size displays
761
+ - Memory usage UI
762
+ - Profiling output
763
+ - Download progress
764
+
765
+ ---
766
+
767
+ ### Multi-Dimensional Array Iteration
768
+
769
+ **Enumerate 2D/3D array indices:**
770
+
771
+ ```csharp
772
+ using WallstopStudios.UnityHelpers.Core.Helper;
773
+
774
+ int[,] grid = new int[10, 10];
775
+
776
+ // Get all indices as tuples
777
+ foreach (var (x, y) in IterationHelpers.IndexOver(grid))
778
+ {
779
+ grid[x, y] = x + y;
780
+ }
781
+
782
+ // Buffered (zero allocation)
783
+ using (var buffer = Buffers<(int, int)>.List.Get())
784
+ {
785
+ IterationHelpers.IndexOver(grid, buffer.Value);
786
+ foreach (var (x, y) in buffer.Value)
787
+ {
788
+ // Process
789
+ }
790
+ }
791
+ ```
792
+
793
+ Also supports 3D arrays with `(int, int, int)` tuples.
794
+
795
+ ---
796
+
797
+ ### Binary Array Conversion
798
+
799
+ **Fast marshalling between int[] and byte[]:**
800
+
801
+ ```csharp
802
+ using WallstopStudios.UnityHelpers.Core.Helper;
803
+
804
+ int[] ints = { 1, 2, 3, 4, 5 };
805
+
806
+ // Convert to bytes (uses Buffer.BlockCopy)
807
+ byte[] bytes = ArrayConverter.IntArrayToByteArrayBlockCopy(ints);
808
+
809
+ // Convert back
810
+ int[] restored = ArrayConverter.ByteArrayToIntArrayBlockCopy(bytes);
811
+ ```
812
+
813
+ **Use for:**
814
+
815
+ - Network serialization
816
+ - Binary file formats
817
+ - Save game data
818
+ - High-performance data conversion
819
+
820
+ **Performance:** O(n) native memory copy, much faster than element-by-element loops.
821
+
822
+ ---
823
+
824
+ ### Custom Comparers
825
+
826
+ **Create IComparer from lambda:**
827
+
828
+ ```csharp
829
+ using WallstopStudios.UnityHelpers.Core.Helper;
830
+
831
+ var enemies = new List<Enemy>();
832
+
833
+ // Sort by health descending
834
+ enemies.Sort(new FuncBasedComparer<Enemy>((a, b) =>
835
+ b.health.CompareTo(a.health) // Descending
836
+ ));
837
+ ```
838
+
839
+ **Reverse any comparer:**
840
+
841
+ ```csharp
842
+ var comparer = Comparer<int>.Default;
843
+ var reversed = new ReverseComparer<int>(comparer);
844
+
845
+ // Now sorts descending
846
+ list.Sort(reversed);
847
+ ```
848
+
849
+ ---
850
+
851
+ ## Best Practices
852
+
853
+ ### Performance
854
+
855
+ - **Cache lookups**: `Helpers.Find<T>()` caches, but don't call every frame anyway
856
+ - **Use buffered variants**: `IterateOverAllChildrenRecursively` with buffers for hot paths
857
+ - **Main thread dispatch**: Don't send hundreds of tiny tasks, batch work
858
+ - **Hierarchy traversal**: Use breadth-first with depth limits for large hierarchies
859
+
860
+ ### Threading
861
+
862
+ - **Main thread rule**: Only Unity APIs need main thread, pure C# can stay on background threads
863
+ - **Avoid blocking**: Don't wait for main thread results in tight loops
864
+ - **CancellationToken**: Always support cancellation for long operations
865
+
866
+ ### Architecture
867
+
868
+ - **Component vs Helper**: Components (MonoBehaviours) for per-object state, Helpers for stateless operations
869
+ - **Static method smell**: If you need instance state, use a component instead
870
+ - **Editor/Runtime split**: Use `#if UNITY_EDITOR` guards for editor-only helpers
871
+
872
+ ### Code Organization
873
+
874
+ - **Namespace imports**: Use `using WallstopStudios.UnityHelpers.Core.Helper;` at top of file
875
+ - **Don't extend helpers**: These are sealed utility classes, not inheritance hierarchies
876
+ - **Prefer composition**: Use helpers from components, don't try to combine them
877
+
878
+ ---
879
+
880
+ ## Related Documentation
881
+
882
+ - [Math & Extensions](MATH_AND_EXTENSIONS.md) - Extension methods on built-in types
883
+ - [Utility Components](UTILITY_COMPONENTS.md) - MonoBehaviour-based utilities
884
+ - [Reflection Helpers](REFLECTION_HELPERS.md) - High-performance reflection utilities
885
+ - [Singletons](SINGLETONS.md) - RuntimeSingleton and ScriptableObjectSingleton