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

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 (135) 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/Helper/Logging/UnityLogTagFormatter.cs +76 -90
  31. package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
  32. package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
  33. package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
  34. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
  35. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
  36. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
  37. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
  38. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
  39. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
  40. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
  41. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
  42. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
  43. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
  44. package/Runtime/Integrations/Reflex.meta +8 -0
  45. package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -1
  46. package/Samples~/DI - Reflex/README.md +527 -0
  47. package/Samples~/DI - Reflex/README.md.meta +7 -0
  48. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
  49. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
  50. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
  51. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
  52. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
  53. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
  54. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
  55. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
  56. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
  57. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
  58. package/Samples~/DI - Reflex/Scripts.meta +8 -0
  59. package/Samples~/DI - Reflex.meta +8 -0
  60. package/Samples~/DI - VContainer/README.md +6 -5
  61. package/Samples~/DI - Zenject/README.md +6 -5
  62. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +29 -31
  63. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
  64. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
  65. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
  66. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
  67. package/Tests/Editor/Integrations/Reflex.meta +8 -0
  68. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +15 -16
  69. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +7 -13
  70. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +7 -11
  71. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +19 -21
  72. package/Tests/Editor/PersistentDirectorySettingsTests.cs +0 -1
  73. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +0 -1
  74. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -2
  75. package/Tests/Editor/Tools/ImageBlurToolTests.cs +1 -1
  76. package/Tests/Editor/Utils/CommonTestBase.cs +17 -0
  77. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +1 -1
  78. package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
  79. package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
  80. package/Tests/Runtime/Extensions/UnityLogTagFormatterEdgeTests.cs +84 -0
  81. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
  82. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
  83. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
  84. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
  85. package/Tests/Runtime/Integrations/Reflex.meta +8 -0
  86. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +24 -29
  87. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +8 -3
  88. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +10 -20
  89. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
  90. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +1 -1
  91. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +1 -1
  92. package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
  93. package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
  94. package/Tests/Runtime/TestUtils/CommonTestBase.cs +11 -0
  95. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
  96. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
  97. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +4 -4
  98. package/Tests/TestUtils.meta +8 -0
  99. package/package.json +6 -1
  100. package/EFFECTS_SYSTEM.md +0 -242
  101. package/MATH_AND_EXTENSIONS.md +0 -316
  102. /package/{CHANGELOG.md → Docs/CHANGELOG.md} +0 -0
  103. /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
  104. /package/{CONTRIBUTING.md → Docs/CONTRIBUTING.md} +0 -0
  105. /package/{CONTRIBUTING.md.meta → Docs/CONTRIBUTING.md.meta} +0 -0
  106. /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
  107. /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
  108. /package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +0 -0
  109. /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
  110. /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
  111. /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
  112. /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
  113. /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
  114. /package/{HULLS.md → Docs/HULLS.md} +0 -0
  115. /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
  116. /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
  117. /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
  118. /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
  119. /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
  120. /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
  121. /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
  122. /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
  123. /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
  124. /package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +0 -0
  125. /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
  126. /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
  127. /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
  128. /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
  129. /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
  130. /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
  131. /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
  132. /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
  133. /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
  134. /package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +0 -0
  135. /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
@@ -0,0 +1,527 @@
1
+ # Reflex Integration - Unity Helpers
2
+
3
+ ## Why This Integration Matters
4
+
5
+ **Stop Writing GetComponent Boilerplate in Every Single Script**
6
+
7
+ When using dependency injection with Reflex, 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.
8
+
9
+ **The Painful Reality:**
10
+
11
+ 1. **Dependencies** → ✅ Handled by Reflex (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**.
17
+
18
+ ### ⚡ Quick Example: Before vs After
19
+
20
+ **Before (Manual):**
21
+
22
+ ```csharp
23
+ public class Enemy : MonoBehaviour
24
+ {
25
+ [Inject] private IHealthSystem _healthSystem;
26
+ private Animator _animator;
27
+ private Rigidbody2D _rigidbody;
28
+ private Collider2D[] _childColliders;
29
+
30
+ void Awake()
31
+ {
32
+ _animator = GetComponent<Animator>();
33
+ _rigidbody = GetComponent<Rigidbody2D>();
34
+ _childColliders = GetComponentsInChildren<Collider2D>();
35
+ // 10+ more lines of GetComponent calls...
36
+
37
+ if (_animator == null) Debug.LogError("Missing Animator!");
38
+ if (_rigidbody == null) Debug.LogError("Missing Rigidbody2D!");
39
+ // More validation...
40
+ }
41
+ }
42
+ ```
43
+
44
+ **After (With Integration):**
45
+
46
+ ```csharp
47
+ public class Enemy : MonoBehaviour
48
+ {
49
+ [Inject] private IHealthSystem _healthSystem;
50
+
51
+ [SiblingComponent] private Animator _animator;
52
+ [SiblingComponent] private Rigidbody2D _rigidbody;
53
+ [ChildComponent] private Collider2D[] _childColliders;
54
+
55
+ // That's it! No Awake() needed - both DI and relational fields are auto-wired
56
+ // Automatic validation with helpful error messages included
57
+ }
58
+ ```
59
+
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.
63
+
64
+ ---
65
+
66
+ ## 🚀 Quick Setup (2 Minutes)
67
+
68
+ ### Step 1: Add the Installer to Your SceneScope
69
+
70
+ 1. Add a `SceneScope` to your scene (Reflex component)
71
+ 2. Add the `RelationalComponentsInstaller` component to the same GameObject
72
+ 3. Enable **"Assign Scene On Initialize"** to automatically wire all scene components after the container builds (recommended)
73
+
74
+ > 💡 **Beginner tip:** Enable all checkboxes in the inspector:
75
+ >
76
+ > - ✅ **Assign Scene On Initialize** → Auto-wires all scene objects (saves you from calling it manually)
77
+ > - ✅ **Include Inactive Objects** → Scans disabled GameObjects too
78
+ > - ✅ **Listen For Additive Scenes** → Auto-wires newly loaded scenes (great for multi-scene setups)
79
+ > - ✅ **Use Single Pass Scan** → Faster scanning (always leave this on)
80
+
81
+ ### Step 2: Use With Prefab Instantiation
82
+
83
+ When spawning prefabs at runtime, use the helpers that combine instantiation, DI, and relational assignment:
84
+
85
+ ```csharp
86
+ using UnityEngine;
87
+ using Reflex.Core;
88
+ using Reflex.Extensions;
89
+ using WallstopStudios.UnityHelpers.Integrations.Reflex;
90
+
91
+ public sealed class EnemySpawner : MonoBehaviour
92
+ {
93
+ [SerializeField] private Enemy _enemyPrefab;
94
+ [SerializeField] private GameObject _enemySquadPrefab;
95
+
96
+ private Container _container;
97
+
98
+ private void Awake()
99
+ {
100
+ _container = gameObject.scene.GetSceneContainer();
101
+ }
102
+
103
+ public Enemy SpawnEnemy(Transform parent)
104
+ {
105
+ return _container.InstantiateComponentWithRelations(_enemyPrefab, parent);
106
+ }
107
+
108
+ public GameObject SpawnEnemySquad(Transform parent)
109
+ {
110
+ return _container.InstantiateGameObjectWithRelations(
111
+ _enemySquadPrefab,
112
+ parent,
113
+ includeInactiveChildren: true
114
+ );
115
+ }
116
+
117
+ public void HydrateExisting(GameObject root)
118
+ {
119
+ _container.InjectGameObjectWithRelations(root, includeInactiveChildren: true);
120
+ }
121
+ }
122
+ ```
123
+
124
+ **That's it!** Both DI injection and relational component wiring happen automatically.
125
+
126
+ ---
127
+
128
+ ## 📦 What's Included in This Sample
129
+
130
+ This sample provides a complete working example:
131
+
132
+ - **Scripts/ReflexSampleInstaller.cs** - Registers a palette service to demonstrate DI working alongside relational attributes
133
+ - **Scripts/ReflexRelationalConsumer.cs** - Component demonstrating relational attributes working with Reflex injection
134
+ - **Scripts/ReflexSpawner.cs** - Demonstrates `InstantiateComponentWithRelations`, `InstantiateGameObjectWithRelations`, and hierarchy hydration
135
+ - **Scripts/ReflexPaletteService.cs** - Sample service showing DI pathway is active
136
+ - **Prefabs/** - Example prefabs with relational fields
137
+ - **Scenes/Reflex_Sample.unity** - Complete working scene with SceneScope
138
+
139
+ ### How to Import This Sample
140
+
141
+ 1. Open Unity Package Manager
142
+ 2. Find **Unity Helpers** in the package list
143
+ 3. Expand the **Samples** section
144
+ 4. Click **Import** next to "DI - Reflex"
145
+ 5. Open `Scenes/Reflex_Sample.unity` and press Play
146
+
147
+ ---
148
+
149
+ ## 🎯 Common Use Cases (By Experience Level)
150
+
151
+ ### 🟢 Beginner: "I just want my components to work"
152
+
153
+ **Perfect for:** Player controllers, enemy AI, simple gameplay scripts
154
+
155
+ **What you get:** No more `GetComponent` calls, no more null reference exceptions from missing components
156
+
157
+ **Example:**
158
+
159
+ ```csharp
160
+ public class PlayerController : MonoBehaviour
161
+ {
162
+ // Injected dependencies
163
+ [Inject] private IInputService _input;
164
+ [Inject] private IAudioService _audio;
165
+
166
+ // Hierarchy references (auto-wired)
167
+ [SiblingComponent] private Animator _animator;
168
+ [SiblingComponent] private Rigidbody2D _rigidbody;
169
+ [ChildComponent(TagFilter = "Weapon")] private Weapon _weapon;
170
+
171
+ // Everything wired automatically when scene loads!
172
+
173
+ void Update()
174
+ {
175
+ Vector2 input = _input.GetMovementInput();
176
+ _rigidbody.velocity = input * moveSpeed;
177
+ _animator.SetFloat("Speed", input.magnitude);
178
+ }
179
+ }
180
+ ```
181
+
182
+ **Important:** Enable **"Assign Scene On Initialize"** in the `RelationalComponentsInstaller` for automatic scene wiring.
183
+
184
+ ### 🟡 Intermediate: "I'm spawning objects at runtime"
185
+
186
+ **Perfect for:** Enemy spawners, projectile systems, object pooling
187
+
188
+ **What you get:** One-line instantiation that handles DI injection + hierarchy wiring automatically
189
+
190
+ **Example:**
191
+
192
+ ```csharp
193
+ public sealed class ProjectileSpawner : MonoBehaviour
194
+ {
195
+ private Container _container;
196
+ [SerializeField] private Projectile _projectilePrefab;
197
+
198
+ private void Awake()
199
+ {
200
+ _container = gameObject.scene.GetSceneContainer();
201
+ }
202
+
203
+ public Projectile Fire(Vector3 position, Vector3 direction)
204
+ {
205
+ Projectile projectile = _container.InstantiateComponentWithRelations(_projectilePrefab);
206
+ projectile.transform.SetPositionAndRotation(position, Quaternion.LookRotation(direction));
207
+ projectile.Launch(direction);
208
+ return projectile;
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### 🔴 Advanced: "I have complex hierarchies and custom workflows"
214
+
215
+ **Perfect for:** UI systems, vehicles with multiple parts, procedural generation
216
+
217
+ **What you get:** Full control over when and how wiring happens, with helpers for every scenario
218
+
219
+ **Example - Complex Prefabs:**
220
+
221
+ ```csharp
222
+ public sealed class VehicleFactory : MonoBehaviour
223
+ {
224
+ private Container _container;
225
+ [SerializeField] private GameObject _vehiclePrefab;
226
+
227
+ private void Awake()
228
+ {
229
+ _container = gameObject.scene.GetSceneContainer();
230
+ }
231
+
232
+ public GameObject CreateVehicle()
233
+ {
234
+ return _container.InstantiateGameObjectWithRelations(
235
+ _vehiclePrefab,
236
+ parent: null,
237
+ includeInactiveChildren: true
238
+ );
239
+ }
240
+ }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 💡 Real-World Impact: A Day in the Life
246
+
247
+ ### Without This Integration
248
+
249
+ **Morning:** You start work on a new enemy type.
250
+
251
+ ```csharp
252
+ public class FlyingEnemy : MonoBehaviour
253
+ {
254
+ [Inject] private IHealthSystem _health;
255
+ [Inject] private IAudioService _audio;
256
+
257
+ private Animator _animator;
258
+ private Rigidbody2D _rigidbody;
259
+ private SpriteRenderer _sprite;
260
+ private Collider2D[] _hitboxes;
261
+ private Transform _weaponMount;
262
+
263
+ void Awake()
264
+ {
265
+ _animator = GetComponent<Animator>();
266
+ if (_animator == null) Debug.LogError("Missing Animator on FlyingEnemy!");
267
+
268
+ _rigidbody = GetComponent<Rigidbody2D>();
269
+ if (_rigidbody == null) Debug.LogError("Missing Rigidbody2D on FlyingEnemy!");
270
+
271
+ _sprite = GetComponent<SpriteRenderer>();
272
+ if (_sprite == null) Debug.LogError("Missing SpriteRenderer on FlyingEnemy!");
273
+
274
+ _hitboxes = GetComponentsInChildren<Collider2D>();
275
+ if (_hitboxes.Length == 0) Debug.LogWarning("No hitboxes found on FlyingEnemy!");
276
+
277
+ _weaponMount = transform.Find("WeaponMount");
278
+ if (_weaponMount == null) Debug.LogError("Missing WeaponMount on FlyingEnemy!");
279
+
280
+ // Finally, actual game logic can start...
281
+ }
282
+ }
283
+ ```
284
+
285
+ **10 minutes later:** You've written 20+ lines of boilerplate before writing any actual game logic.
286
+
287
+ **30 minutes later:** Null reference exception in the build! You forgot to add the SpriteRenderer to the prefab.
288
+
289
+ **60 minutes later:** You're manually wiring up the 8th enemy variant of the day...
290
+
291
+ ### With This Integration
292
+
293
+ **Morning:** You start work on a new enemy type.
294
+
295
+ ```csharp
296
+ public class FlyingEnemy : MonoBehaviour
297
+ {
298
+ [Inject] private IHealthSystem _health;
299
+ [Inject] private IAudioService _audio;
300
+
301
+ [SiblingComponent] private Animator _animator;
302
+ [SiblingComponent] private Rigidbody2D _rigidbody;
303
+ [SiblingComponent] private SpriteRenderer _sprite;
304
+ [ChildComponent] private Collider2D[] _hitboxes;
305
+ [ChildComponent(NameFilter = "WeaponMount")] private Transform _weaponMount;
306
+
307
+ // Start writing game logic immediately
308
+ void Start() => _animator.Play("Idle");
309
+ }
310
+ ```
311
+
312
+ **2 minutes later:** You're done with wiring and writing game logic.
313
+
314
+ **10 minutes later:** You've shipped 5 enemy variants with zero boilerplate.
315
+
316
+ **Never:** You never see "Missing component" runtime errors because validation happens automatically with helpful messages.
317
+
318
+ ---
319
+
320
+ ## 🔧 Advanced Configuration
321
+
322
+ ### RelationalComponentsInstaller Options
323
+
324
+ The installer component provides these settings:
325
+
326
+ **Assign Scene On Initialize** *(default: true)*
327
+
328
+ - When enabled, automatically wires all scene components with relational attributes after the container builds
329
+ - Disable if you want to manually control when scene wiring happens
330
+
331
+ **Include Inactive Objects** *(default: true)*
332
+
333
+ - When enabled, scans inactive GameObjects and disabled components
334
+ - Disable to only wire active objects
335
+
336
+ **Listen For Additive Scenes** *(default: false)*
337
+
338
+ - When enabled, automatically wires components in additively loaded scenes
339
+ - Essential for multi-scene workflows
340
+
341
+ **Use Single Pass Scan** *(default: true)*
342
+
343
+ - Uses optimized metadata-driven scanning (faster)
344
+ - Leave enabled unless debugging scan issues
345
+
346
+ ### Manual Hierarchy Wiring
347
+
348
+ For dynamic hierarchies or pooled objects:
349
+
350
+ ```csharp
351
+ private Container _container;
352
+
353
+ void Awake()
354
+ {
355
+ _container = gameObject.scene.GetSceneContainer();
356
+ }
357
+
358
+ void SetupComplexHierarchy(GameObject root)
359
+ {
360
+ // Wire all components in hierarchy
361
+ _container.AssignRelationalHierarchy(root, includeInactiveChildren: false);
362
+ }
363
+ ```
364
+
365
+ ### Performance: Prewarming Reflection Caches
366
+
367
+ For large projects, prewarm reflection caches during loading to avoid first-use stalls:
368
+
369
+ ```csharp
370
+ using WallstopStudios.UnityHelpers.Core.Attributes;
371
+
372
+ public class GameBootstrap : MonoBehaviour
373
+ {
374
+ void Awake()
375
+ {
376
+ // Call once during bootstrap/loading screen
377
+ RelationalComponentInitializer.Initialize();
378
+ }
379
+ }
380
+ ```
381
+
382
+ Or enable auto-prewarm on the `AttributeMetadataCache` asset:
383
+
384
+ 1. Create: `Assets > Create > Wallstop Studios > Unity Helpers > Attribute Metadata Cache`
385
+ 2. Enable **"Prewarm Relational On Load"** in the Inspector
386
+
387
+ ---
388
+
389
+ ## 🧰 Additional Helpers & Recipes
390
+
391
+ ### One-liners for DI + Relational Wiring
392
+
393
+ ```csharp
394
+ // Inject + assign a single component
395
+ container.InjectWithRelations(component);
396
+
397
+ // Instantiate a component prefab + assign
398
+ var comp = container.InstantiateComponentWithRelations(prefabComp, parent);
399
+
400
+ // Inject + assign a whole hierarchy
401
+ container.InjectGameObjectWithRelations(root, includeInactiveChildren: true);
402
+
403
+ // Instantiate a GameObject prefab + inject + assign hierarchy
404
+ var go = container.InstantiateGameObjectWithRelations(prefabGo, parent);
405
+ ```
406
+
407
+ ### Additive Scenes & Options
408
+
409
+ Enable "Listen For Additive Scenes" in the installer, or manually control scene assignment:
410
+
411
+ ```csharp
412
+ private Container _container;
413
+
414
+ void OnSceneLoaded(Scene scene, LoadSceneMode mode)
415
+ {
416
+ if (mode == LoadSceneMode.Additive)
417
+ {
418
+ // Manually wire additive scene
419
+ _container.AssignRelationalScene(scene, includeInactive: true);
420
+ }
421
+ }
422
+ ```
423
+
424
+ ---
425
+
426
+ ## ❓ Troubleshooting
427
+
428
+ ### My relational fields are null even with the integration
429
+
430
+ **Check these common issues:**
431
+
432
+ 1. **Did you add the installer?**
433
+ - Ensure `RelationalComponentsInstaller` is on your `SceneScope` GameObject
434
+ - Check that it's enabled in the Inspector
435
+
436
+ 2. **Scene components not wired?**
437
+ - Enable **"Assign Scene On Initialize"** in the `RelationalComponentsInstaller`
438
+ - Or manually call `_container.AssignRelationalHierarchy(gameObject, includeInactiveChildren: true)` at bootstrap time
439
+
440
+ 3. **Are you using the right attributes?**
441
+ - Fields need `[SiblingComponent]`, `[ParentComponent]`, or `[ChildComponent]` attributes
442
+ - These are different from `[Inject]` - you can use both on the same component
443
+
444
+ 4. **Runtime instantiation not working?**
445
+ - Use `_container.InstantiateComponentWithRelations(...)`, `_container.InstantiateGameObjectWithRelations(...)`, or `_container.InjectGameObjectWithRelations(...)`
446
+ - Regular `Instantiate()` won't trigger relational wiring without these helpers
447
+
448
+ 5. **Check your filters:**
449
+ - `TagFilter` must match an existing Unity tag exactly
450
+ - `NameFilter` is case-sensitive
451
+
452
+ ### Do I need to call AssignRelationalComponents() in Awake()?
453
+
454
+ **No!** The integration handles this automatically:
455
+
456
+ - **Scene objects:** Wired when you enable "Assign Scene On Initialize" (recommended)
457
+ - **Runtime objects:** Wired when you call any of the helper methods (`InstantiateComponentWithRelations`, `InstantiateGameObjectWithRelations`, or `InjectGameObjectWithRelations`)
458
+
459
+ Only call `AssignRelationalComponents()` manually if you need fine-grained control.
460
+
461
+ ### Does this work without Reflex?
462
+
463
+ **Yes!** The integration gracefully falls back to standard Unity Helpers behavior if Reflex isn't detected. You can:
464
+
465
+ - Adopt incrementally without breaking existing code
466
+ - Use in projects that mix DI and non-DI components
467
+ - Remove Reflex later without refactoring all your components
468
+
469
+ ### Performance impact?
470
+
471
+ **Minimal:** Relational component assignment happens once per component at initialization time. After that, there's zero runtime overhead - the references are just regular fields.
472
+
473
+ **Optimization tips:**
474
+
475
+ - Use `MaxDepth` to limit hierarchy traversal
476
+ - Use `TagFilter` or `NameFilter` to narrow searches
477
+ - Use `OnlyDescendants`/`OnlyAncestors` to exclude self when appropriate
478
+
479
+ ---
480
+
481
+ ## 📚 Learn More
482
+
483
+ **Unity Helpers Documentation:**
484
+
485
+ - [Relational Components Guide](../../Docs/RELATIONAL_COMPONENTS.md) - Complete attribute reference and recipes
486
+ - [Getting Started](../../Docs/GETTING_STARTED.md) - Unity Helpers quick start guide
487
+ - [Main README](../../README.md) - Full feature overview
488
+
489
+ **Reflex Documentation:**
490
+
491
+ - [Reflex GitHub](https://github.com/gustavopsantos/reflex) - Official Reflex documentation and source code
492
+
493
+ **Troubleshooting:**
494
+
495
+ - [Relational Components Troubleshooting](../../Docs/RELATIONAL_COMPONENTS.md#troubleshooting) - Detailed solutions
496
+ - [DI Integration Testing Guide](../../Docs/RELATIONAL_COMPONENTS.md#di-integrations-testing-and-edge-cases) - Advanced scenarios
497
+
498
+ ---
499
+
500
+ ## 🎓 Next Steps
501
+
502
+ 1. **Try the sample scene:** Open `Reflex_Sample.unity` and press Play
503
+ 2. **Read the scripts:** See how `ReflexSpawner` and `ReflexRelationalConsumer` work
504
+ 3. **Add to your project:** Add `RelationalComponentsInstaller` to your `SceneScope`
505
+ 4. **Explore attributes:** Check out the [Relational Components Guide](../../Docs/RELATIONAL_COMPONENTS.md) for all options
506
+
507
+ ---
508
+
509
+ ## 🔄 Comparison: Reflex vs VContainer vs Zenject Integration
510
+
511
+ If you're choosing between DI frameworks, here's how the integrations differ:
512
+
513
+ | Feature | Reflex | VContainer | Zenject |
514
+ |---------|--------|------------|---------|
515
+ | Setup | Add installer to SceneScope | Call in LifetimeScope.Configure() | Add installer to SceneContext |
516
+ | Scene wiring | Toggle on installer | Automatic | Toggle on installer |
517
+ | Runtime instantiation | `InstantiateComponentWithRelations()`, `InstantiateGameObjectWithRelations()` | `InstantiateComponentWithRelations()`, `InstantiateGameObjectWithRelations()`, `BuildUpWithRelations()` | `InstantiateComponentWithRelations()`, `InstantiateGameObjectWithRelations()` |
518
+ | Performance | Fast | Slightly faster | Good |
519
+ | Maintenance | Actively developed | Actively developed | Community-maintained |
520
+
521
+ All three integrations provide the same relational component features - choose based on your DI framework preference.
522
+
523
+ ---
524
+
525
+ ## Made with ❤️ by Wallstop Studios
526
+
527
+ *Unity Helpers is production-ready and actively maintained. [Star the repo](https://github.com/wallstop/unity-helpers) if you find it useful!*
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 21bfd65de6494d09ae7c36393ce9645c
3
+ TextScriptImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,36 @@
1
+ namespace Samples.UnityHelpers.DI.Reflex
2
+ {
3
+ using UnityEngine;
4
+
5
+ /// <summary>
6
+ /// Small service registered in the Reflex container so relational components receive dependencies.
7
+ /// </summary>
8
+ public sealed class ReflexPaletteService
9
+ {
10
+ private readonly Color _accentColor;
11
+ private readonly Color _inactiveColor;
12
+ private readonly Color _warningColor;
13
+
14
+ public ReflexPaletteService(Color accentColor, Color inactiveColor, Color warningColor)
15
+ {
16
+ _accentColor = accentColor;
17
+ _inactiveColor = inactiveColor;
18
+ _warningColor = warningColor;
19
+ }
20
+
21
+ public Color AccentColor
22
+ {
23
+ get { return _accentColor; }
24
+ }
25
+
26
+ public Color InactiveColor
27
+ {
28
+ get { return _inactiveColor; }
29
+ }
30
+
31
+ public Color WarningColor
32
+ {
33
+ get { return _warningColor; }
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 78b539aec9e04e9b9c385c4825e9efd4
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,79 @@
1
+ namespace Samples.UnityHelpers.DI.Reflex
2
+ {
3
+ using Reflex.Attributes;
4
+ using UnityEngine;
5
+ using WallstopStudios.UnityHelpers.Core.Attributes;
6
+
7
+ /// <summary>
8
+ /// Simple MonoBehaviour that receives a Reflex dependency and relational components at runtime.
9
+ /// </summary>
10
+ public sealed class ReflexRelationalConsumer : MonoBehaviour
11
+ {
12
+ [Inject]
13
+ private ReflexPaletteService _paletteService;
14
+
15
+ [SiblingComponent]
16
+ private SpriteRenderer _spriteRenderer;
17
+
18
+ [ChildComponent(OnlyDescendants = true)]
19
+ private ParticleSystem[] _childParticles;
20
+
21
+ private void Awake()
22
+ {
23
+ WarmUpParticles();
24
+ }
25
+
26
+ private void OnEnable()
27
+ {
28
+ ApplyAccentColor();
29
+ }
30
+
31
+ private void OnDisable()
32
+ {
33
+ ApplyInactiveColor();
34
+ }
35
+
36
+ public void ApplyAccentColor()
37
+ {
38
+ if (_spriteRenderer != null && _paletteService != null)
39
+ {
40
+ _spriteRenderer.color = _paletteService.AccentColor;
41
+ }
42
+ }
43
+
44
+ public void ApplyInactiveColor()
45
+ {
46
+ if (_spriteRenderer != null && _paletteService != null)
47
+ {
48
+ _spriteRenderer.color = _paletteService.InactiveColor;
49
+ }
50
+ }
51
+
52
+ public void FlashWarningColor()
53
+ {
54
+ if (_spriteRenderer != null && _paletteService != null)
55
+ {
56
+ _spriteRenderer.color = _paletteService.WarningColor;
57
+ }
58
+ }
59
+
60
+ private void WarmUpParticles()
61
+ {
62
+ if (_childParticles == null)
63
+ {
64
+ return;
65
+ }
66
+
67
+ for (int i = 0; i < _childParticles.Length; i++)
68
+ {
69
+ ParticleSystem system = _childParticles[i];
70
+ if (system == null)
71
+ {
72
+ continue;
73
+ }
74
+
75
+ system.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
76
+ }
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 91102afce47c4f599e1c0c99346dc04f
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,30 @@
1
+ namespace Samples.UnityHelpers.DI.Reflex
2
+ {
3
+ using Reflex.Core;
4
+ using UnityEngine;
5
+
6
+ /// <summary>
7
+ /// Installs lightweight sample services so the scene demonstrates Reflex + relational wiring.
8
+ /// </summary>
9
+ public sealed class ReflexSampleInstaller : MonoBehaviour, IInstaller
10
+ {
11
+ [SerializeField]
12
+ private Color _accentColor = new Color(0.156f, 0.768f, 0.972f, 1.0f);
13
+
14
+ [SerializeField]
15
+ private Color _inactiveColor = new Color(0.196f, 0.196f, 0.196f, 1.0f);
16
+
17
+ [SerializeField]
18
+ private Color _warningColor = new Color(0.949f, 0.419f, 0.270f, 1.0f);
19
+
20
+ public void InstallBindings(ContainerBuilder builder)
21
+ {
22
+ builder.AddSingleton(CreatePaletteService, typeof(ReflexPaletteService));
23
+ }
24
+
25
+ private ReflexPaletteService CreatePaletteService(Container container)
26
+ {
27
+ return new ReflexPaletteService(_accentColor, _inactiveColor, _warningColor);
28
+ }
29
+ }
30
+ }