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.
- package/.github/workflows/format-on-demand.yml +2 -2
- package/.github/workflows/markdown-json.yml +1 -1
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +4 -4
- package/.github/workflows/yaml-format-lint.yml +1 -1
- package/Docs/EFFECTS_SYSTEM.md +1316 -0
- package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
- package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +10 -8
- package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
- package/Docs/HELPER_UTILITIES.md +885 -0
- package/Docs/HELPER_UTILITIES.md.meta +7 -0
- package/{INDEX.md → Docs/INDEX.md} +107 -62
- package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
- package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
- package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +21 -3
- package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
- package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
- package/{SPATIAL_TREE_2D_PERFORMANCE.md → Docs/SPATIAL_TREE_2D_PERFORMANCE.md} +64 -64
- package/{SPATIAL_TREE_3D_PERFORMANCE.md → Docs/SPATIAL_TREE_3D_PERFORMANCE.md} +64 -64
- package/Docs/UTILITY_COMPONENTS.md +906 -0
- package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
- package/Docs/VISUAL_COMPONENTS.md +337 -0
- package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
- package/Editor/Sprites/AnimationCopier.cs +3 -3
- package/README.md +69 -62
- package/Runtime/AssemblyInfo.cs +2 -0
- package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
- package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
- package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
- package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
- package/Runtime/Integrations/Reflex.meta +8 -0
- package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -1
- package/Samples~/DI - Reflex/README.md +527 -0
- package/Samples~/DI - Reflex/README.md.meta +7 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
- package/Samples~/DI - Reflex/Scripts.meta +8 -0
- package/Samples~/DI - Reflex.meta +8 -0
- package/Samples~/DI - VContainer/README.md +6 -5
- package/Samples~/DI - Zenject/README.md +6 -5
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +29 -31
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
- package/Tests/Editor/Integrations/Reflex.meta +8 -0
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +15 -16
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +7 -13
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +7 -11
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +19 -21
- package/Tests/Editor/PersistentDirectorySettingsTests.cs +0 -1
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +0 -1
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -2
- package/Tests/Editor/Tools/ImageBlurToolTests.cs +1 -1
- package/Tests/Editor/Utils/CommonTestBase.cs +17 -0
- package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +1 -1
- package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +24 -29
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +8 -3
- package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +10 -20
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +1 -1
- package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
- package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
- package/Tests/Runtime/TestUtils/CommonTestBase.cs +11 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
- package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +4 -4
- package/Tests/TestUtils.meta +8 -0
- package/package.json +6 -1
- package/EFFECTS_SYSTEM.md +0 -242
- package/MATH_AND_EXTENSIONS.md +0 -316
- /package/{CHANGELOG.md → Docs/CHANGELOG.md} +0 -0
- /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
- /package/{CONTRIBUTING.md → Docs/CONTRIBUTING.md} +0 -0
- /package/{CONTRIBUTING.md.meta → Docs/CONTRIBUTING.md.meta} +0 -0
- /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
- /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
- /package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +0 -0
- /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
- /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
- /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
- /package/{HULLS.md → Docs/HULLS.md} +0 -0
- /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
- /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
- /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
- /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
- /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
- /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
- /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
- /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
- /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
- /package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +0 -0
- /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
- /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
- /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
- /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
- /package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +0 -0
- /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
|