com.wallstop-studios.unity-helpers 2.0.0-rc81.9 → 2.0.0

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 (168) hide show
  1. package/.editorconfig +1 -1
  2. package/.gitattributes +1 -1
  3. package/.githooks/pre-commit +31 -5
  4. package/.githooks/pre-push +50 -0
  5. package/.github/dependabot.yml +24 -2
  6. package/.github/scripts/check-markdown-links.ps1 +77 -0
  7. package/.github/scripts/check_markdown_links.py +89 -0
  8. package/.github/scripts/check_markdown_url_encoding.py +74 -0
  9. package/.github/scripts/validate_markdown_links.py +194 -0
  10. package/.github/workflows/csharpier-autofix.yml +152 -0
  11. package/.github/workflows/format-on-demand.yml +305 -0
  12. package/.github/workflows/lint-doc-links.yml +8 -5
  13. package/.github/workflows/markdown-json.yml +6 -2
  14. package/.github/workflows/prettier-autofix.yml +195 -0
  15. package/.github/workflows/update-dotnet-tools.yml +80 -0
  16. package/.github/workflows/yaml-format-lint.yml +41 -0
  17. package/.lychee.toml +4 -4
  18. package/.markdownlint.jsonc +21 -0
  19. package/.pre-commit-config.yaml +11 -3
  20. package/.yamllint.yaml +31 -0
  21. package/AGENTS.md +5 -1
  22. package/CHANGELOG.md +11 -0
  23. package/CONTRIBUTING.md +49 -0
  24. package/CONTRIBUTING.md.meta +7 -0
  25. package/EDITOR_TOOLS_GUIDE.md +4 -0
  26. package/Editor/AnimationEventEditor.cs +337 -160
  27. package/Editor/Core/Helper/AnimationEventHelpers.cs +178 -152
  28. package/Editor/CustomEditors/PersistentDirectoryGUI.cs +20 -11
  29. package/Editor/CustomEditors/TexturePlatformOverrideEntryDrawer.cs +11 -2
  30. package/Editor/FitTextureSizeWindow.cs +43 -19
  31. package/Editor/PersistentDirectorySettings.cs +64 -12
  32. package/Editor/PrefabChecker.cs +72 -5
  33. package/Editor/Sprites/AnimationCopier.cs +132 -56
  34. package/Editor/Sprites/AnimationCreator.cs +63 -22
  35. package/Editor/Sprites/AnimationViewerWindow.cs +42 -6
  36. package/Editor/Sprites/TexturePlatformNameHelper.cs +50 -39
  37. package/Editor/Sprites/TextureResizerWizard.cs +23 -1
  38. package/Editor/Sprites/TextureSettingsApplierWindow.cs +148 -85
  39. package/Editor/Tools/ImageBlurTool.cs +81 -10
  40. package/Editor/Utils/EditorUi.cs +1 -1
  41. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +1 -1
  42. package/GETTING_STARTED.md +40 -56
  43. package/RANDOM_PERFORMANCE.md +12 -12
  44. package/README.md +395 -2407
  45. package/RELATIONAL_COMPONENTS.md +92 -83
  46. package/Runtime/AssemblyInfo.cs +2 -0
  47. package/Runtime/Core/Attributes/NotNullAttribute.cs +1 -3
  48. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +50 -5
  49. package/Runtime/Core/DataStructure/CyclicBuffer.cs +0 -1
  50. package/Runtime/Core/Extension/RandomExtensions.cs +68 -0
  51. package/Runtime/Core/Extension/WallstopStudiosLogger.cs +16 -0
  52. package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +4 -1
  53. package/Runtime/Core/Helper/ReflectionHelpers.cs +21 -10
  54. package/Runtime/Core/Helper/SpriteHelpers.cs +3 -1
  55. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +45 -1
  56. package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +13 -5
  57. package/Runtime/Core/Serialization/JsonConverters/ResolutionConverter.cs +1 -1
  58. package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +1 -1
  59. package/Runtime/Core/Serialization/Serializer.cs +101 -0
  60. package/Runtime/Integrations/VContainer/AssemblyInfo.cs +9 -0
  61. package/Runtime/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
  62. package/Runtime/Integrations/VContainer/ObjectResolverRelationalExtensions.cs +96 -0
  63. package/Runtime/Integrations/VContainer/RelationalComponentEntryPoint.cs +90 -10
  64. package/Runtime/Integrations/VContainer/RelationalComponentsBuilderExtensions.cs +13 -1
  65. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs +114 -0
  66. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs.meta +11 -0
  67. package/Runtime/Integrations/VContainer/RelationalSceneAssignmentOptions.cs +16 -4
  68. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs +241 -0
  69. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs.meta +11 -0
  70. package/Runtime/Integrations/Zenject/AssemblyInfo.cs +9 -0
  71. package/Runtime/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
  72. package/Runtime/Integrations/Zenject/DiContainerRelationalExtensions.cs +69 -2
  73. package/Runtime/Integrations/Zenject/RelationalComponentSceneInitializer.cs +89 -12
  74. package/Runtime/Integrations/Zenject/RelationalComponentsInstaller.cs +23 -1
  75. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs +44 -0
  76. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs.meta +11 -0
  77. package/Runtime/Integrations/Zenject/RelationalSceneAssignmentOptions.cs +16 -10
  78. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs +243 -0
  79. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs.meta +11 -0
  80. package/Runtime/Tags/AttributeMetadataCache.cs +1 -4
  81. package/Runtime/Utils/Buffers.cs +4 -4
  82. package/Runtime/Utils/ScriptableObjectSingleton.cs +0 -1
  83. package/Runtime/Utils/SetTextureImportData.cs +3 -1
  84. package/Runtime/Utils/TextureScale.cs +10 -2
  85. package/Runtime/Visuals/UGUI/EnhancedImage.cs +6 -0
  86. package/Runtime/Visuals/UIToolkit/LayeredImage.cs +4 -1
  87. package/SERIALIZATION.md +15 -0
  88. package/SPATIAL_TREE_2D_PERFORMANCE.md +85 -82
  89. package/SPATIAL_TREE_3D_PERFORMANCE.md +94 -91
  90. package/Samples~/DI - VContainer/README.md +232 -51
  91. package/Samples~/DI - VContainer/Scripts/GameLifetimeScope.cs +22 -4
  92. package/Samples~/DI - VContainer/Scripts/RelationalConsumer.cs +5 -2
  93. package/Samples~/DI - VContainer/Scripts/Spawner.cs +113 -4
  94. package/Samples~/DI - Zenject/README.md +217 -53
  95. package/Samples~/DI - Zenject/Scripts/RelationalConsumer.cs +3 -0
  96. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs +37 -0
  97. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs.meta +12 -0
  98. package/Samples~/DI - Zenject/Scripts/SpawnerZenject.cs +74 -3
  99. package/Samples~/Random - PRNG/README.md +2 -1
  100. package/Samples~/Relational Components - Basic/README.md +3 -1
  101. package/Samples~/Serialization - JSON/README.md +2 -1
  102. package/Samples~/Spatial Structures - 2D and 3D/README.md +2 -1
  103. package/Samples~/UGUI - EnhancedImage/README.md +2 -1
  104. package/Samples~/UI Toolkit - MultiFile Selector (Editor)/README.md +2 -1
  105. package/THIRD_PARTY_NOTICES.md +1 -1
  106. package/Tests/Editor/Attributes/AnimationEventHelpersTests.cs +16 -0
  107. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +3 -3
  108. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +6 -2
  109. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +170 -0
  110. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs.meta +11 -0
  111. package/Tests/Editor/Integrations/VContainer/WallstopStudios.UnityHelpers.Tests.Editor.VContainer.asmdef +2 -1
  112. package/Tests/Editor/Integrations/Zenject/WallstopStudios.UnityHelpers.Tests.Editor.Zenject.asmdef +3 -2
  113. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +131 -0
  114. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs.meta +11 -0
  115. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +6 -2
  116. package/Tests/Editor/PersistentDirectorySettingsTests.cs +59 -0
  117. package/Tests/Editor/PersistentDirectorySettingsTests.cs.meta +11 -0
  118. package/Tests/Editor/PrefabCheckerReportTests.cs +32 -0
  119. package/Tests/Editor/PrefabCheckerReportTests.cs.meta +11 -0
  120. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +64 -0
  121. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs.meta +11 -0
  122. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +1 -1
  123. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +38 -0
  124. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs.meta +11 -0
  125. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +1 -1
  126. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +12 -12
  127. package/Tests/Editor/Sprites/SpriteCropperTests.cs +9 -9
  128. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +3 -3
  129. package/Tests/Editor/Sprites/TexturePlatformNameHelperTests.cs +18 -0
  130. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +5 -5
  131. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +3 -3
  132. package/Tests/Editor/Sprites/TextureSettingsApplierWizardAdditionalTests.cs +4 -4
  133. package/Tests/Editor/Sprites/TextureSettingsApplierWizardTests.cs +4 -4
  134. package/Tests/Editor/Tools/ImageBlurToolTests.cs +22 -110
  135. package/Tests/Editor/Utils/CommonTestBase.cs +43 -1
  136. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +5 -5
  137. package/Tests/Editor/Windows/FitTextureSizeWindowTests.cs +66 -74
  138. package/Tests/Runtime/Attributes/RelationalComponentInitializerTests.cs +4 -15
  139. package/Tests/Runtime/DataStructures/SpatialTree3DBoundsConsistencyTests.cs +29 -29
  140. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +259 -218
  141. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +86 -0
  142. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs.meta +11 -0
  143. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +255 -227
  144. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +5 -0
  145. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +3 -0
  146. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs +30 -0
  147. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs.meta +11 -0
  148. package/Tests/Runtime/Serialization/JsonConverterTests.cs +8 -12
  149. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +16 -5
  150. package/Tests/Runtime/Serialization/SerializerAdditionalTests.cs +12 -0
  151. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs +105 -0
  152. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs.meta +11 -0
  153. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs +247 -0
  154. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs.meta +11 -0
  155. package/Tests/Runtime/TestUtils/CommonTestBase.cs +88 -0
  156. package/Tests/Runtime/Utils/CoroutineHandlerTests.cs +1 -1
  157. package/Tests/Runtime/Utils/LZMAComprehensiveTests.cs +1 -1
  158. package/Tests/Runtime/Utils/LZMATests.cs +1 -1
  159. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +1 -1
  160. package/Tests/Runtime/Visuals/EnhancedImageTests.cs +25 -56
  161. package/Tests/Runtime/Visuals/VisualsTestHelpers.cs +1 -8
  162. package/package-lock.json.meta +7 -0
  163. package/package.json +8 -4
  164. package/scripts/check-eol.ps1 +4 -5
  165. package/scripts/lint-tests.ps1 +156 -0
  166. package/scripts/lint-tests.ps1.meta +7 -0
  167. package/scripts/normalize-eol.ps1 +6 -9
  168. package/.github/workflows/csharpier.yml +0 -135
@@ -2,17 +2,23 @@
2
2
 
3
3
  ## Why This Integration Matters
4
4
 
5
- **The Problem:** When using dependency injection with Zenject, you often need to wire up both:
6
- 1. **Dependencies** (injected via constructor/properties/fields)
7
- 2. **Hierarchy references** (SpriteRenderer, Rigidbody2D, child colliders, etc.)
5
+ **Stop Writing GetComponent Boilerplate in Every Single Script**
8
6
 
9
- Doing this manually means writing boilerplate in every component.
7
+ When using dependency injection with Zenject, you've solved half the problem - your service dependencies get injected cleanly. But you're **still stuck** writing repetitive `GetComponent` boilerplate for hierarchy references in every. single. MonoBehaviour.
10
8
 
11
- **The Solution:** Unity Helpers' Zenject integration automatically wires up relational component fields **right after** DI injection completes - giving you the best of both worlds with zero extra code.
9
+ **The Painful Reality:**
10
+
11
+ 1. **Dependencies** → ✅ Handled by Zenject (IHealthSystem, IAudioService, etc.)
12
+ 2. **Hierarchy references** → ❌ Still manual hell (SpriteRenderer, Rigidbody2D, child colliders, etc.)
13
+
14
+ You're using a modern DI framework but still writing 2008-era Unity boilerplate. **Unity Helpers fixes this.**
15
+
16
+ **The Solution:** This integration automatically wires up relational component fields **right after** DI injection completes - giving you the best of both worlds with **literally zero extra code per component**.
12
17
 
13
18
  ### ⚡ Quick Example: Before vs After
14
19
 
15
20
  **Before (Manual):**
21
+
16
22
  ```csharp
17
23
  public class Enemy : MonoBehaviour
18
24
  {
@@ -36,6 +42,7 @@ public class Enemy : MonoBehaviour
36
42
  ```
37
43
 
38
44
  **After (With Integration):**
45
+
39
46
  ```csharp
40
47
  public class Enemy : MonoBehaviour
41
48
  {
@@ -50,7 +57,9 @@ public class Enemy : MonoBehaviour
50
57
  }
51
58
  ```
52
59
 
53
- **Time Saved:** 10-20 lines of boilerplate per component × hundreds of components = **weeks** of development time.
60
+ **⏱️ Time Saved:** 10-20 lines of boilerplate per component × hundreds of components = **weeks** of development time.
61
+ **🧠 Mental Load Eliminated:** No more context-switching between DI patterns and Unity hierarchy patterns.
62
+ **🐛 Bugs Prevented:** Automatic validation catches missing references **before** they cause runtime errors.
54
63
 
55
64
  ---
56
65
 
@@ -60,37 +69,47 @@ public class Enemy : MonoBehaviour
60
69
 
61
70
  1. Add a `SceneContext` to your scene (if you don't have one already)
62
71
  2. Add the `RelationalComponentsInstaller` component to the same GameObject
63
- 3. *(Optional)* Toggle **"Assign Scene On Initialize"** to automatically wire all scene components after the container builds
72
+ 3. Enable **"Assign Scene On Initialize"** to automatically wire all scene components after the container builds (recommended)
64
73
 
65
74
  ![SceneContext Setup](../../Docs/Images/zenject_setup.png)
66
75
 
76
+ > 💡 **Beginner tip:** Enable both checkboxes in the inspector:
77
+ >
78
+ > - ✅ **Assign Scene On Initialize** → Auto-wires all scene objects (saves you from calling it manually)
79
+ > - ✅ **Listen For Additive Scenes** → Auto-wires newly loaded scenes (great for multi-scene setups)
80
+
67
81
  ### Step 2: Use With Prefab Instantiation
68
82
 
69
- When spawning prefabs at runtime, use `InstantiateComponentWithRelations` instead of regular Zenject instantiation:
83
+ When spawning prefabs at runtime, use the helpers that combine instantiation, DI, and relational assignment:
70
84
 
71
85
  ```csharp
72
86
  using UnityEngine;
73
87
  using Zenject;
74
88
  using WallstopStudios.UnityHelpers.Integrations.Zenject;
75
89
 
76
- public class EnemySpawner : MonoBehaviour
90
+ public sealed class EnemySpawner : MonoBehaviour
77
91
  {
78
92
  [Inject] private DiContainer _container;
79
93
  [SerializeField] private Enemy _enemyPrefab;
94
+ [SerializeField] private GameObject _enemySquadPrefab;
80
95
 
81
- public void SpawnEnemy(Vector3 position)
96
+ public Enemy SpawnEnemy(Transform parent)
82
97
  {
83
- // Performs DI injection AND relational component wiring
84
- Enemy enemy = _container.InstantiateComponentWithRelations(
85
- _enemyPrefab,
86
- position,
87
- Quaternion.identity,
88
- parentTransform: null
98
+ return _container.InstantiateComponentWithRelations(_enemyPrefab, parent);
99
+ }
100
+
101
+ public GameObject SpawnEnemySquad(Transform parent)
102
+ {
103
+ return _container.InstantiateGameObjectWithRelations(
104
+ _enemySquadPrefab,
105
+ parent,
106
+ includeInactiveChildren: true
89
107
  );
108
+ }
90
109
 
91
- // enemy._healthSystem is injected
92
- // enemy._animator, enemy._rigidbody are auto-wired
93
- // Ready to use immediately!
110
+ public void HydrateExisting(GameObject root)
111
+ {
112
+ _container.AssignRelationalHierarchy(root, includeInactiveChildren: true);
94
113
  }
95
114
  }
96
115
  ```
@@ -103,7 +122,8 @@ public class EnemySpawner : MonoBehaviour
103
122
 
104
123
  This sample provides a complete working example:
105
124
 
106
- - **Scripts/SpawnerZenject.cs** - Runtime instantiation using `InstantiateComponentWithRelations()`
125
+ - **Scripts/SpawnerZenject.cs** - Demonstrates `InstantiateComponentWithRelations`, `InstantiateGameObjectWithRelations`, optional pooling, and hierarchy hydration
126
+ - **Scripts/RelationalConsumerPool.cs** - Minimal `RelationalMemoryPool` implementation for use with Zenject memory pools
107
127
  - **Scripts/RelationalConsumer.cs** - Component demonstrating relational attributes
108
128
  - **Prefabs/RelationalConsumer.prefab** - Example prefab with relational fields
109
129
  - **Prefabs/SpawnerZenject.prefab** - Spawner prefab showing runtime usage
@@ -119,11 +139,15 @@ This sample provides a complete working example:
119
139
 
120
140
  ---
121
141
 
122
- ## 🎯 Common Use Cases
142
+ ## 🎯 Common Use Cases (By Experience Level)
143
+
144
+ ### 🟢 Beginner: "I just want my components to work"
145
+
146
+ **Perfect for:** Player controllers, enemy AI, simple gameplay scripts
123
147
 
124
- ### Scene Objects with Both DI and Hierarchy References
148
+ **What you get:** No more `GetComponent` calls, no more null reference exceptions from missing components
125
149
 
126
- Perfect for player controllers, managers, and gameplay systems:
150
+ **Example:**
127
151
 
128
152
  ```csharp
129
153
  public class PlayerController : MonoBehaviour
@@ -150,56 +174,56 @@ public class PlayerController : MonoBehaviour
150
174
 
151
175
  **Important:** Enable **"Assign Scene On Initialize"** in the `RelationalComponentsInstaller` for automatic scene wiring.
152
176
 
153
- ### Runtime-Spawned Prefabs
177
+ ### 🟡 Intermediate: "I'm spawning objects at runtime"
154
178
 
155
- For enemies, projectiles, and dynamic objects:
179
+ **Perfect for:** Enemy spawners, projectile systems, object pooling
180
+
181
+ **What you get:** One-line instantiation that handles DI injection + hierarchy wiring automatically
182
+
183
+ **Example:**
156
184
 
157
185
  ```csharp
158
- public class ProjectileSpawner : MonoBehaviour
186
+ public sealed class ProjectileSpawner : MonoBehaviour
159
187
  {
160
188
  [Inject] private DiContainer _container;
161
189
  [SerializeField] private Projectile _projectilePrefab;
162
190
 
163
- public void Fire(Vector3 position, Vector3 direction)
191
+ public Projectile Fire(Vector3 position, Vector3 direction)
164
192
  {
165
- // Both DI injection and relational component wiring happen here
166
- Projectile projectile = _container.InstantiateComponentWithRelations(
167
- _projectilePrefab,
168
- position,
169
- Quaternion.LookRotation(direction)
170
- );
171
-
193
+ Projectile projectile = _container.InstantiateComponentWithRelations(_projectilePrefab);
194
+ projectile.transform.SetPositionAndRotation(position, Quaternion.LookRotation(direction));
172
195
  projectile.Launch(direction);
196
+ return projectile;
173
197
  }
174
198
  }
175
199
  ```
176
200
 
177
- ### Complex Prefab Hierarchies
201
+ ### 🔴 Advanced: "I have complex hierarchies and custom workflows"
202
+
203
+ **Perfect for:** UI systems, vehicles with multiple parts, procedural generation, custom factories
204
+
205
+ **What you get:** Full control over when and how wiring happens, with helpers for every scenario
178
206
 
179
- For UI panels, vehicles, or multi-part systems:
207
+ **Example - Complex Prefabs:**
180
208
 
181
209
  ```csharp
182
- public class VehicleFactory : MonoBehaviour
210
+ public sealed class VehicleFactory : MonoBehaviour
183
211
  {
184
212
  [Inject] private DiContainer _container;
185
213
  [SerializeField] private GameObject _vehiclePrefab;
186
214
 
187
215
  public GameObject CreateVehicle()
188
216
  {
189
- // Instantiate with DI
190
- GameObject vehicle = _container.InstantiatePrefab(_vehiclePrefab);
191
-
192
- // Wire up entire hierarchy - all nested components get relational wiring
193
- _container.AssignRelationalHierarchy(vehicle, includeInactiveChildren: true);
194
-
195
- return vehicle;
217
+ return _container.InstantiateGameObjectWithRelations(
218
+ _vehiclePrefab,
219
+ parent: null,
220
+ includeInactiveChildren: true
221
+ );
196
222
  }
197
223
  }
198
224
  ```
199
225
 
200
- ### Factory Pattern with Relational Components
201
-
202
- Combine Zenject factories with relational wiring:
226
+ **Example - Custom Factories:**
203
227
 
204
228
  ```csharp
205
229
  public class EnemyFactory : PlaceholderFactory<Enemy>
@@ -221,6 +245,81 @@ Container.BindFactory<Enemy, EnemyFactory>()
221
245
 
222
246
  ---
223
247
 
248
+ ## 💡 Real-World Impact: A Day in the Life
249
+
250
+ ### Without This Integration
251
+
252
+ **Morning:** You start work on a new enemy type.
253
+
254
+ ```csharp
255
+ public class FlyingEnemy : MonoBehaviour
256
+ {
257
+ [Inject] private IHealthSystem _health;
258
+ [Inject] private IAudioService _audio;
259
+
260
+ private Animator _animator;
261
+ private Rigidbody2D _rigidbody;
262
+ private SpriteRenderer _sprite;
263
+ private Collider2D[] _hitboxes;
264
+ private Transform _weaponMount;
265
+
266
+ void Awake()
267
+ {
268
+ _animator = GetComponent<Animator>();
269
+ if (_animator == null) Debug.LogError("Missing Animator on FlyingEnemy!");
270
+
271
+ _rigidbody = GetComponent<Rigidbody2D>();
272
+ if (_rigidbody == null) Debug.LogError("Missing Rigidbody2D on FlyingEnemy!");
273
+
274
+ _sprite = GetComponent<SpriteRenderer>();
275
+ if (_sprite == null) Debug.LogError("Missing SpriteRenderer on FlyingEnemy!");
276
+
277
+ _hitboxes = GetComponentsInChildren<Collider2D>();
278
+ if (_hitboxes.Length == 0) Debug.LogWarning("No hitboxes found on FlyingEnemy!");
279
+
280
+ _weaponMount = transform.Find("WeaponMount");
281
+ if (_weaponMount == null) Debug.LogError("Missing WeaponMount on FlyingEnemy!");
282
+
283
+ // Finally, actual game logic can start...
284
+ }
285
+ }
286
+ ```
287
+
288
+ **10 minutes later:** You've written 20+ lines of boilerplate before writing any actual game logic.
289
+
290
+ **30 minutes later:** Null reference exception in the build! You forgot to add the SpriteRenderer to the prefab.
291
+
292
+ **60 minutes later:** You're manually wiring up the 8th enemy variant of the day...
293
+
294
+ ### With This Integration
295
+
296
+ **Morning:** You start work on a new enemy type.
297
+
298
+ ```csharp
299
+ public class FlyingEnemy : MonoBehaviour
300
+ {
301
+ [Inject] private IHealthSystem _health;
302
+ [Inject] private IAudioService _audio;
303
+
304
+ [SiblingComponent] private Animator _animator;
305
+ [SiblingComponent] private Rigidbody2D _rigidbody;
306
+ [SiblingComponent] private SpriteRenderer _sprite;
307
+ [ChildComponent] private Collider2D[] _hitboxes;
308
+ [ChildComponent(NameFilter = "WeaponMount")] private Transform _weaponMount;
309
+
310
+ // Start writing game logic immediately
311
+ void Start() => _animator.Play("Idle");
312
+ }
313
+ ```
314
+
315
+ **2 minutes later:** You're done with wiring and writing game logic.
316
+
317
+ **10 minutes later:** You've shipped 5 enemy variants with zero boilerplate.
318
+
319
+ **Never:** You never see "Missing component" runtime errors because validation happens automatically with helpful messages.
320
+
321
+ ---
322
+
224
323
  ## 🔧 Advanced Configuration
225
324
 
226
325
  ### RelationalComponentsInstaller Options
@@ -228,6 +327,7 @@ Container.BindFactory<Enemy, EnemyFactory>()
228
327
  The installer component provides these settings:
229
328
 
230
329
  **Assign Scene On Initialize** *(default: true)*
330
+
231
331
  - When enabled, automatically wires all scene components with relational attributes after the container builds
232
332
  - Disable if you want to manually control when scene wiring happens
233
333
 
@@ -276,11 +376,68 @@ Container.BindInterfacesTo<GameBootstrap>().AsSingle();
276
376
  ```
277
377
 
278
378
  Or enable auto-prewarm on the `AttributeMetadataCache` asset:
379
+
279
380
  1. Create: `Assets > Create > Wallstop Studios > Unity Helpers > Attribute Metadata Cache`
280
381
  2. Enable **"Prewarm Relational On Load"** in the Inspector
281
382
 
282
383
  ---
283
384
 
385
+ ## 🧰 Additional Helpers & Recipes
386
+
387
+ ### One-liners for DI + Relational Wiring
388
+
389
+ ```csharp
390
+ // Inject + assign a single component
391
+ Container.InjectWithRelations(component);
392
+
393
+ // Instantiate a component prefab + assign
394
+ var comp = Container.InstantiateComponentWithRelations(prefabComp, parent);
395
+
396
+ // Inject + assign a whole hierarchy
397
+ Container.InjectGameObjectWithRelations(root, includeInactiveChildren: true);
398
+
399
+ // Instantiate a GameObject prefab + inject + assign hierarchy
400
+ var go = Container.InstantiateGameObjectWithRelations(prefabGo, parent);
401
+ ```
402
+
403
+ ### Additive Scenes & Options
404
+
405
+ In the `RelationalComponentsInstaller`, enable “Assign Scene On Initialize” and “Listen For Additive Scenes”. You can also control scanning behavior via options:
406
+
407
+ ```csharp
408
+ public sealed class GameInstaller : MonoInstaller
409
+ {
410
+ public override void InstallBindings()
411
+ {
412
+ // Bind assigner (done by installer automatically if used)
413
+ // Container.Bind<IRelationalComponentAssigner>().To<RelationalComponentAssigner>().AsSingle();
414
+
415
+ // Configure scan options used by the initializer/listener
416
+ Container.BindInstance(new RelationalSceneAssignmentOptions(
417
+ includeInactive: true,
418
+ useSinglePassScan: true
419
+ ));
420
+
421
+ // Register initializer + additive scene listener (installer toggles also available)
422
+ Container.BindInterfacesTo<RelationalComponentSceneInitializer>().AsSingle();
423
+ Container.BindInterfacesTo<RelationalSceneLoadListener>().AsSingle();
424
+ }
425
+ }
426
+ ```
427
+
428
+ ### Pools
429
+
430
+ Use DI-aware Zenject memory pools to assign on spawn automatically:
431
+
432
+ ```csharp
433
+ public class EnemyPool : RelationalMemoryPool<Enemy> {}
434
+
435
+ // Or with a spawn parameter
436
+ public class BulletPool : RelationalMemoryPool<Vector3, Bullet> {}
437
+ ```
438
+
439
+ ---
440
+
284
441
  ## ❓ Troubleshooting
285
442
 
286
443
  ### My relational fields are null even with the integration
@@ -293,15 +450,15 @@ Or enable auto-prewarm on the `AttributeMetadataCache` asset:
293
450
 
294
451
  2. **Scene components not wired?**
295
452
  - Enable **"Assign Scene On Initialize"** in the `RelationalComponentsInstaller`
296
- - Or manually call `_container.AssignRelationalComponents(this)` in your component
453
+ - Or manually call `_container.AssignRelationalHierarchy(gameObject, includeInactiveChildren: true)` at bootstrap time
297
454
 
298
455
  3. **Are you using the right attributes?**
299
456
  - Fields need `[SiblingComponent]`, `[ParentComponent]`, or `[ChildComponent]` attributes
300
457
  - These are different from `[Inject]` - you can use both on the same component
301
458
 
302
459
  4. **Runtime instantiation not working?**
303
- - Use `_container.InstantiateComponentWithRelations()` instead of regular Zenject methods
304
- - Regular `InstantiatePrefab()` won't trigger relational wiring
460
+ - Use `_container.InstantiateComponentWithRelations(...)`, `_container.InstantiateGameObjectWithRelations(...)`, or `_container.AssignRelationalHierarchy(...)`
461
+ - Regular `InstantiatePrefab()`/`InstantiatePrefabForComponent()` won't trigger relational wiring without these helpers
305
462
 
306
463
  5. **Check your filters:**
307
464
  - `TagFilter` must match an existing Unity tag exactly
@@ -310,14 +467,16 @@ Or enable auto-prewarm on the `AttributeMetadataCache` asset:
310
467
  ### Do I need to call AssignRelationalComponents() in Awake()?
311
468
 
312
469
  **No!** The integration handles this automatically:
470
+
313
471
  - **Scene objects:** Wired when you enable "Assign Scene On Initialize" (recommended)
314
- - **Runtime objects:** Wired when you call `InstantiateComponentWithRelations()`
472
+ - **Runtime objects:** Wired when you call any of the helper methods (`InstantiateComponentWithRelations`, `InstantiateGameObjectWithRelations`, `AssignRelationalHierarchy`, or the pooling helpers built on `RelationalMemoryPool`)
315
473
 
316
474
  Only call `AssignRelationalComponents()` manually if you need fine-grained control.
317
475
 
318
476
  ### Does this work without Zenject?
319
477
 
320
478
  **Yes!** The integration gracefully falls back to standard Unity Helpers behavior if Zenject isn't detected. You can:
479
+
321
480
  - Adopt incrementally without breaking existing code
322
481
  - Use in projects that mix DI and non-DI components
323
482
  - Remove Zenject later without refactoring all your components
@@ -327,6 +486,7 @@ Only call `AssignRelationalComponents()` manually if you need fine-grained contr
327
486
  **Minimal:** Relational component assignment happens once per component at initialization time. After that, there's zero runtime overhead - the references are just regular fields.
328
487
 
329
488
  **Optimization tips:**
489
+
330
490
  - Use `MaxDepth` to limit hierarchy traversal
331
491
  - Use `TagFilter` or `NameFilter` to narrow searches
332
492
  - Use `OnlyDescendants`/`OnlyAncestors` to exclude self when appropriate
@@ -334,6 +494,7 @@ Only call `AssignRelationalComponents()` manually if you need fine-grained contr
334
494
  ### Zenject vs Extenject?
335
495
 
336
496
  This integration works with **all** Zenject variants:
497
+
337
498
  - **Zenject** (original)
338
499
  - **Extenject** (community fork)
339
500
  - **Zenject (Modesttree)** (updated original)
@@ -345,15 +506,18 @@ Unity Helpers automatically detects which one you're using.
345
506
  ## 📚 Learn More
346
507
 
347
508
  **Unity Helpers Documentation:**
509
+
348
510
  - [Relational Components Guide](../../RELATIONAL_COMPONENTS.md) - Complete attribute reference and recipes
349
511
  - [Getting Started](../../GETTING_STARTED.md) - Unity Helpers quick start guide
350
512
  - [Main README](../../README.md) - Full feature overview
351
513
 
352
514
  **Zenject Documentation:**
515
+
353
516
  - [Zenject GitHub](https://github.com/modesttree/Zenject) - Official Zenject documentation
354
517
  - [Extenject GitHub](https://github.com/svermeulen/Extenject) - Community fork with updates
355
518
 
356
519
  **Troubleshooting:**
520
+
357
521
  - [Relational Components Troubleshooting](../../RELATIONAL_COMPONENTS.md#troubleshooting) - Detailed solutions
358
522
  - [DI Integration Testing Guide](../../RELATIONAL_COMPONENTS.md#di-integrations-testing-and-edge-cases) - Advanced scenarios
359
523
 
@@ -376,7 +540,7 @@ If you're choosing between Zenject and VContainer, here's how the integrations d
376
540
  |---------|---------|------------|
377
541
  | Setup | Add installer to SceneContext | Call in LifetimeScope.Configure() |
378
542
  | Scene wiring | Toggle on installer | Automatic |
379
- | Runtime instantiation | `InstantiateComponentWithRelations()` | `BuildUpWithRelations()` |
543
+ | Runtime instantiation | `InstantiateComponentWithRelations()`, `InstantiateGameObjectWithRelations()`, `RelationalMemoryPool` helpers | `InstantiateComponentWithRelations()`, `InstantiateGameObjectWithRelations()`, `BuildUpWithRelations()`, `RelationalObjectPools` helpers |
380
544
  | Performance | Good | Slightly faster |
381
545
  | Maintenance | Community-maintained | Actively developed |
382
546
 
@@ -384,6 +548,6 @@ Both integrations provide the same relational component features - choose based
384
548
 
385
549
  ---
386
550
 
387
- **Made with ❤️ by Wallstop Studios**
551
+ ## Made with ❤️ by Wallstop Studios
388
552
 
389
553
  *Unity Helpers is production-ready and actively maintained. [Star the repo](https://github.com/wallstop/unity-helpers) if you find it useful!*
@@ -3,6 +3,9 @@ namespace Samples.UnityHelpers.DI.Zenject
3
3
  using UnityEngine;
4
4
  using WallstopStudios.UnityHelpers.Core.Attributes;
5
5
 
6
+ /// <summary>
7
+ /// Minimal component that demonstrates relational attributes being resolved by the Zenject integration.
8
+ /// </summary>
6
9
  public sealed class RelationalConsumer : MonoBehaviour
7
10
  {
8
11
  [SiblingComponent]
@@ -0,0 +1,37 @@
1
+ namespace Samples.UnityHelpers.DI.Zenject
2
+ {
3
+ using global::Zenject;
4
+ using UnityEngine;
5
+ using WallstopStudios.UnityHelpers.Integrations.Zenject;
6
+
7
+ /// <summary>
8
+ /// Example Zenject memory pool that automatically hydrates relational fields when items are spawned.
9
+ /// Bind this pool in an installer with
10
+ /// <code>
11
+ /// Container.BindMemoryPool&lt;RelationalConsumer, RelationalConsumerPool&gt;()
12
+ /// .FromComponentInNewPrefab(componentPrefab)
13
+ /// .UnderTransform(poolRoot);
14
+ /// </code>
15
+ /// </summary>
16
+ public sealed class RelationalConsumerPool : RelationalMemoryPool<RelationalConsumer>
17
+ {
18
+ protected override void OnSpawned(RelationalConsumer item)
19
+ {
20
+ base.OnSpawned(item);
21
+ if (item != null)
22
+ {
23
+ item.gameObject.SetActive(true);
24
+ }
25
+ }
26
+
27
+ protected override void OnDespawned(RelationalConsumer item)
28
+ {
29
+ if (item != null)
30
+ {
31
+ item.gameObject.SetActive(false);
32
+ }
33
+
34
+ base.OnDespawned(item);
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,12 @@
1
+ fileFormatVersion: 2
2
+ guid: f5f3d2a1c3e941dca246b1a20f7c9d1b
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
12
+
@@ -7,14 +7,85 @@ namespace Samples.UnityHelpers.DI.Zenject
7
7
  public sealed class SpawnerZenject : MonoBehaviour
8
8
  {
9
9
  [SerializeField]
10
- private RelationalConsumer _prefab;
10
+ private RelationalConsumer _componentPrefab;
11
+
12
+ [SerializeField]
13
+ private GameObject _hierarchyPrefab;
14
+
15
+ [SerializeField]
16
+ private Transform _defaultParent;
11
17
 
12
18
  [Inject]
13
19
  private DiContainer _container;
14
20
 
15
- public RelationalConsumer Build(Transform parent)
21
+ [Inject(Optional = true)]
22
+ private RelationalConsumerPool _pool;
23
+
24
+ /// <summary>
25
+ /// Instantiate a component prefab through Zenject so dependencies and relational fields are populated automatically.
26
+ /// </summary>
27
+ public RelationalConsumer SpawnComponent(Transform parent)
28
+ {
29
+ Transform targetParent = parent != null ? parent : _defaultParent;
30
+ return _container.InstantiateComponentWithRelations(_componentPrefab, targetParent);
31
+ }
32
+
33
+ /// <summary>
34
+ /// Instantiate a hierarchy prefab and hydrate every attributed component beneath it.
35
+ /// </summary>
36
+ public GameObject SpawnHierarchy(Transform parent)
37
+ {
38
+ Transform targetParent = parent != null ? parent : _defaultParent;
39
+ return _container.InstantiateGameObjectWithRelations(
40
+ _hierarchyPrefab,
41
+ targetParent,
42
+ includeInactiveChildren: true
43
+ );
44
+ }
45
+
46
+ /// <summary>
47
+ /// Rent an instance from a Zenject memory pool (falls back to SpawnComponent when the pool is not bound).
48
+ /// </summary>
49
+ public RelationalConsumer SpawnFromPool(Transform parent)
50
+ {
51
+ if (_pool == null)
52
+ {
53
+ return SpawnComponent(parent);
54
+ }
55
+
56
+ RelationalConsumer instance = _pool.Spawn();
57
+ Transform targetParent = parent != null ? parent : _defaultParent;
58
+ if (targetParent != null)
59
+ {
60
+ instance.transform.SetParent(targetParent, false);
61
+ }
62
+ return instance;
63
+ }
64
+
65
+ /// <summary>
66
+ /// Return an instance to the pool.
67
+ /// </summary>
68
+ public void DespawnToPool(RelationalConsumer instance)
16
69
  {
17
- return _container.InstantiateComponentWithRelations(_prefab, parent);
70
+ if (_pool == null || instance == null)
71
+ {
72
+ return;
73
+ }
74
+
75
+ _pool.Despawn(instance);
76
+ }
77
+
78
+ /// <summary>
79
+ /// Hydrate an existing hierarchy that was created outside of the container (editor tooling etc.).
80
+ /// </summary>
81
+ public void HydrateExistingHierarchy(GameObject root)
82
+ {
83
+ if (root == null)
84
+ {
85
+ return;
86
+ }
87
+
88
+ _container.AssignRelationalHierarchy(root, includeInactiveChildren: true);
18
89
  }
19
90
  }
20
91
  }
@@ -3,10 +3,11 @@ Random – PRNG Utilities
3
3
  Shows quick usage of the high-quality PRNGs (e.g., PCG) for integers, floats, Gaussian values, and sampling from collections.
4
4
 
5
5
  How to use
6
+
6
7
  - Add `RandomPrngDemo` to any GameObject and press Play.
7
8
  - Optionally change the `seed` in the inspector for deterministic sequences.
8
9
 
9
10
  What it shows
11
+
10
12
  - `PcgRandom` construction with a seed and sampling `Next`, `NextFloat`, `NextGaussian`.
11
13
  - `NextOf` to pick elements from lists.
12
-
@@ -3,17 +3,19 @@ Relational Components – Basic
3
3
  This sample demonstrates the built-in Relational Component Attributes without any DI container. Attach the script to a GameObject hierarchy and the fields are auto-assigned at runtime.
4
4
 
5
5
  How to use
6
+
6
7
  - Open any scene and create a simple hierarchy with a parent, a child, and a sibling next to the consumer object.
7
8
  - Add `RelationalBasicConsumer` to the child object.
8
9
  - Enter Play Mode and check the Console for the assigned references.
9
10
 
10
11
  What it shows
12
+
11
13
  - `[ParentComponent]` finds components on ancestors (configurable depth).
12
14
  - `[SiblingComponent]` finds components on the same GameObject.
13
15
  - `[ChildComponent]` finds components on children (recursively).
14
16
  - `this.AssignRelationalComponents()` hydrates all relational fields in `Awake()`/`OnEnable()`.
15
17
 
16
18
  Notes
19
+
17
20
  - You can adjust filters on attributes: `Optional`, `IncludeInactive`, `TagFilter`, `NameFilter`, and `MaxCount` for collections.
18
21
  - For DI integrations, see the separate VContainer/Zenject samples.
19
-
@@ -3,11 +3,12 @@ Serialization – JSON Converters
3
3
  Demonstrates serialization of Unity types using System.Text.Json with Unity Helpers’ built-in converters and helper APIs.
4
4
 
5
5
  How to use
6
+
6
7
  - Add `JsonSerializationDemo` to any GameObject and press Play.
7
8
  - Check the Console for serialized JSON, byte sizes, and a round-trip decode.
8
9
 
9
10
  What it shows
11
+
10
12
  - `Serializer.JsonStringify` and `Serializer.JsonDeserialize<T>` for text workflows.
11
13
  - `Serializer.JsonSerialize` and `Serializer.JsonDeserialize<T>(byte[])` for UTF-8 bytes.
12
14
  - Using `Serializer.CreateFastJsonOptions()` for hot paths.
13
-
@@ -3,11 +3,12 @@ Spatial Structures – 2D and 3D
3
3
  Demonstrates building and querying spatial structures for static point sets and grid-based indexing.
4
4
 
5
5
  How to use
6
+
6
7
  - Add `SpatialStructuresDemo` to any GameObject and press Play.
7
8
  - Adjust `pointCount`, `areaSize`, and `queryRadius` in the inspector.
8
9
 
9
10
  What it shows
11
+
10
12
  - `QuadTree2D<T>` created from points with a transformer and radius query.
11
13
  - `KdTree2D<T>` approximate nearest neighbors.
12
14
  - `SpatialHash2D<T>` bucketed insertion and radius query.
13
-