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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) 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/npm-publish.yml +1 -1
  15. package/.github/workflows/prettier-autofix.yml +195 -0
  16. package/.github/workflows/update-dotnet-tools.yml +80 -0
  17. package/.github/workflows/yaml-format-lint.yml +41 -0
  18. package/.lychee.toml +4 -4
  19. package/.markdownlint.jsonc +21 -0
  20. package/.pre-commit-config.yaml +11 -3
  21. package/.yamllint.yaml +31 -0
  22. package/AGENTS.md +5 -1
  23. package/Docs/CHANGELOG.md +11 -0
  24. package/Docs/CONTRIBUTING.md +49 -0
  25. package/Docs/CONTRIBUTING.md.meta +7 -0
  26. package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +4 -0
  27. package/Docs/EFFECTS_SYSTEM.md +1316 -0
  28. package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
  29. package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +50 -64
  30. package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
  31. package/Docs/HELPER_UTILITIES.md +885 -0
  32. package/Docs/HELPER_UTILITIES.md.meta +7 -0
  33. package/{INDEX.md → Docs/INDEX.md} +107 -62
  34. package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
  35. package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
  36. package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +111 -84
  37. package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +15 -0
  38. package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
  39. package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
  40. package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +241 -0
  41. package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +243 -0
  42. package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +1 -1
  43. package/Docs/UTILITY_COMPONENTS.md +906 -0
  44. package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
  45. package/Docs/VISUAL_COMPONENTS.md +337 -0
  46. package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
  47. package/Editor/AnimationEventEditor.cs +337 -160
  48. package/Editor/Core/Helper/AnimationEventHelpers.cs +178 -152
  49. package/Editor/CustomEditors/PersistentDirectoryGUI.cs +20 -11
  50. package/Editor/CustomEditors/TexturePlatformOverrideEntryDrawer.cs +11 -2
  51. package/Editor/FitTextureSizeWindow.cs +43 -19
  52. package/Editor/PersistentDirectorySettings.cs +64 -12
  53. package/Editor/PrefabChecker.cs +72 -5
  54. package/Editor/Sprites/AnimationCopier.cs +131 -55
  55. package/Editor/Sprites/AnimationCreator.cs +63 -22
  56. package/Editor/Sprites/AnimationViewerWindow.cs +42 -6
  57. package/Editor/Sprites/TexturePlatformNameHelper.cs +50 -39
  58. package/Editor/Sprites/TextureResizerWizard.cs +23 -1
  59. package/Editor/Sprites/TextureSettingsApplierWindow.cs +148 -85
  60. package/Editor/Tools/ImageBlurTool.cs +81 -10
  61. package/Editor/Utils/EditorUi.cs +1 -1
  62. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +1 -1
  63. package/README.md +428 -2433
  64. package/Runtime/AssemblyInfo.cs +4 -0
  65. package/Runtime/Core/Attributes/NotNullAttribute.cs +1 -3
  66. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +50 -5
  67. package/Runtime/Core/DataStructure/CyclicBuffer.cs +0 -1
  68. package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
  69. package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
  70. package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
  71. package/Runtime/Core/Extension/RandomExtensions.cs +68 -0
  72. package/Runtime/Core/Extension/WallstopStudiosLogger.cs +16 -0
  73. package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +4 -1
  74. package/Runtime/Core/Helper/ReflectionHelpers.cs +21 -10
  75. package/Runtime/Core/Helper/SpriteHelpers.cs +3 -1
  76. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +45 -1
  77. package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +13 -5
  78. package/Runtime/Core/Serialization/JsonConverters/ResolutionConverter.cs +1 -1
  79. package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +1 -1
  80. package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
  81. package/Runtime/Core/Serialization/Serializer.cs +101 -0
  82. package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
  83. package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
  84. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
  85. package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
  86. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
  87. package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
  88. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
  89. package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
  90. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
  91. package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
  92. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
  93. package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
  94. package/Runtime/Integrations/Reflex.meta +8 -0
  95. package/Runtime/Integrations/VContainer/AssemblyInfo.cs +9 -0
  96. package/Runtime/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
  97. package/Runtime/Integrations/VContainer/ObjectResolverRelationalExtensions.cs +96 -0
  98. package/Runtime/Integrations/VContainer/RelationalComponentEntryPoint.cs +90 -10
  99. package/Runtime/Integrations/VContainer/RelationalComponentsBuilderExtensions.cs +13 -1
  100. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs +114 -0
  101. package/Runtime/Integrations/VContainer/RelationalObjectPools.cs.meta +11 -0
  102. package/Runtime/Integrations/VContainer/RelationalSceneAssignmentOptions.cs +16 -4
  103. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs +241 -0
  104. package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs.meta +11 -0
  105. package/Runtime/Integrations/Zenject/AssemblyInfo.cs +9 -0
  106. package/Runtime/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
  107. package/Runtime/Integrations/Zenject/DiContainerRelationalExtensions.cs +69 -2
  108. package/Runtime/Integrations/Zenject/RelationalComponentSceneInitializer.cs +89 -12
  109. package/Runtime/Integrations/Zenject/RelationalComponentsInstaller.cs +23 -1
  110. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs +44 -0
  111. package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs.meta +11 -0
  112. package/Runtime/Integrations/Zenject/RelationalSceneAssignmentOptions.cs +16 -10
  113. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs +243 -0
  114. package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs.meta +11 -0
  115. package/Runtime/Tags/AttributeMetadataCache.cs +1 -4
  116. package/Runtime/Utils/Buffers.cs +4 -4
  117. package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -2
  118. package/Runtime/Utils/SetTextureImportData.cs +3 -1
  119. package/Runtime/Utils/TextureScale.cs +10 -2
  120. package/Runtime/Visuals/UGUI/EnhancedImage.cs +6 -0
  121. package/Runtime/Visuals/UIToolkit/LayeredImage.cs +4 -1
  122. package/Samples~/DI - Reflex/README.md +527 -0
  123. package/Samples~/DI - Reflex/README.md.meta +7 -0
  124. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
  125. package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
  126. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
  127. package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
  128. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
  129. package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
  130. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
  131. package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
  132. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
  133. package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
  134. package/Samples~/DI - Reflex/Scripts.meta +8 -0
  135. package/Samples~/DI - Reflex.meta +8 -0
  136. package/Samples~/DI - VContainer/README.md +238 -56
  137. package/Samples~/DI - VContainer/Scripts/GameLifetimeScope.cs +22 -4
  138. package/Samples~/DI - VContainer/Scripts/RelationalConsumer.cs +5 -2
  139. package/Samples~/DI - VContainer/Scripts/Spawner.cs +113 -4
  140. package/Samples~/DI - Zenject/README.md +223 -58
  141. package/Samples~/DI - Zenject/Scripts/RelationalConsumer.cs +3 -0
  142. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs +37 -0
  143. package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs.meta +12 -0
  144. package/Samples~/DI - Zenject/Scripts/SpawnerZenject.cs +74 -3
  145. package/Samples~/Random - PRNG/README.md +2 -1
  146. package/Samples~/Relational Components - Basic/README.md +3 -1
  147. package/Samples~/Serialization - JSON/README.md +2 -1
  148. package/Samples~/Spatial Structures - 2D and 3D/README.md +2 -1
  149. package/Samples~/UGUI - EnhancedImage/README.md +2 -1
  150. package/Samples~/UI Toolkit - MultiFile Selector (Editor)/README.md +2 -1
  151. package/Tests/Editor/Attributes/AnimationEventHelpersTests.cs +16 -0
  152. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +32 -34
  153. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
  154. package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
  155. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
  156. package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
  157. package/Tests/Editor/Integrations/Reflex.meta +8 -0
  158. package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +21 -18
  159. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +164 -0
  160. package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs.meta +11 -0
  161. package/Tests/Editor/Integrations/VContainer/WallstopStudios.UnityHelpers.Tests.Editor.VContainer.asmdef +2 -1
  162. package/Tests/Editor/Integrations/Zenject/WallstopStudios.UnityHelpers.Tests.Editor.Zenject.asmdef +3 -2
  163. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +127 -0
  164. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs.meta +11 -0
  165. package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +25 -23
  166. package/Tests/Editor/PersistentDirectorySettingsTests.cs +58 -0
  167. package/Tests/Editor/PersistentDirectorySettingsTests.cs.meta +11 -0
  168. package/Tests/Editor/PrefabCheckerReportTests.cs +32 -0
  169. package/Tests/Editor/PrefabCheckerReportTests.cs.meta +11 -0
  170. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +63 -0
  171. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs.meta +11 -0
  172. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +1 -1
  173. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +38 -0
  174. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs.meta +11 -0
  175. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +1 -1
  176. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +12 -12
  177. package/Tests/Editor/Sprites/SpriteCropperTests.cs +9 -9
  178. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +3 -3
  179. package/Tests/Editor/Sprites/TexturePlatformNameHelperTests.cs +18 -0
  180. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +5 -5
  181. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +3 -3
  182. package/Tests/Editor/Sprites/TextureSettingsApplierWizardAdditionalTests.cs +4 -4
  183. package/Tests/Editor/Sprites/TextureSettingsApplierWizardTests.cs +4 -4
  184. package/Tests/Editor/Tools/ImageBlurToolTests.cs +22 -110
  185. package/Tests/Editor/Utils/CommonTestBase.cs +60 -1
  186. package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +6 -6
  187. package/Tests/Editor/Windows/FitTextureSizeWindowTests.cs +66 -74
  188. package/Tests/Runtime/Attributes/RelationalComponentInitializerTests.cs +4 -15
  189. package/Tests/Runtime/DataStructures/SpatialTree3DBoundsConsistencyTests.cs +29 -29
  190. package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
  191. package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
  192. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
  193. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
  194. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
  195. package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
  196. package/Tests/Runtime/Integrations/Reflex.meta +8 -0
  197. package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +257 -221
  198. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +91 -0
  199. package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs.meta +11 -0
  200. package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +251 -233
  201. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
  202. package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +6 -1
  203. package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +4 -1
  204. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs +30 -0
  205. package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs.meta +11 -0
  206. package/Tests/Runtime/Serialization/JsonConverterTests.cs +8 -12
  207. package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
  208. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +16 -5
  209. package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
  210. package/Tests/Runtime/Serialization/SerializerAdditionalTests.cs +12 -0
  211. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs +105 -0
  212. package/Tests/Runtime/Serialization/SerializerFileIoTests.cs.meta +11 -0
  213. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs +247 -0
  214. package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs.meta +11 -0
  215. package/Tests/Runtime/TestUtils/CommonTestBase.cs +99 -0
  216. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
  217. package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
  218. package/Tests/Runtime/Utils/CoroutineHandlerTests.cs +1 -1
  219. package/Tests/Runtime/Utils/LZMAComprehensiveTests.cs +1 -1
  220. package/Tests/Runtime/Utils/LZMATests.cs +1 -1
  221. package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +5 -5
  222. package/Tests/Runtime/Visuals/EnhancedImageTests.cs +25 -56
  223. package/Tests/Runtime/Visuals/VisualsTestHelpers.cs +1 -8
  224. package/Tests/TestUtils.meta +8 -0
  225. package/package-lock.json.meta +7 -0
  226. package/package.json +13 -4
  227. package/scripts/check-eol.ps1 +4 -5
  228. package/scripts/lint-tests.ps1 +156 -0
  229. package/scripts/lint-tests.ps1.meta +7 -0
  230. package/scripts/normalize-eol.ps1 +6 -9
  231. package/.github/workflows/csharpier.yml +0 -135
  232. package/CHANGELOG.md +0 -0
  233. package/EFFECTS_SYSTEM.md +0 -242
  234. package/MATH_AND_EXTENSIONS.md +0 -316
  235. package/SPATIAL_TREE_2D_PERFORMANCE.md +0 -238
  236. package/SPATIAL_TREE_3D_PERFORMANCE.md +0 -240
  237. /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
  238. /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
  239. /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
  240. /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
  241. /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
  242. /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
  243. /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
  244. /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
  245. /package/{HULLS.md → Docs/HULLS.md} +0 -0
  246. /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
  247. /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
  248. /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
  249. /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
  250. /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
  251. /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
  252. /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
  253. /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
  254. /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
  255. /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
  256. /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
  257. /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
  258. /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
  259. /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
  260. /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
  261. /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
  262. /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
  263. /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
  264. /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
package/README.md CHANGED
@@ -1,127 +1,61 @@
1
1
  # Unity Helpers
2
2
 
3
- [![Unity 2021.3+](https://img.shields.io/badge/Unity-2021.3%2B-000000?logo=unity&logoColor=white)](https://unity.com/releases/editor/whats-new/2021.3)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
- [![CSharpier](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier.yml)
6
- [![Markdown & JSON Lint/Format](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml)
7
- [![Lint Docs Links](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml)
3
+ [![Unity 2021.3+](https://img.shields.io/badge/Unity-2021.3%2B-000000?logo=unity&logoColor=white)](https://unity.com/releases/2021-lts)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![CSharpier](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier-autofix.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier-autofix.yml)
6
+ [![Markdown & JSON Lint/Format](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml)
7
+ [![Lint Docs Links](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml)
8
8
  [![Npm Publish](https://github.com/wallstop/unity-helpers/actions/workflows/npm-publish.yml/badge.svg?branch=main)](https://github.com/wallstop/unity-helpers/actions/workflows/npm-publish.yml)
9
9
 
10
- A comprehensive collection of high-performance utilities, data structures, and editor tools for Unity game development. Unity Helpers provides everything from blazing-fast random number generators and spatial trees to powerful editor wizards and component relationship management.
10
+ **Stop writing boilerplate. Start shipping features.**
11
11
 
12
- ---
13
-
14
- **📚 New to Unity Helpers?** Start here: [Getting Started Guide](GETTING_STARTED.md)
15
-
16
- **🔍 Looking for something specific?** Check the [Feature Index](INDEX.md)
17
-
18
- **❓ Need a definition?** See the [Glossary](GLOSSARY.md)
19
-
20
- ---
21
-
22
- ## 👋 First Time Here? Choose Your Path
23
-
24
- Unity Helpers provides tools for different roles and needs. Pick your path to get started quickly:
25
-
26
- ### 🎮 For Gameplay Programmers
27
-
28
- **You want:** Faster iteration on game features without sacrificing performance
29
-
30
- <!-- markdownlint-disable MD036 -->
31
-
32
- **Your quick wins:**
33
-
34
- 1. **[Random Number Generators](#random-number-generators)** - 10-15x faster with extensive API
35
- - Weighted selection, Gaussian distributions, noise maps - all built-in
36
- - Seedable for deterministic gameplay (replays, networking)
37
-
38
- 2. **[Relational Components](#auto-component-discovery)** - Stop writing GetComponent boilerplate
39
- - `[SiblingComponent]`, `[ParentComponent]`, `[ChildComponent]` - that's it
40
- - Works with DI containers (VContainer/Zenject)
41
-
42
- 3. **[Effects System](#effects-attributes-and-tags)** - Data-driven buffs/debuffs
43
- - Designers create effects as ScriptableObjects
44
- - Automatic stacking, timing, and tag management
45
-
46
- **Start here:** [Random in 60 Seconds](#random-number-generation) → Try it now
12
+ Unity Helpers eliminates entire categories of repetitive work with production-ready utilities that are 10-100x faster than writing it yourself. From auto-wiring components to blazing-fast spatial queries, this is the toolkit that pays for itself in the first hour.
47
13
 
48
- ---
49
-
50
- ### 🔧 For Tools & Editor Developers
51
-
52
- **You want:** Automate asset pipelines and validation workflows
53
-
54
- **Your quick wins:**
14
+ **What makes this different:**
55
15
 
56
- 1. **[Editor Tools](#editor-tools)** - 20+ tools for sprites, animations, validation
57
- - Sprite cropper, atlas generator, animation creator
58
- - Prefab checker with comprehensive validation rules
59
-
60
- 2. **[ScriptableObject Singletons](#singleton-utilities-odin-compatible)** - Global settings management
61
- - Auto-created from Resources/ folder
62
- - ODIN Inspector compatible
63
-
64
- 3. **[Reflection Helpers](#reflectionhelpers-blazing-fast-reflection)** - 100x faster than System.Reflection
65
- - IL-emitted delegates for field/property access
66
- - Safe for IL2CPP and AOT platforms
67
-
68
- **Start here:** [Editor Tools Guide](EDITOR_TOOLS_GUIDE.md)
16
+ - **10-15x faster** random generation than Unity.Random
17
+ - 🔌 **Zero boilerplate** component wiring with attributes
18
+ - 🎮 **Designer-friendly** effects system (buffs/debuffs as ScriptableObjects)
19
+ - 🌳 **O(log n)** spatial queries instead of O(n) loops
20
+ - 🛠️ **20+ editor tools** that automate sprite/animation workflows
21
+ - **4,000+ tests** ensuring production quality
69
22
 
70
23
  ---
71
24
 
72
- ### For Performance Engineers
73
-
74
- **You want:** Optimize hotspots and eliminate GC pressure
25
+ **📚 New to Unity Helpers?** Start here: [Getting Started Guide](Docs/GETTING_STARTED.md)
75
26
 
76
- **Your quick wins:**
27
+ **🔍 Looking for something specific?** Check the [Feature Index](Docs/INDEX.md)
77
28
 
78
- 1. **[Spatial Trees](#spatial-trees)** - O(log n) queries vs O(n) loops
79
- - QuadTree2D, KDTree2D/3D, RTree2D/3D
80
- - Scale to millions of objects
81
-
82
- 2. **[Buffering Pattern](#buffering-pattern)** - Zero-allocation queries
83
- - Reusable collections eliminate GC spikes
84
- - Professional-grade pooling with automatic cleanup
85
-
86
- 3. **[Data Structures](#data-structures)** - Production-ready containers
87
- - Heaps, tries, sparse sets with clear trade-offs
88
- - Performance benchmarks for informed decisions
89
-
90
- **Start here:** [Spatial Trees 2D Guide](SPATIAL_TREES_2D_GUIDE.md) + [Performance Benchmarks](#performance)
29
+ **❓ Need a definition?** See the [Glossary](Docs/GLOSSARY.md)
91
30
 
92
31
  ---
93
32
 
94
- ### 🏗️ For Architects & Tech Leads
95
-
96
- **You want:** Understand integration points and architectural patterns
33
+ ## 👋 First Time Here?
97
34
 
98
- **Your quick wins:**
35
+ **Pick your starting point based on your biggest pain point:**
99
36
 
100
- 1. **[DI Integration](#dependency-injection-integrations)** - VContainer & Zenject support
101
- - Automatic relational component wiring after DI injection
102
- - Scene and runtime instantiation patterns
37
+ | Your Problem | Your Solution | Time to Value |
38
+ | ------------------------------------ | ---------------------------------------------------------------------------------------- | ------------- |
39
+ | 🐌 Writing `GetComponent` everywhere | [**Relational Components**](#relational-components) - Auto-wire with attributes | 2 minutes |
40
+ | 🎮 Need buffs/debuffs system | [**Effects System**](#effects-attributes-and-tags) - Designer-friendly ScriptableObjects | 5 minutes |
41
+ | 🔍 Slow spatial searches | [**Spatial Trees**](#spatial-trees) - O(log n) queries | 5 minutes |
42
+ | 🎲 Random is too slow/limited | [**PRNG.Instance**](#random-number-generators) - 10-15x faster, extensive API | 1 minute |
43
+ | 💾 Need save/load system | [**Serialization**](#serialization) - Unity types just work | 10 minutes |
44
+ | 🛠️ Manual sprite workflows | [**Editor Tools**](#editor-tools) - 20+ automation tools | 3 minutes |
103
45
 
104
- 2. **[Serialization](#serialization)** - JSON/Protobuf with Unity type support
105
- - Schema evolution for save files that never break
106
- - Pooled buffers for hot paths
107
-
108
- 3. **[Feature Index](INDEX.md)** - Complete feature catalog
109
- - Alphabetical reference with links
110
- - Quick navigation to any feature
111
-
112
- **Start here:** [DI Integration Samples](#dependency-injection-integrations) or [Architecture Overview](#why-unity-helpers)
46
+ **Not sure where to start?** → [Getting Started Guide](Docs/GETTING_STARTED.md) walks through the top 3 features in 5 minutes.
113
47
 
114
48
  ---
115
49
 
116
- ## ⚡ Top 5 Features That Will Save You Weeks
50
+ ## ⚡ Top 5 Time-Savers
117
51
 
118
- Unity Helpers isn't just about performance - it's about **eliminating entire categories of repetitive work**. Here are the five features that deliver the biggest time savings:
52
+ These features eliminate entire categories of repetitive work. Pick one that solves your immediate pain:
119
53
 
120
- ### 1. 🔌 Auto-Wire Components (Relational Components)
54
+ ### 1. 🔌 Auto-Wire Components
121
55
 
122
- **Time saved: 10-20 minutes per script × 100+ scripts = 20+ hours**
56
+ #### ⏱️ 10-20 min/script × 100 scripts = 20+ hours saved
123
57
 
124
- Stop writing GetComponent boilerplate forever. Replace 20+ lines of repetitive code with 3 attributes.
58
+ Stop writing GetComponent boilerplate forever. Replace 20+ lines with 3 attributes:
125
59
 
126
60
  ```csharp
127
61
  // ❌ OLD WAY: 20+ lines per script
@@ -145,15 +79,15 @@ void Awake() => this.AssignRelationalComponents();
145
79
 
146
80
  **Bonus:** Works with VContainer/Zenject for automatic DI + relational wiring!
147
81
 
148
- [📖 Learn More](RELATIONAL_COMPONENTS.md) | [🎯 DI Integration](Samples~/DI%20-%20VContainer/README.md)
82
+ [📖 Learn More](Docs/RELATIONAL_COMPONENTS.md) | [🎯 DI – VContainer](Samples~/DI%20-%20VContainer/README.md) | [🎯 DI – Zenject](Samples~/DI%20-%20Zenject/README.md) | [🎯 DI – Reflex](Samples~/DI%20-%20Reflex/README.md)
149
83
 
150
84
  ---
151
85
 
152
- ### 2. 🎮 Data-Driven Effects System
86
+ ### 2. 🎮 Data-Driven Effects
153
87
 
154
- **Time saved: 2-4 hours per effect × 50 effects = 150+ hours**
88
+ #### ⏱️ 2-4 hours/effect × 50 effects = 150+ hours saved
155
89
 
156
- Designers create buffs/debuffs without touching code. Zero programmer time after initial setup.
90
+ Designers create buffs/debuffs as ScriptableObjects. Zero programmer time after 20-minute setup:
157
91
 
158
92
  ```csharp
159
93
  // Create once (ScriptableObject in editor):
@@ -172,15 +106,17 @@ player.RemoveAllEffectsWithTag("Haste"); // Batch removal
172
106
  - Cosmetic VFX/SFX that spawn/despawn automatically
173
107
  - Designer-friendly iteration without code changes
174
108
 
175
- [📖 Full Guide](EFFECTS_SYSTEM.md) | [🚀 5-Minute Tutorial](#effects-in-one-minute)
109
+ **Beyond buffs:** Tags become a powerful capability system for AI decisions, permission gates, state management, and complex gameplay interactions (invulnerability, stealth, elemental systems).
110
+
111
+ [📖 Full Guide](Docs/EFFECTS_SYSTEM.md) | [🚀 5-Minute Tutorial](Docs/EFFECTS_SYSTEM_TUTORIAL.md)
176
112
 
177
113
  ---
178
114
 
179
115
  ### 3. 💾 Unity-Aware Serialization
180
116
 
181
- **Time saved: 40+ hours on initial save system + preventing player data loss**
117
+ #### ⏱️ 40+ hours on initial implementation + prevents player data loss
182
118
 
183
- JSON/Protobuf that understands `Vector3`, `GameObject`, `Color` - no custom converters needed.
119
+ JSON/Protobuf that understands `Vector3`, `GameObject`, `Color` - no custom converters needed:
184
120
 
185
121
  ```csharp
186
122
  // Vector3, Color, GameObject references just work:
@@ -202,15 +138,15 @@ byte[] data = Serializer.JsonSerialize(saveData);
202
138
 
203
139
  **Real-world impact:** Ship updates without worrying about corrupting player saves.
204
140
 
205
- [📖 Serialization Guide](SERIALIZATION.md)
141
+ [📖 Serialization Guide](Docs/SERIALIZATION.md)
206
142
 
207
143
  ---
208
144
 
209
- ### 4. 🎱 Professional Pooling (Buffers<T>)
145
+ ### 4. 🎱 Professional Pooling
210
146
 
211
- **Time saved: Eliminates GC spikes = 5-10 FPS improvement in complex scenes**
147
+ #### ⏱️ Eliminates GC spikes = 5-10 FPS in complex scenes
212
148
 
213
- Zero-allocation queries with automatic cleanup. Thread-safe, production-grade pooling in one line.
149
+ Zero-allocation queries with automatic cleanup. Thread-safe pooling in one line:
214
150
 
215
151
  ```csharp
216
152
  // Get pooled buffer - automatically returned on scope exit
@@ -241,9 +177,9 @@ void ProcessEnemies(QuadTree2D<Enemy> enemyTree) {
241
177
 
242
178
  ### 5. 🛠️ Editor Tools Suite
243
179
 
244
- **Time saved: 1-2 hours per batch operation × weekly usage = hundreds of hours/year**
180
+ #### ⏱️ 1-2 hours/operation × weekly use = 100+ hours/year
245
181
 
246
- 20+ tools that automate sprite cropping, animation creation, atlas generation, prefab validation.
182
+ 20+ tools that automate sprite cropping, animation creation, atlas generation, prefab validation:
247
183
 
248
184
  **Common workflows:**
249
185
 
@@ -252,698 +188,125 @@ void ProcessEnemies(QuadTree2D<Enemy> enemyTree) {
252
188
  - **Prefab Checker**: Validate 200 prefabs for missing references → 1 click (was: manual QA)
253
189
  - **Atlas Generator**: Create sprite atlases from regex/labels → automated (was: manual setup)
254
190
 
255
- [📖 Editor Tools Guide](EDITOR_TOOLS_GUIDE.md)
256
-
257
- ---
258
-
259
- ## 💎 Hidden Gems Worth Discovering
260
-
261
- These powerful utilities solve common problems but might not be obvious from feature names:
262
-
263
- | Feature | What It Does | Time Saved |
264
- | ----------------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------ |
265
- | **[Predictive Targeting](#predictive-targeting-hit-moving-targets)** | Perfect ballistics for turrets/missiles in one call | 2-3 hours per shooting system |
266
- | **[UpdateShapeToSprite()](#lifecycle-helpers-no-more-destroyimmediate-bugs)** | Collider instantly matches sprite changes at runtime | 30 minutes per dynamic sprite system |
267
- | **[Coroutine Jitter](#coroutine-timing-with-jitter)** | Prevents 100 enemies polling on same frame | Eliminates frame spikes |
268
- | **[GetAngleWithSpeed()](#lifecycle-helpers-no-more-destroyimmediate-bugs)** | Smooth rotation toward target in one line | 15 minutes per rotating entity |
269
- | **[IL-Emitted Reflection](#reflectionhelpers-blazing-fast-reflection)** | 100x faster than System.Reflection, IL2CPP safe | Critical for serialization/modding |
270
- | **[SmartDestroy()](#lifecycle-helpers-no-more-destroyimmediate-bugs)** | Editor/runtime safe destruction (no scene corruption) | Prevents countless debugging hours |
271
- | **[Convex/Concave Hulls](#convex--concave-hull-generation)** | Generate territory borders from point clouds | 4-6 hours per hull algorithm |
191
+ [📖 Editor Tools Guide](Docs/EDITOR_TOOLS_GUIDE.md)
272
192
 
273
193
  ---
274
194
 
275
- ## 🚀 Common Workflows: Get Started in Minutes
276
-
277
- Jump straight to complete working examples for common scenarios:
278
-
279
- ### Save System in 5 Minutes
280
-
281
- ```csharp
282
- using WallstopStudios.UnityHelpers.Core.Serialization;
283
-
284
- // 1. Define your save data (Unity types just work)
285
- [System.Serializable]
286
- public class SaveData {
287
- public Vector3 playerPosition;
288
- public Quaternion playerRotation;
289
- public Color playerColor;
290
- public int gold;
291
- public List<string> inventory;
292
- }
293
-
294
- // 2. Save to file (one line)
295
- var data = new SaveData { /* ... */ };
296
- Serializer.WriteToJsonFile(data, "save.json", pretty: true);
297
-
298
- // 3. Load from file (one line)
299
- SaveData loaded = Serializer.ReadFromJsonFile<SaveData>("save.json");
300
-
301
- // ✅ Done! Vector3, Quaternion, Color all serialize correctly
302
- ```
303
-
304
- **What you get:** Schema evolution, Unity type support, human-readable files.
195
+ ## 🎁 Batteries-Included Extensions
305
196
 
306
- [📖 Full Serialization Guide](SERIALIZATION.md)
197
+ **Stop Googling "Unity how to..." for the 100th time.**
307
198
 
308
- ---
199
+ Unity Helpers includes 200+ extension methods that handle the tedious stuff you're tired of writing:
309
200
 
310
- ### Buff/Debuff System in 10 Minutes
201
+ ### Hierarchy Traversal (Optimized)
311
202
 
312
203
  ```csharp
313
- // 1. Create stats component
314
- public class CharacterStats : AttributesComponent {
315
- public Attribute Speed = 5f;
316
- public Attribute Health = 100f;
317
- }
204
+ // Get all ancestors without allocating
205
+ transform.GetAncestors(buffer); // 10x faster than recursive GetComponentInParent loops
318
206
 
319
- // 2. Create effect in editor (ScriptableObject)
320
- // - Right-click Create Wallstop Studios → Attribute Effect
321
- // - Name: "HasteEffect"
322
- // - Modification: Speed × 1.5
323
- // - Duration: 5 seconds
324
- // - Tags: "Haste"
207
+ // Find specific ancestor
208
+ Canvas canvas = transform.GetAncestor<Canvas>(); // Stops at first match
325
209
 
326
- // 3. Use it (zero boilerplate)
327
- player.ApplyEffect(hasteEffect); // Apply
328
- if (player.HasTag("Stunned")) return; // Query
329
- player.RemoveAllEffectsWithTag("Haste"); // Remove
330
-
331
- // ✅ Done! Designers can now create hundreds of effects without code
210
+ // Breadth-first child search with depth control
211
+ transform.GetDescendants(buffer, maxDepth: 2); // Avoid traversing entire tree
332
212
  ```
333
213
 
334
- **What you get:** Automatic stacking, duration management, cosmetic VFX/SFX, tags.
335
-
336
- [📖 5-Minute Tutorial](EFFECTS_SYSTEM_TUTORIAL.md) | [📖 Full Guide](EFFECTS_SYSTEM.md)
214
+ **Why this matters:** The naive way allocates arrays on every call. These methods use buffering and early-exit for hot paths.
337
215
 
338
- ---
339
-
340
- ### DI-Integrated Component Auto-Wiring in 2 Minutes
341
-
342
- **With VContainer:**
216
+ ### Unity Type Extensions
343
217
 
344
218
  ```csharp
345
- // 1. Register in LifetimeScope
346
- using WallstopStudios.UnityHelpers.Integrations.VContainer;
219
+ // Color averaging (4 methods: LAB, HSV, Weighted, Dominant)
220
+ Color teamColor = sprite.GetAverageColor(ColorAveragingMethod.LAB); // Perceptually accurate
347
221
 
348
- protected override void Configure(IContainerBuilder builder) {
349
- builder.RegisterRelationalComponents();
350
- }
351
-
352
- // 2. Use it (DI + relational fields both auto-wired)
353
- public class Player : MonoBehaviour {
354
- [Inject] private IInputService _input; // DI injected
355
- [SiblingComponent] private Animator _animator; // Auto-wired
356
- // No Awake() needed!
357
- }
222
+ // Collider auto-fitting
223
+ polygonCollider.UpdateShapeToSprite(); // Instant sprite → collider sync
358
224
 
359
- // Done! Both DI dependencies and hierarchy references wired automatically
360
- ```
225
+ // Smooth rotation in one line
226
+ transform.rotation = transform.GetAngleWithSpeed(target, rotationSpeed, Time.deltaTime);
361
227
 
362
- **With Zenject:**
228
+ // Safe destruction (works in editor AND runtime)
229
+ gameObject.SmartDestroy(); // No more #if UNITY_EDITOR everywhere
363
230
 
364
- ```csharp
365
- // 1. Add RelationalComponentsInstaller to SceneContext (toggle scene scan)
231
+ // Camera world bounds
232
+ Bounds visibleArea = Camera.main.OrthographicBounds(); // Perfect for culling/spawning
366
233
 
367
- // 2. Use it
368
- public class Player : MonoBehaviour {
369
- [Inject] private IInputService _input; // DI injected
370
- [SiblingComponent] private Animator _animator; // Auto-wired
234
+ // Predictive targeting (intercept moving targets)
235
+ if (Ballistics.TryGetInterceptVelocity(shooter, target, projectileSpeed, out Vector3 velocity)) {
236
+ Instantiate(projectile, shooter, Quaternion.LookRotation(velocity));
371
237
  }
372
-
373
- // ✅ Done! Scene objects wired on initialize, runtime via InstantiateComponentWithRelations()
374
238
  ```
375
239
 
376
- **What you get:** Zero boilerplate, works with scene + runtime objects, graceful fallback.
377
-
378
- [📖 VContainer Guide](Samples~/DI%20-%20VContainer/README.md) | [📖 Zenject Guide](Samples~/DI%20-%20Zenject/README.md)
379
-
380
- ---
381
-
382
- ### Fast Spatial Queries in 3 Minutes
240
+ ### Math That Should Be Built-In
383
241
 
384
242
  ```csharp
385
- using WallstopStudios.UnityHelpers.Core.DataStructure;
386
-
387
- // 1. Build tree (once or per frame for moving objects)
388
- Enemy[] enemies = FindObjectsOfType<Enemy>();
389
- var tree = new QuadTree2D<Enemy>(enemies, e => e.transform.position);
390
-
391
- // 2. Query efficiently (O(log n) vs O(n))
392
- using var lease = Buffers<Enemy>.List.Get(out List<Enemy> nearby);
393
- tree.GetElementsInRange(playerPos, radius: 10f, nearby);
394
-
395
- // 3. Process results (zero GC with pooled buffer)
396
- foreach (Enemy enemy in nearby) {
397
- enemy.TakeDamage(5f);
398
- }
399
-
400
- // ✅ Done! Scales to millions of objects, automatic buffer pooling
401
- ```
402
-
403
- **What you get:** 100-1000x faster queries, zero GC, production-ready.
404
-
405
- [📖 2D Trees Guide](SPATIAL_TREES_2D_GUIDE.md) | [📊 Performance](SPATIAL_TREE_2D_PERFORMANCE.md)
243
+ // Positive modulo (no more negative results!)
244
+ int index = (-1).PositiveMod(array.Length); // 4, not -1
406
245
 
407
- ---
408
-
409
- ## 📊 With vs Without Unity Helpers
410
-
411
- Real code comparisons showing exactly what you're avoiding:
412
-
413
- ### Component Wiring: 20 Lines → 4 Lines
414
-
415
- | Without Unity Helpers | With Unity Helpers |
416
- | ---------------------------------- | ----------------------------- |
417
- | **25 lines per script** | **4 lines total** |
418
- | Manual GetComponent calls | Attributes |
419
- | Manual null checks | Auto-validated |
420
- | Error-prone | Self-documenting |
421
- | Must update when hierarchy changes | Handles changes automatically |
422
-
423
- ```csharp
424
- // ❌ WITHOUT (25 lines)
425
- public class Player : MonoBehaviour {
426
- private SpriteRenderer sprite;
427
- private Animator animator;
428
- private Rigidbody2D rb;
429
- private Collider2D[] colliders;
430
-
431
- void Awake() {
432
- sprite = GetComponent<SpriteRenderer>();
433
- if (sprite == null) {
434
- Debug.LogError($"Missing SpriteRenderer on {name}!");
435
- }
436
-
437
- animator = GetComponent<Animator>();
438
- if (animator == null) {
439
- Debug.LogError($"Missing Animator on {name}!");
440
- }
441
-
442
- rb = GetComponentInParent<Rigidbody2D>();
443
- if (rb == null) {
444
- Debug.LogError($"Missing Rigidbody2D in parent of {name}!");
445
- }
446
-
447
- colliders = GetComponentsInChildren<Collider2D>();
448
- if (colliders.Length == 0) {
449
- Debug.LogWarning($"No Collider2D found in children of {name}!");
450
- }
451
- }
452
- }
246
+ // Wrapped add for ring buffers
247
+ index = index.WrappedAdd(2, capacity); // Handles overflow correctly
453
248
 
454
- // WITH (4 lines)
455
- public class Player : MonoBehaviour {
456
- [SiblingComponent] private SpriteRenderer sprite;
457
- [SiblingComponent] private Animator animator;
458
- [ParentComponent] private Rigidbody2D rb;
459
- [ChildComponent] private Collider2D[] colliders;
249
+ // Approximate equality with tolerance
250
+ if (transform.position.x.Approximately(target.x, 0.01f)) { /* close enough */ }
460
251
 
461
- void Awake() => this.AssignRelationalComponents();
462
- }
252
+ // Polyline simplification (Douglas–Peucker)
253
+ List<Vector2> simplified = LineHelper.Simplify(path, epsilon: 0.5f); // Reduce pathfinding waypoints
463
254
  ```
464
255
 
465
- **Impact:** 10-20 minutes saved per script × 100 scripts = **16-33 hours saved**
466
-
467
- ---
468
-
469
- ### Buff/Debuff System: 80 Lines → 0 Lines
470
-
471
- | Without Unity Helpers | With Unity Helpers |
472
- | ---------------------------- | -------------------------- |
473
- | **80-100 lines per effect** | **0 lines - editor only** |
474
- | Manual duration tracking | Automatic |
475
- | Manual stacking logic | Built-in |
476
- | Manual VFX lifecycle | Cosmetic system |
477
- | Code changes for new effects | Designer creates in editor |
256
+ ### Collection Utilities
478
257
 
479
258
  ```csharp
480
- // WITHOUT (80-100 lines per effect type)
481
- public class HasteEffect : MonoBehaviour {
482
- public float speedMultiplier = 1.5f;
483
- public float duration = 5f;
484
- public GameObject particlePrefab;
485
-
486
- private float remainingTime;
487
- private float originalSpeed;
488
- private GameObject spawnedParticles;
489
- private PlayerStats stats;
490
-
491
- void Start() {
492
- stats = GetComponent<PlayerStats>();
493
- originalSpeed = stats.speed;
494
- stats.speed *= speedMultiplier;
495
- remainingTime = duration;
496
-
497
- if (particlePrefab != null) {
498
- spawnedParticles = Instantiate(particlePrefab, transform);
499
- }
500
- }
259
+ // Infinite iterator (no extra allocation)
260
+ foreach (var item in itemList.Infinite()) { /* cycles forever */ }
501
261
 
502
- void Update() {
503
- remainingTime -= Time.deltaTime;
504
- if (remainingTime <= 0) {
505
- RemoveEffect();
506
- }
507
- }
508
-
509
- void RemoveEffect() {
510
- if (stats != null) {
511
- stats.speed = originalSpeed;
512
- }
513
- if (spawnedParticles != null) {
514
- Destroy(spawnedParticles);
515
- }
516
- Destroy(this);
517
- }
518
-
519
- void OnDestroy() {
520
- if (stats != null && stats.speed != originalSpeed) {
521
- stats.speed = originalSpeed;
522
- }
523
- }
524
-
525
- // TODO: Handle stacking (another 30 lines)
526
- // TODO: Handle reapplication (another 20 lines)
527
- // TODO: Handle early removal (another 10 lines)
528
- }
529
-
530
- // ✅ WITH (0 lines - create in editor)
531
- // Right-click → Create → Wallstop Studios → Attribute Effect
532
- // Fill in fields in Inspector:
533
- // - Modification: Speed × 1.5
534
- // - Duration: 5 seconds
535
- // - Cosmetic: particle prefab
536
- // - Tags: "Haste"
537
- //
538
- // Use: player.ApplyEffect(hasteEffect);
539
- ```
540
-
541
- **Impact:** 2-4 hours per effect type × 50 effects = **100-200 hours saved**
542
-
543
- ---
544
-
545
- ### Spatial Queries: O(n) → O(log n)
546
-
547
- | Without Unity Helpers | With Unity Helpers |
548
- | ---------------------------------------------- | ----------------------------------------- |
549
- | **O(n) linear search** | **O(log n) tree query** |
550
- | Scales poorly (10,000 objects = 10,000 checks) | Scales well (10,000 objects = ~13 checks) |
551
- | Allocates garbage | Zero GC with pooling |
552
-
553
- ```csharp
554
- // ❌ WITHOUT (slow, allocates)
555
- Enemy[] enemies = FindObjectsOfType<Enemy>(); // O(n) + allocation
556
- List<Enemy> nearby = new List<Enemy>(); // Allocation
557
-
558
- foreach (Enemy enemy in enemies) { // O(n) iteration
559
- float dist = Vector2.Distance(playerPos, enemy.transform.position);
560
- if (dist <= radius) {
561
- nearby.Add(enemy);
562
- }
563
- }
564
- // Result: 10,000 enemies = 10,000 distance checks per frame
565
- // GC pressure from new List every frame
262
+ // Aggregate bounds from multiple renderers
263
+ Bounds? combined = renderers.Select(r => r.bounds).GetBounds();
566
264
 
567
- // WITH (fast, zero GC)
568
- var tree = new QuadTree2D<Enemy>(enemies, e => e.transform.position);
265
+ // String similarity for fuzzy search
266
+ int distance = playerName.LevenshteinDistance("jon"); // "john" = 1, close match!
569
267
 
570
- using var lease = Buffers<Enemy>.List.Get(out List<Enemy> nearby);
571
- tree.GetElementsInRange(playerPos, radius, nearby);
572
- // Result: 10,000 enemies = ~13 tree node checks
573
- // Zero GC with pooled buffer reuse
268
+ // Case conversions (6 styles: Pascal, Camel, Snake, Kebab, Title, Constant)
269
+ string apiKey = "user_name".ToPascalCase(); // "UserName"
574
270
  ```
575
271
 
576
- **Performance gain:** 100-1000x faster queries, stable 60 FPS with thousands of objects
577
-
578
- [📊 See Benchmarks](SPATIAL_TREE_2D_PERFORMANCE.md)
272
+ **Full list:** [Math & Extensions Guide](Docs/MATH_AND_EXTENSIONS.md) | [Reflection Helpers](Docs/REFLECTION_HELPERS.md)
579
273
 
580
274
  ---
581
275
 
582
- ### Save System: 40 Hours → 5 Minutes
583
-
584
- | Without Unity Helpers | With Unity Helpers |
585
- | --------------------------------------- | ------------------ |
586
- | **40+ hours initial** | **5 minutes** |
587
- | Write custom converters for Unity types | Built-in |
588
- | Handle schema changes manually | Automatic |
589
- | Risk breaking old saves | Schema evolution |
590
-
591
- ```csharp
592
- // ❌ WITHOUT (need custom converters for every Unity type)
593
- public class Vector3Converter : JsonConverter<Vector3> {
594
- public override Vector3 Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) {
595
- // 20 lines of parsing logic...
596
- }
597
- public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) {
598
- // 15 lines of writing logic...
599
- }
600
- }
601
- // Repeat for: Vector2, Color, Quaternion, Matrix4x4, GameObject references...
602
- // Then configure options, handle file I/O, catch exceptions...
603
-
604
- // ✅ WITH (Unity types work out of the box)
605
- var data = new SaveData {
606
- position = new Vector3(1, 2, 3), // Works
607
- color = Color.cyan, // Works
608
- rotation = Quaternion.identity // Works
609
- };
276
+ ## 💎 Hidden Gems Worth Discovering
610
277
 
611
- Serializer.WriteToJsonFile(data, "save.json", pretty: true);
612
- SaveData loaded = Serializer.ReadFromJsonFile<SaveData>("save.json");
613
- ```
278
+ These powerful utilities solve specific problems that waste hours if you implement them yourself:
614
279
 
615
- **Impact:** 40+ hours initial development + preventing player data corruption on updates
280
+ | Feature | What It Does | Time Saved |
281
+ | ------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------- |
282
+ | **[Predictive Targeting](Docs/MATH_AND_EXTENSIONS.md#predictive-target)** | Perfect ballistics for turrets/missiles in one call | 2-3 hours per shooting system |
283
+ | **[Coroutine Jitter](Docs/MATH_AND_EXTENSIONS.md#unity-extensions)** | Prevents 100 enemies polling on same frame | Eliminates frame spikes |
284
+ | **[IL-Emitted Reflection](Docs/REFLECTION_HELPERS.md)** | 100x faster than System.Reflection, IL2CPP safe | Critical for serialization/modding |
285
+ | **[SmartDestroy()](Docs/MATH_AND_EXTENSIONS.md#lifecycle-helpers)** | Editor/runtime safe destruction (no scene corruption) | Prevents countless debugging hours |
286
+ | **[Convex/Concave Hulls](Docs/HULLS.md)** | Generate territory borders from point clouds | 4-6 hours per hull algorithm |
616
287
 
617
288
  ---
618
289
 
619
- ## Quick Onramp
290
+ ## Why Teams Choose Unity Helpers
620
291
 
621
- ### Why Unity Helpers? The Killer Features
292
+ **The Reality:** You're spending 30-40% of your time writing the same GetComponent boilerplate, spatial query loops, and save/load plumbing over and over. Unity Helpers gives you that time back.
622
293
 
623
- **⚡ Performance - Make Your Game Faster**
294
+ **Built for Real Projects:**
624
295
 
625
- - **10-15x faster random** ([PRNG.Instance](RANDOM_PERFORMANCE.md)) vs UnityEngine.Random + seedable for determinism
626
- - **Zero-allocation spatial queries** ([Buffering Pattern](#buffering-pattern)) no GC spikes, stable 60fps
627
- - **O(log n) spatial trees** ([Spatial Trees](SPATIAL_TREES_2D_GUIDE.md)) scale to millions of objects
628
- - **IL-emitted reflection** ([ReflectionHelpers](#reflectionhelpers-blazing-fast-reflection)) → field/property access 10-100x faster than System.Reflection
296
+ - **Production-tested** in shipped commercial games
297
+ - **4,000+ automated tests** catch edge cases before you hit them
298
+ - **Zero dependencies** - drop it in any project
299
+ - **IL2CPP/WebGL ready** with optimized SINGLE_THREADED paths
300
+ - ✅ **MIT Licensed** - use freely in commercial projects
629
301
 
630
- **🚀 Productivity - Ship Features Faster**
302
+ **Who This Is For:**
631
303
 
632
- - **Auto-wire components** ([Relational Components](RELATIONAL_COMPONENTS.md)) eliminate GetComponent boilerplate
633
- - **Data-driven effects** ([Effects System](EFFECTS_SYSTEM.md)) designers create 100s of buffs/debuffs without programmer
634
- - **20+ editor tools** ([Editor Tools](EDITOR_TOOLS_GUIDE.md)) automate sprite cropping, animations, atlases
635
- - **Predictive targeting** ([PredictCurrentTarget](#predictive-targeting-hit-moving-targets)) → perfect ballistics for turrets/missiles in 1 line
636
- - **Professional pooling** ([Buffers](#professional-grade-object-pooling)) → zero-alloc patterns with automatic cleanup
637
-
638
- **🛡️ Production-Ready - Never Break Player Saves**
639
-
640
- - **Protobuf schema evolution** ([Serialization](SERIALIZATION.md#protobuf-schema-evolution-the-killer-feature)) → add/remove fields without breaking old saves
641
- - **4,000+ test cases** → used in shipped commercial games
642
- - **Fully multiplatform** → WebGL, IL2CPP, Mobile, Desktop, Consoles with SINGLE_THREADED hot path optimizations
643
- - **IL2CPP optimized** → works with Unity's aggressive compiler, full AOT compatibility
644
- - **SmartDestroy** ([Lifecycle Helpers](#lifecycle-helpers-no-more-destroyimmediate-bugs)) → editor/runtime safe destruction, never corrupt scenes again
304
+ - **Indie devs** who need professional tools without enterprise overhead
305
+ - **Teams** who value performance and want their junior devs to use battle-tested code
306
+ - **Senior engineers** who are tired of re-implementing the same utilities every project
645
307
 
646
308
  ---
647
309
 
648
- TL;DR — Why use this?
649
-
650
- - Ship faster with production‑ready utilities that are (much) faster than stock Unity options.
651
- - Solve common problems: global settings/services, fast spatial queries, auto‑wiring components, robust serialization.
652
- - 4,000+ tests and diagrams make behavior and trade‑offs clear.
653
-
654
- Who is this for?
655
-
656
- - Unity devs who want pragmatic, high‑quality building blocks without adopting a full framework.
657
- - Teams that value performance, determinism, and predictable editor tooling.
658
-
659
- Install in 60 seconds
660
-
661
- ```json
662
- // Packages/manifest.json
663
- {
664
- "dependencies": {
665
- "com.wallstop-studios.unity-helpers": "https://github.com/wallstop/unity-helpers.git"
666
- }
667
- }
668
- ```
669
-
670
- First 5 minutes: three quick wins
671
-
672
- - Random: swap in a faster, seedable RNG
673
-
674
- ```csharp
675
- using WallstopStudios.UnityHelpers.Core.Random;
676
- IRandom rng = PRNG.Instance;
677
- int damage = rng.Next(10, 20);
678
- ```
679
-
680
- - Relational wiring: stop writing GetComponent
681
-
682
- ```csharp
683
- using WallstopStudios.UnityHelpers.Core.Attributes;
684
- public class Player : MonoBehaviour
685
- {
686
- [SiblingComponent] SpriteRenderer sprite;
687
- void Awake() => this.AssignRelationalComponents();
688
-
689
- > Need DI integration? Optional assemblies automatically light up when Zenject or VContainer is installed, exposing helpers like `RelationalComponentsInstaller` and `RegisterRelationalComponents()` so relational fields are assigned during container initialization.
690
-
691
- }
692
- ```
693
-
694
- - Spatial queries: O(log n) instead of O(n)
695
-
696
- ```csharp
697
- using WallstopStudios.UnityHelpers.Core.DataStructure;
698
- var tree = new QuadTree2D<Vector2>(points, p => p);
699
- var results = new List<Vector2>();
700
- tree.GetElementsInRange(playerPos, 10f, results);
701
- ```
702
-
703
- Pick the right spatial structure (2D)
704
-
705
- - Broad‑phase, many moving points: QuadTree2D
706
- - Nearest neighbors on static points: KDTree2D (Balanced)
707
- - Fast builds, good‑enough queries: KDTree2D (Unbalanced)
708
- - Objects with size, bounds queries: RTree2D
709
-
710
- Next steps
711
-
712
- - Browse the Guides: Singletons, Relational Components, Spatial Trees 2D/3D, Serialization
713
- - Skim the Performance pages for realistic expectations
714
- - Use the Editor Tools to automate common art/content workflows
715
-
716
- ## Table of Contents
717
-
718
- - [Quick Onramp](#quick-onramp)
719
- - [Why Unity Helpers?](#why-unity-helpers)
720
- - [Key Features](#key-features)
721
- - [Installation](#installation)
722
- - [Compatibility](#compatibility)
723
- - [Quick Start Guide](#quick-start-guide)
724
- - [Random Number Generation](#random-number-generation)
725
- - [Auto Component Discovery](#auto-component-discovery)
726
- - [Spatial Queries](#spatial-queries)
727
- - [Effects in One Minute](#effects-in-one-minute)
728
- - [Serialization in One Minute](#serialization-in-one-minute)
729
- - [Core Features](#core-features)
730
- - [Random Number Generators](#random-number-generators)
731
- - [Spatial Trees](#spatial-trees)
732
- - [Effects, Attributes, and Tags](#effects-attributes-and-tags)
733
- - [Component Attributes](#component-attributes)
734
- - [Relational Components Guide](#relational-components-guide)
735
- - [Serialization](#serialization)
736
- - [Serialization Guide (Full)](SERIALIZATION.md)
737
- - [Data Structures](#data-structures)
738
- - [Editor Tools](#editor-tools)
739
- - [Use Cases & Examples](#use-cases--examples)
740
- - [Performance](#performance)
741
- - [Contributing](#contributing)
742
- - [License](#license)
743
- - [Relational Components Guide](#relational-components-guide)
744
- - [API Index](#api-index)
745
- - [Buffering Pattern](#buffering-pattern)
746
- - [Docs Index](#docs-index)
747
-
748
- ## Why Unity Helpers?
749
-
750
- Unity Helpers was built to solve common game development challenges with **performance-first** solutions:
751
-
752
- - 🚀 **10-15x faster** random number generation compared to Unity's built-in Random
753
- - 🌳 **O(log n)** spatial queries instead of O(n) linear searches
754
- - 🔧 **20+ editor tools** to streamline your workflow
755
- - 📦 **Zero dependencies** - just import and use
756
- - ✅ **Production-tested** in shipped games
757
- - 🧪 **5000+ test cases** cover most of the public API and run before each release to catch regressions and prevent bugs
758
-
759
- ## Key Features
760
-
761
- ### High-Performance Random Number Generators
762
-
763
- **🎯 The Problem Unity.Random Solves Poorly:**
764
-
765
- - Slow (~65-85M ops/sec) - becomes a bottleneck in proc-gen and particle systems
766
- - Not seedable - impossible to create deterministic gameplay or replays
767
- - Not thread-safe - can only use on main thread
768
- - Basic API - missing weighted selection, distributions, noise generation
769
-
770
- **⚡ Unity Helpers Solution - PRNG.Instance:**
771
-
772
- - **10-15x faster** (655-885M ops/sec) - [See benchmarks](RANDOM_PERFORMANCE.md)
773
- - **Fully seedable** - same seed = identical results (perfect for networking, replays, proc-gen)
774
- - **Thread-safe** - via thread-local instances, use anywhere
775
- - **Game-ready API** - weighted selection, Gaussian distributions, Perlin noise, and more
776
-
777
- #### Quick Win Example
778
-
779
- ```csharp
780
- using WallstopStudios.UnityHelpers.Core.Random;
781
- using WallstopStudios.UnityHelpers.Core.Extension;
782
-
783
- // ❌ OLD WAY (Slow + Not Reproducible)
784
- void SpawnEnemies()
785
- {
786
- for (int i = 0; i < 1000; i++)
787
- {
788
- Vector2 pos = new Vector2(Random.value * 100, Random.value * 100);
789
- // No way to reproduce this exact pattern!
790
- // Slow - each call is expensive
791
- }
792
- }
793
-
794
- // ✅ NEW WAY (10x Faster + Fully Deterministic)
795
- void SpawnEnemies(int levelSeed)
796
- {
797
- IRandom rng = new IllusionFlow(seed: levelSeed);
798
-
799
- for (int i = 0; i < 1000; i++)
800
- {
801
- Vector2 pos = rng.NextVector2() * 100f;
802
- // Same seed = identical enemy layout every time!
803
- // 10x faster execution
804
- }
805
- }
806
- ```
807
-
808
- #### When This Really Matters
809
-
810
- 1. **Procedural Generation** - Levels, dungeons, terrain (thousands of random rolls per generation)
811
- 2. **Networked Multiplayer** - Clients generate identical results from shared seeds
812
- 3. **Replay Systems** - Reproduce exact gameplay sequences frame-by-frame
813
- 4. **Particle Systems** - Hundreds of random values per frame without GC
814
- 5. **Performance-Critical Loops** - Every microsecond counts in tight loops
815
-
816
- #### Rich API for Game Development
817
-
818
- Beyond basic `NextFloat()`, get game-ready features out of the box:
819
-
820
- ```csharp
821
- IRandom rng = PRNG.Instance;
822
-
823
- // Weighted random selection (loot tables, spawn weights)
824
- string[] loot = { "Common", "Rare", "Epic", "Legendary" };
825
- float[] weights = { 0.60f, 0.25f, 0.10f, 0.05f };
826
- string drop = loot[rng.NextWeightedIndex(weights)];
827
-
828
- // Gaussian distribution (natural-looking randomness for damage variance, spawn positions)
829
- float damage = rng.NextGaussian(mean: 100f, stdDev: 15f);
830
-
831
- // Perlin noise maps (terrain generation, texture synthesis)
832
- float[,] heightMap = new float[256, 256];
833
- rng.NextNoiseMap(heightMap, octaves: 4, persistence: 0.5f);
834
-
835
- // Collections (shuffling, random picks)
836
- List<Enemy> enemies = GetAllEnemies();
837
- rng.Shuffle(enemies); // Fisher-Yates shuffle
838
- Enemy target = rng.NextOf(enemies); // Random element
839
-
840
- // Unity types (spawning, effects)
841
- Vector3 randomPos = rng.NextVector3() * spawnRadius;
842
- Color randomColor = rng.NextColor();
843
- Quaternion randomRot = rng.NextRotation();
844
- ```
845
-
846
- #### Available Generators
847
-
848
- | Generator | Speed | Quality | Use Case |
849
- | ------------------------------- | --------- | --------- | ----------------------------- |
850
- | **IllusionFlow** ⭐ | Fast | Good | Default (via PRNG.Instance) |
851
- | **PcgRandom** | Very Fast | Excellent | Explicit seeding, determinism |
852
- | **RomuDuo** | Fastest | Good | Maximum speed needed |
853
- | **LinearCongruentialGenerator** | Fastest | Fair | Simple, fast generation |
854
-
855
- ⭐ **Recommended**: Use `PRNG.Instance` (currently IllusionFlow) for the best balance of speed, quality, and ease of use.
856
-
857
- [📊 Full Performance Benchmarks](RANDOM_PERFORMANCE.md)
858
-
859
- ### Spatial Trees for Fast Queries
860
-
861
- - **2D & 3D spatial trees** (QuadTree, OctTree, KDTree, RTree)
862
- - Perfect for collision detection, AI, visibility culling
863
- - **Massive performance gains** for games with many objects
864
- - Immutable trees with O(log n) query performance
865
- - Note on stability and semantics:
866
- - 2D: QuadTree2D and KdTree2D (balanced/unbalanced) return the same results for the same inputs/queries; they differ only in performance characteristics. RTree2D indexes rectangles (bounds), so results differ for sized objects.
867
- - 3D: KdTree3D (balanced/unbalanced) and OctTree3D can yield different results for the same inputs/queries due to boundary, tie‑breaking, and traversal semantics. RTree3D indexes 3D bounds and differs by design. See Spatial Tree Semantics for details.
868
-
869
- ### Powerful Component Attributes
870
-
871
- - `[ParentComponent]`, `[ChildComponent]`, `[SiblingComponent]` - Auto-wire components
872
- - `[ValidateAssignment]` - Catch missing references at edit time
873
- - `[DxReadOnly]` - Display calculated values in inspector
874
- - `[WShowIf]` - Conditional inspector fields
875
-
876
- See the in-depth guide: [Relational Components](RELATIONAL_COMPONENTS.md).
877
-
878
- ### 20+ Editor Tools
879
-
880
- - **Sprite tools**: Cropper, Atlas Generator, Animation Editor, Animation Creator (one‑click bulk from naming patterns)
881
- - **Texture tools**: Blur, Resize, Settings Applier
882
- - **Validation**: Prefab Checker, Animation Event Editor
883
- - **Automation**: ScriptableObject Singleton Creator
884
- - [Full Editor Tools Documentation](EDITOR_TOOLS_GUIDE.md)
885
-
886
- ### Core Math & Extensions
887
-
888
- - Numeric helpers, geometry primitives, Unity extensions, colors, collections, strings, directions.
889
- - See the guide: [Core Math & Extensions](MATH_AND_EXTENSIONS.md).
890
-
891
- #### At a Glance
892
-
893
- - `PositiveMod`, `WrappedAdd` — Safe cyclic arithmetic for indices/angles. See: [Numeric Helpers](MATH_AND_EXTENSIONS.md#numeric-helpers).
894
- - `LineHelper.Simplify` — Reduce polyline vertices with Douglas–Peucker. See: [Geometry](MATH_AND_EXTENSIONS.md#geometry).
895
- - `Line2D.Intersects` — Robust 2D segment intersection and closest-point helpers. See: [Geometry](MATH_AND_EXTENSIONS.md#geometry).
896
- - `RectTransform.GetWorldRect` — Axis-aligned world bounds for rotated UI. See: [Unity Extensions](MATH_AND_EXTENSIONS.md#unity-extensions).
897
- - `Camera.OrthographicBounds` — Compute visible world bounds for ortho cameras. See: [Unity Extensions](MATH_AND_EXTENSIONS.md#unity-extensions).
898
- - `Color.GetAverageColor` — LAB/HSV/Weighted/Dominant color averaging. See: [Color Utilities](MATH_AND_EXTENSIONS.md#color-utilities).
899
- - `IEnumerable.Infinite` — Cycle sequences without extra allocations. See: [Collections](MATH_AND_EXTENSIONS.md#collections).
900
- - `StringExtensions.LevenshteinDistance` — Edit distance for fuzzy matching. See: [Strings](MATH_AND_EXTENSIONS.md#strings).
901
-
902
- <a id="singleton-utilities-odin-compatible"></a>
903
-
904
- ### Singleton Utilities (ODIN‑compatible)
905
-
906
- - `RuntimeSingleton<T>` — Global component singleton with optional cross‑scene persistence. See the guide: [Singleton Utilities](SINGLETONS.md).
907
- - `ScriptableObjectSingleton<T>` — Global settings/data singleton loaded from `Resources/`, auto‑created by the editor tool. See the guide: [Singleton Utilities](SINGLETONS.md) and the tool: [ScriptableObject Singleton Creator](EDITOR_TOOLS_GUIDE.md#scriptableobject-singleton-creator).
908
-
909
- ## Docs Index
910
-
911
- **Start Here**
912
-
913
- - 🚀 Getting Started — [Getting Started Guide](GETTING_STARTED.md)
914
- - 🔍 Feature Index — [Complete A-Z Index](INDEX.md)
915
- - 📖 Glossary — [Term Definitions](GLOSSARY.md)
916
-
917
- **Core Guides**
918
-
919
- - Serialization Guide — [Serialization](SERIALIZATION.md)
920
- - Editor Tools Guide — [Editor Tools](EDITOR_TOOLS_GUIDE.md)
921
- - Math & Extensions — [Core Math & Extensions](MATH_AND_EXTENSIONS.md)
922
- - Singletons — [Singleton Utilities](SINGLETONS.md)
923
- - Relational Components — [Relational Components](RELATIONAL_COMPONENTS.md)
924
- - Effects System — [Effects System](EFFECTS_SYSTEM.md)
925
- - Data Structures — [Data Structures](DATA_STRUCTURES.md)
926
-
927
- **Spatial Trees**
928
-
929
- - 2D Spatial Trees Guide — [2D Spatial Trees Guide](SPATIAL_TREES_2D_GUIDE.md)
930
- - 3D Spatial Trees Guide — [3D Spatial Trees Guide](SPATIAL_TREES_3D_GUIDE.md)
931
- - Spatial Tree Semantics — [Spatial Tree Semantics](SPATIAL_TREE_SEMANTICS.md)
932
- - Spatial Tree 2D Performance — [Spatial Tree 2D Performance](SPATIAL_TREE_2D_PERFORMANCE.md)
933
- - Spatial Tree 3D Performance — [Spatial Tree 3D Performance](SPATIAL_TREE_3D_PERFORMANCE.md)
934
- - Hulls (Convex vs Concave) — [Hulls](HULLS.md)
935
-
936
- **Performance & Reference**
937
-
938
- - Random Performance — [Random Performance](RANDOM_PERFORMANCE.md)
939
- - Reflection Helpers — [Reflection Helpers](REFLECTION_HELPERS.md)
940
-
941
- **Project Info**
942
-
943
- - Changelog — [Changelog](CHANGELOG.md)
944
- - License — [License](LICENSE.md)
945
- - Third‑Party Notices — [Third‑Party Notices](THIRD_PARTY_NOTICES.md)
946
-
947
310
  ## Installation
948
311
 
949
312
  ### As Unity Package (Recommended)
@@ -980,6 +343,8 @@ See the in-depth guide: [Relational Components](RELATIONAL_COMPONENTS.md).
980
343
  2. Copy the contents to your project's `Assets` folder
981
344
  3. Unity will automatically import the package
982
345
 
346
+ ---
347
+
983
348
  ## Compatibility
984
349
 
985
350
  | Unity Version | Built-In | URP | HDRP |
@@ -1036,7 +401,7 @@ Some features in Unity Helpers use reflection internally (particularly **Protobu
1036
401
  - Missing fields after Protobuf deserialization
1037
402
  - Reflection helpers failing to find types at runtime
1038
403
 
1039
- **Solution: Use link.xml to preserve required types**
404
+ #### Solution: Use link.xml to preserve required types
1040
405
 
1041
406
  Create a `link.xml` file in your `Assets` folder to prevent stripping:
1042
407
 
@@ -1072,285 +437,47 @@ Create a `link.xml` file in your `Assets` folder to prevent stripping:
1072
437
 
1073
438
  - [Unity Manual: Managed Code Stripping](https://docs.unity3d.com/Manual/ManagedCodeStripping.html)
1074
439
  - [Protobuf-net and IL2CPP](https://github.com/protobuf-net/protobuf-net#il2cpp)
1075
- - [Serialization Guide: IL2CPP Warning](SERIALIZATION.md#️-il2cpp-and-code-stripping-warning)
1076
- - [Reflection Helpers: IL2CPP Warning](REFLECTION_HELPERS.md#️-il2cpp-code-stripping-considerations)
1077
-
1078
- ## Serialization
440
+ - [Serialization Guide: IL2CPP Warning](Docs/SERIALIZATION.md#️-il2cpp-and-code-stripping-warning)
441
+ - [Reflection Helpers: IL2CPP Warning](Docs/REFLECTION_HELPERS.md#️-il2cpp-code-stripping-considerations)
1079
442
 
1080
- - Formats: JSON (System.Text.Json), Protobuf (protobuf-net), SystemBinary (legacy)
1081
- - Unity-aware JSON converters (Vector2/3/4, Color, Matrix4x4, Type, GameObject)
1082
- - Pooled buffers to minimize GC; byte[] APIs for hot paths
443
+ ---
1083
444
 
1084
- JSON profiles
445
+ ## Quick Start Guide
1085
446
 
1086
- - Normal robust defaults (case-insensitive, includes fields, comments/trailing commas allowed)
1087
- - Pretty — human-friendly, indented
1088
- - Fast — strict, minimal with Unity converters (case-sensitive, strict numbers, no comments/trailing commas, IncludeFields=false)
1089
- - FastPOCO — strict, minimal, no Unity converters; best for pure POCO graphs
447
+ > 💡 **First time?** Skip to section #1 ([Relational Components](#1--auto-wire-components)) - it has the biggest immediate impact.
1090
448
 
1091
- Usage
449
+ Already read the [Top 5 Time-Savers](#-top-5-time-savers)? Jump directly to the [Core Features](#core-features) reference below, or check out the comprehensive [Getting Started Guide](Docs/GETTING_STARTED.md).
1092
450
 
1093
- ```csharp
1094
- using WallstopStudios.UnityHelpers.Core.Serialization;
451
+ ---
1095
452
 
1096
- var normal = Serializer.CreateNormalJsonOptions();
1097
- var pretty = Serializer.CreatePrettyJsonOptions();
1098
- var fast = Serializer.CreateFastJsonOptions();
1099
- var fastPOCO = Serializer.CreateFastPocoJsonOptions();
453
+ ## Core Features
1100
454
 
1101
- byte[] buf = null;
1102
- Serializer.JsonSerialize(model, fast, ref buf); // pooled, minimal allocs
1103
- Serializer.JsonSerialize(model, fast, sizeHint: 512*1024, ref buf); // preallocate for large outputs
1104
- var rt = Serializer.JsonDeserialize<MyType>(buf, null, fast); // span-based; no string alloc
1105
- ```
455
+ ### Random Number Generators
1106
456
 
1107
- When to use what
457
+ Unity Helpers includes **12 high-quality random number generators**, all implementing a rich `IRandom` interface:
1108
458
 
1109
- - Save/configs: Normal or Pretty
1110
- - Hot loops/large arrays: Fast or FastPOCO (POCO-only graphs)
1111
- - Mixed graphs with Unity types: Fast
459
+ #### Available Generators
1112
460
 
1113
- See the full guide for trade-offs, tips, and examples: [Serialization Guide](SERIALIZATION.md)
461
+ | Generator | Speed | Quality | Use Case |
462
+ | ------------------------------- | --------- | --------- | ---------------------------------------- |
463
+ | **IllusionFlow** ⭐ | Fast | Good | Default choice (via PRNG.Instance) |
464
+ | **PcgRandom** | Very Fast | Excellent | Deterministic gameplay; explicit seeding |
465
+ | **RomuDuo** | Fastest | Good | Maximum performance needed |
466
+ | **LinearCongruentialGenerator** | Fastest | Fair | Simple, fast generation |
467
+ | **XorShiftRandom** | Very Fast | Good | General purpose |
468
+ | **XoroShiroRandom** | Very Fast | Good | General purpose |
469
+ | **SplitMix64** | Very Fast | Good | Initialization, hashing |
470
+ | **SquirrelRandom** | Moderate | Good | Hash-based generation |
471
+ | **WyRandom** | Moderate | Good | Hashing applications |
472
+ | **DotNetRandom** | Moderate | Good | .NET compatibility |
473
+ | **SystemRandom** | Slow | Good | Backward compatibility |
474
+ | **UnityRandom** | Very Slow | Good | Unity compatibility |
1114
475
 
1115
- ## Quick Start Guide
476
+ **Recommended**: Use `PRNG.Instance` (currently IllusionFlow)
1116
477
 
1117
- ### Random Number Generation
478
+ #### Rich API
1118
479
 
1119
- Replace Unity's Random with high-performance alternatives:
1120
-
1121
- ```csharp
1122
- using System;
1123
- using UnityEngine;
1124
- using WallstopStudios.UnityHelpers.Core.Random;
1125
- using WallstopStudios.UnityHelpers.Core.Extension; // extension APIs like NextVector2(), NextWeightedIndex()
1126
-
1127
- // Use the recommended default (currently IllusionFlow)
1128
- IRandom random = PRNG.Instance;
1129
-
1130
- // Basic random values
1131
- float chance = random.NextFloat(); // 0.0f to 1.0f
1132
- int damage = random.Next(10, 20); // 10 to 19
1133
- bool critical = random.NextBool(); // true or false
1134
-
1135
- // Advanced features
1136
- Vector2 position = random.NextVector2(); // Random 2D position (extension method)
1137
- Guid playerId = random.NextGuid(); // UUIDv4
1138
- float gaussian = random.NextGaussian(); // Normal distribution
1139
-
1140
- // Random selection
1141
- string[] lootTable = { "Sword", "Shield", "Potion" };
1142
- string item = random.NextOf(lootTable);
1143
-
1144
- // Weighted bool
1145
- float probability = 0.7f;
1146
- bool lucky = random.NextBool(probability);
1147
-
1148
- // Noise generation
1149
- float[,] noiseMap = new float[256, 256];
1150
- random.NextNoiseMap(noiseMap, octaves: 4);
1151
- ```
1152
-
1153
- **Why use PRNG.Instance?**
1154
-
1155
- - 10-15x faster than Unity.Random
1156
- - Seedable for deterministic gameplay
1157
- - Thread-safe access (uses a thread-local instance)
1158
- - Extensive API for common patterns
1159
-
1160
- [📊 View Random Performance Benchmarks](RANDOM_PERFORMANCE.md)
1161
-
1162
- ### Auto Component Discovery
1163
-
1164
- Stop writing GetComponent calls everywhere:
1165
-
1166
- ```csharp
1167
- using WallstopStudios.UnityHelpers.Core.Attributes;
1168
- using UnityEngine;
1169
-
1170
- public class Player : MonoBehaviour
1171
- {
1172
- // Automatically finds SpriteRenderer on same GameObject
1173
- [SiblingComponent]
1174
- private SpriteRenderer spriteRenderer;
1175
-
1176
- // Finds Rigidbody2D on same GameObject, but it's optional
1177
- [SiblingComponent(Optional = true)]
1178
- private Rigidbody2D rigidbody;
1179
-
1180
- // Finds Camera in parent hierarchy
1181
- [ParentComponent]
1182
- private Camera parentCamera;
1183
-
1184
- // Only search ancestors, not siblings
1185
- [ParentComponent(OnlyAncestors = true)]
1186
- private Transform[] parentTransforms;
1187
-
1188
- // Finds all PolygonCollider2D in children
1189
- [ChildComponent]
1190
- private List<PolygonCollider2D> childColliders;
1191
-
1192
- // Only search descendants
1193
- [ChildComponent(OnlyDescendants = true)]
1194
- private EdgeCollider2D edgeCollider;
1195
-
1196
- private void Awake()
1197
- {
1198
- // One call wires up everything!
1199
- this.AssignRelationalComponents();
1200
-
1201
- // All fields are now assigned (or logged errors if missing)
1202
- spriteRenderer.color = Color.red;
1203
- }
1204
- }
1205
- ```
1206
-
1207
- **Benefits:**
1208
-
1209
- - Cleaner, more declarative code
1210
- - Safer defaults (required by default; opt-in `Optional = true`)
1211
- - Filters by tag/name, limit results, control depth, support interfaces
1212
- - Works with single fields, arrays, `List<T>`, and `HashSet<T>`
1213
- - Descriptive error logging for missing required components
1214
- - Honors `IncludeInactive` (include disabled/inactive when true)
1215
-
1216
- For a complete walkthrough with recipes, FAQs, and troubleshooting, see [Relational Components](RELATIONAL_COMPONENTS.md) (Troubleshooting: [Tips & Troubleshooting](RELATIONAL_COMPONENTS.md#troubleshooting)).
1217
-
1218
- ### Spatial Queries
1219
-
1220
- Fast spatial lookups for AI, collision detection, and more:
1221
-
1222
- ```csharp
1223
- using WallstopStudios.UnityHelpers.Core.DataStructure;
1224
- using UnityEngine;
1225
-
1226
- public class EnemyManager : MonoBehaviour
1227
- {
1228
- private QuadTree2D<Enemy> enemyTree;
1229
-
1230
- void Start()
1231
- {
1232
- // Build tree from all enemies
1233
- Enemy[] enemies = FindObjectsOfType<Enemy>();
1234
- enemyTree = new QuadTree2D<Enemy>(enemies, e => e.transform.position);
1235
- }
1236
-
1237
- // Find all enemies in radius (O(log n) instead of O(n))
1238
- public List<Enemy> GetEnemiesInRange(Vector2 position, float radius)
1239
- {
1240
- List<Enemy> results = new();
1241
- enemyTree.GetElementsInRange(position, radius, results);
1242
- return results;
1243
- }
1244
-
1245
- // Find enemies in a rectangular area
1246
- public List<Enemy> GetEnemiesInArea(Bounds area)
1247
- {
1248
- List<Enemy> results = new();
1249
- enemyTree.GetElementsInBounds(area, results);
1250
- return results;
1251
- }
1252
-
1253
- // Find nearest enemies fast
1254
- public List<Enemy> GetNearestEnemies(Vector2 position, int count)
1255
- {
1256
- List<Enemy> results = new();
1257
- enemyTree.GetApproximateNearestNeighbors(position, count, results);
1258
- return results;
1259
- }
1260
- }
1261
- ```
1262
-
1263
- **Important:** Spatial trees are **immutable** - rebuild them when positions change.
1264
-
1265
- [📊 View 2D Performance Benchmarks](SPATIAL_TREE_2D_PERFORMANCE.md) | [📊 View 3D Performance Benchmarks](SPATIAL_TREE_3D_PERFORMANCE.md)
1266
-
1267
- For zero‑alloc queries and stable GC, see the [Buffering Pattern](#buffering-pattern).
1268
-
1269
- ### Effects in One Minute
1270
-
1271
- Author stackable buffs/debuffs as assets and apply/remove at runtime.
1272
-
1273
- ```csharp
1274
- using WallstopStudios.UnityHelpers.Core.Effects;
1275
-
1276
- // ScriptableObject: AttributeEffect (create via menu)
1277
- // Contains: modifications (e.g., Speed x1.5), tags (e.g., "Haste"), duration
1278
-
1279
- // Apply to a target GameObject
1280
- EffectHandle? handle = target.ApplyEffect(hasteEffect);
1281
-
1282
- // Later: remove one stack or all
1283
- if (handle.HasValue) target.RemoveEffect(handle.Value);
1284
- target.RemoveAllEffectsWithTag("Haste");
1285
- ```
1286
-
1287
- Why use it
1288
-
1289
- - Declarative authoring, automatic stacking/timing/tagging, clean removal.
1290
- - Cosmetic hooks for VFX/SFX via `CosmeticEffectData`.
1291
-
1292
- ### Serialization in One Minute
1293
-
1294
- Serialize/deserialize with Unity‑aware JSON profiles; use pooled buffers for hot paths.
1295
-
1296
- ```csharp
1297
- using WallstopStudios.UnityHelpers.Core.Serialization;
1298
-
1299
- var opts = Serializer.CreateFastJsonOptions(); // or Pretty/Normal
1300
-
1301
- // Serialize into a pooled buffer
1302
- byte[] buf = null;
1303
- Serializer.JsonSerialize(model, opts, ref buf);
1304
-
1305
- // Deserialize directly from bytes (no string alloc)
1306
- var model2 = Serializer.JsonDeserialize<MyType>(buf, null, opts);
1307
- ```
1308
-
1309
- Tips
1310
-
1311
- - Pretty/Normal for configs; Fast for hot loops; FastPOCO for pure POCO graphs.
1312
- - Unity converters handle Vector/Color/Matrix/GameObject references.
1313
-
1314
- ### Choosing Spatial Structures
1315
-
1316
- - QuadTree2D — Static or semi-static point data in 2D. Great for circular and rectangular queries, approximate kNN. Immutable (rebuild when positions change).
1317
- - KdTree2D/3D — Excellent nearest-neighbor performance for points. Balanced variant for uniform data; unbalanced for quicker builds. Immutable.
1318
- - RTree2D — For rectangular/sized objects (sprites, colliders). Great for bounds and radius intersection queries. Immutable.
1319
- - SpatialHash2D/3D — Many moving objects that are fairly uniformly distributed. Cheap updates; fast approximate neighborhood queries.
1320
-
1321
- Rules of thumb:
1322
-
1323
- - Frequent movement? Prefer SpatialHash. Static or batched rebuilds? Use QuadTree/KdTree/RTree.
1324
- - Query by area/rectangle? RTree2D excels. Nearest neighbors? KdTree. Broad-phase neighbor checks? SpatialHash.
1325
-
1326
- ## Core Features
1327
-
1328
- ### Random Number Generators
1329
-
1330
- Unity Helpers includes **12 high-quality random number generators**, all implementing a rich `IRandom` interface:
1331
-
1332
- #### Available Generators
1333
-
1334
- | Generator | Speed | Quality | Use Case |
1335
- | ------------------------------- | --------- | --------- | ---------------------------------------- |
1336
- | **IllusionFlow** ⭐ | Fast | Good | Default choice (via PRNG.Instance) |
1337
- | **PcgRandom** | Very Fast | Excellent | Deterministic gameplay; explicit seeding |
1338
- | **RomuDuo** | Fastest | Good | Maximum performance needed |
1339
- | **LinearCongruentialGenerator** | Fastest | Fair | Simple, fast generation |
1340
- | **XorShiftRandom** | Very Fast | Good | General purpose |
1341
- | **XoroShiroRandom** | Very Fast | Good | General purpose |
1342
- | **SplitMix64** | Very Fast | Good | Initialization, hashing |
1343
- | **SquirrelRandom** | Moderate | Good | Hash-based generation |
1344
- | **WyRandom** | Moderate | Good | Hashing applications |
1345
- | **DotNetRandom** | Moderate | Good | .NET compatibility |
1346
- | **SystemRandom** | Slow | Good | Backward compatibility |
1347
- | **UnityRandom** | Very Slow | Good | Unity compatibility |
1348
-
1349
- ⭐ **Recommended**: Use `PRNG.Instance` (currently IllusionFlow)
1350
-
1351
- #### Rich API
1352
-
1353
- All generators implement `IRandom` with extensive functionality:
480
+ All generators implement `IRandom` with extensive functionality:
1354
481
 
1355
482
  ```csharp
1356
483
  IRandom random = PRNG.Instance;
@@ -1371,7 +498,6 @@ Quaternion rot = random.NextRotation(); // Random rotation
1371
498
 
1372
499
  // Distributions
1373
500
  float gaussian = random.NextGaussian(mean: 0f, stdDev: 1f);
1374
- float triangular = random.NextTriangular(min: 0f, max: 1f, mode: 0.5f);
1375
501
 
1376
502
  // Collections
1377
503
  T item = random.NextOf(collection); // Random element
@@ -1397,13 +523,15 @@ IRandom replay = new IllusionFlow(seed: 12345);
1397
523
  // Both will generate identical values
1398
524
  ```
1399
525
 
1400
- Threading
526
+ **Threading:**
1401
527
 
1402
528
  - Do not share a single RNG instance across threads.
1403
- - Use `PRNG.Instance` for a thread-local default, or use each generators `TypeName.Instance` (e.g., `IllusionFlow.Instance`, `PcgRandom.Instance`).
529
+ - Use `PRNG.Instance` for a thread-local default, or use each generator's `TypeName.Instance` (e.g., `IllusionFlow.Instance`, `PcgRandom.Instance`).
1404
530
  - Alternatively, create one separate instance per thread.
1405
531
 
1406
- [📊 Performance Comparison](RANDOM_PERFORMANCE.md)
532
+ [📊 Performance Comparison](Docs/RANDOM_PERFORMANCE.md)
533
+
534
+ ---
1407
535
 
1408
536
  ### Spatial Trees
1409
537
 
@@ -1470,15 +598,73 @@ tree.GetElementsInRange(center, radius: 50f, results);
1470
598
  - Single queries
1471
599
  - Already using Unity's physics system
1472
600
 
1473
- [📊 2D Benchmarks](SPATIAL_TREE_2D_PERFORMANCE.md) | [📊 3D Benchmarks](SPATIAL_TREE_3D_PERFORMANCE.md)
601
+ [📊 2D Benchmarks](Docs/SPATIAL_TREE_2D_PERFORMANCE.md) | [📊 3D Benchmarks](Docs/SPATIAL_TREE_3D_PERFORMANCE.md)
602
+
603
+ For behavior details and edge cases, see: [Spatial Tree Semantics](Docs/SPATIAL_TREE_SEMANTICS.md)
604
+
605
+ ---
606
+
607
+ ### Relational Components
608
+
609
+ Stop writing GetComponent boilerplate. Auto-wire components using attributes.
610
+
611
+ **Key attributes:**
612
+
613
+ - `[SiblingComponent]` - Find components on same GameObject
614
+ - `[ParentComponent]` - Find components in parent hierarchy
615
+ - `[ChildComponent]` - Find components in children
616
+ - `[ValidateAssignment]` - Validate at edit time, show errors in inspector
617
+ - `[NotNull]` - Must be assigned in inspector
618
+ - `[DxReadOnly]` - Read-only display in inspector
619
+ - `[WShowIf]` - Conditional display based on field values
620
+
621
+ **Quick example:**
622
+
623
+ ```csharp
624
+ using WallstopStudios.UnityHelpers.Core.Attributes;
625
+
626
+ public class Enemy : MonoBehaviour
627
+ {
628
+ // Find on same GameObject
629
+ [SiblingComponent]
630
+ private Animator animator;
631
+
632
+ // Find in parent
633
+ [ParentComponent]
634
+ private EnemySpawner spawner;
635
+
636
+ // Find in children
637
+ [ChildComponent]
638
+ private List<Weapon> weapons;
639
+
640
+ // Optional component (no error if missing)
641
+ [SiblingComponent(Optional = true)]
642
+ private AudioSource audioSource;
643
+
644
+ // Only search direct children/parents
645
+ [ParentComponent(OnlyAncestors = true)]
646
+ private Transform[] parentHierarchy;
647
+
648
+ // Include inactive components
649
+ [ChildComponent(IncludeInactive = true)]
650
+ private ParticleSystem[] effects;
651
+
652
+ private void Awake()
653
+ {
654
+ this.AssignRelationalComponents();
655
+ }
656
+ }
657
+ ```
658
+
659
+ See the in-depth guide: [Relational Components](Docs/RELATIONAL_COMPONENTS.md).
1474
660
 
1475
- For behavior details and edge cases, see: [Spatial Tree Semantics](SPATIAL_TREE_SEMANTICS.md)
661
+ ---
1476
662
 
1477
663
  ### Effects, Attributes, and Tags
1478
664
 
1479
665
  Create data-driven gameplay effects that modify stats, apply tags, and drive cosmetics.
1480
666
 
1481
- Key pieces:
667
+ **Key pieces:**
1482
668
 
1483
669
  - `AttributeEffect` — ScriptableObject that bundles stat changes, tags, cosmetics, and duration.
1484
670
  - `EffectHandle` — Unique ID for one application instance; remove/refresh specific stacks.
@@ -1486,13 +672,7 @@ Key pieces:
1486
672
  - `TagHandler` — Counts and queries string tags for gating gameplay (e.g., "Stunned").
1487
673
  - `CosmeticEffectData` — Prefab-like container of behaviors shown while an effect is active.
1488
674
 
1489
- Why this helps:
1490
-
1491
- - Decouples gameplay logic from presentation and from effect sources.
1492
- - Safe stacking and independent removal via handles and tag reference counts.
1493
- - Designer-friendly: author once in assets, reuse everywhere.
1494
-
1495
- Quick start:
675
+ **Quick example:**
1496
676
 
1497
677
  ```csharp
1498
678
  using WallstopStudios.UnityHelpers.Tags;
@@ -1524,7 +704,7 @@ if (handle.HasValue)
1524
704
  if (player.HasTag("Stunned")) { /* disable input */ }
1525
705
  ```
1526
706
 
1527
- Details at a glance:
707
+ **Details at a glance:**
1528
708
 
1529
709
  - `ModifierDurationType.Instant` — applies permanently; returns null handle.
1530
710
  - `ModifierDurationType.Duration` — temporary; expires automatically; reapply can reset if enabled.
@@ -1532,84 +712,14 @@ Details at a glance:
1532
712
  - `AttributeModification` order: Addition → Multiplication → Override.
1533
713
  - `CosmeticEffectData.RequiresInstancing` — instance per application or reuse shared presenters.
1534
714
 
1535
- Tips:
1536
-
1537
- - Use the Attribute Metadata Cache generator to power dropdowns and avoid typos in attribute names.
1538
- - Prefer `%`-style changes with Multiplication and small flat changes with Addition.
1539
- - Keep tag strings consistent; centralize in constants to avoid mistakes.
1540
-
1541
- Further reading: see the full guide [Effects System](EFFECTS_SYSTEM.md).
1542
-
1543
- ### Component Attributes
1544
-
1545
- Streamline component relationships and inspector validation.
1546
-
1547
- #### Relational Component Attributes
1548
-
1549
- ```csharp
1550
- public class Enemy : MonoBehaviour
1551
- {
1552
- // Find on same GameObject
1553
- [SiblingComponent]
1554
- private Animator animator;
1555
-
1556
- // Find in parent
1557
- [ParentComponent]
1558
- private EnemySpawner spawner;
1559
-
1560
- // Find in children
1561
- [ChildComponent]
1562
- private List<Weapon> weapons;
1563
-
1564
- // Optional component (no error if missing)
1565
- [SiblingComponent(Optional = true)]
1566
- private AudioSource audioSource;
1567
-
1568
- // Only search direct children/parents
1569
- [ParentComponent(OnlyAncestors = true)]
1570
- private Transform[] parentHierarchy;
1571
-
1572
- // Include inactive components
1573
- [ChildComponent(IncludeInactive = true)]
1574
- private ParticleSystem[] effects;
1575
-
1576
- private void Awake()
1577
- {
1578
- this.AssignRelationalComponents();
1579
- }
1580
- }
1581
- ```
1582
-
1583
- #### Validation Attributes
1584
-
1585
- ```csharp
1586
- public class PlayerController : MonoBehaviour
1587
- {
1588
- // Validates at edit time, shows errors in inspector
1589
- [ValidateAssignment]
1590
- [SerializeField] private Rigidbody2D rigidbody;
1591
-
1592
- // Must be assigned in inspector
1593
- [NotNull]
1594
- [SerializeField] private PlayerData playerData;
715
+ **Power Pattern:** Tags aren't just for buffs—use them to build robust capability systems for invulnerability, AI decision-making, permission gates, state management, and elemental interactions. See [Advanced Scenarios](Docs/EFFECTS_SYSTEM.md#advanced-scenarios-beyond-buffs-and-debuffs) for patterns.
1595
716
 
1596
- // Read-only display in inspector
1597
- [DxReadOnly]
1598
- public float currentHealth;
717
+ Further reading: see the full guide [Effects System](Docs/EFFECTS_SYSTEM.md).
1599
718
 
1600
- // Conditional display based on enum
1601
- public enum Mode { Simple, Advanced }
1602
- public Mode currentMode;
1603
-
1604
- [WShowIf(nameof(currentMode), expectedValues = new object[] { Mode.Advanced })]
1605
- public float advancedParameter;
1606
- }
1607
- ```
719
+ ---
1608
720
 
1609
721
  ### Serialization
1610
722
 
1611
- [Full guide: Serialization](SERIALIZATION.md)
1612
-
1613
723
  Fast, compact serialization for save systems, config, and networking.
1614
724
 
1615
725
  This package provides three serialization technologies:
@@ -1620,30 +730,26 @@ This package provides three serialization technologies:
1620
730
 
1621
731
  All are exposed via `WallstopStudios.UnityHelpers.Core.Serialization.Serializer`.
1622
732
 
1623
- #### Formats Provided
733
+ #### JSON Profiles
1624
734
 
1625
- - Json
1626
- - Human‑readable; great for configs, save files you want to inspect or diff.
1627
- - Includes converters for Unity types (Vector2/3/4, Color, Matrix4x4, GameObject, Type, enums as strings, cycles ignored, case‑insensitive, includes fields).
1628
- - Protobuf (protobuf‑net)
1629
- - Small, fast, ideal for networking and large save payloads.
1630
- - Forward/backward compatible when evolving messages (see tips below).
1631
- - SystemBinary (BinaryFormatter)
1632
- - Only for legacy or trusted, same‑version, local data. Not recommended for long‑term persistence or untrusted input (security + versioning issues).
735
+ - **Normal** — robust defaults (case-insensitive, includes fields, comments/trailing commas allowed)
736
+ - **Pretty** human-friendly, indented
737
+ - **Fast** strict, minimal with Unity converters (case-sensitive, strict numbers, no comments/trailing commas, IncludeFields=false)
738
+ - **FastPOCO** — strict, minimal, no Unity converters; best for pure POCO graphs
1633
739
 
1634
740
  #### When To Use What
1635
741
 
1636
- - Use Json for:
742
+ - Use **Json** for:
1637
743
  - Player or tool settings, human‑readable saves, serverless workflows.
1638
744
  - Interop with tooling, debugging, or versioning in Git.
1639
- - Use Protobuf for:
745
+ - Use **Protobuf** for:
1640
746
  - Network payloads, large save files, bandwidth/storage‑sensitive data.
1641
747
  - Situations where you expect schema evolution across versions.
1642
- - Use SystemBinary only for:
748
+ - Use **SystemBinary** only for:
1643
749
  - Transient caches in trusted environments where data and code version match.
1644
750
  - Never for untrusted data or long‑term persistence.
1645
751
 
1646
- #### JSON Examples (Unity‑aware)
752
+ #### JSON Example
1647
753
 
1648
754
  ```csharp
1649
755
  using System.Collections.Generic;
@@ -1680,7 +786,7 @@ byte[] bytes = Serializer.Serialize(data, SerializationType.Json);
1680
786
  SaveData loaded = Serializer.Deserialize<SaveData>(bytes, SerializationType.Json);
1681
787
  ```
1682
788
 
1683
- #### Protobuf Examples (Compact + Evolvable)
789
+ #### Protobuf Example
1684
790
 
1685
791
  ```csharp
1686
792
  using ProtoBuf; // protobuf-net
@@ -1710,38 +816,11 @@ int len = Serializer.Serialize(message, SerializationType.Protobuf, ref buffer);
1710
816
  NetworkMessage again = Serializer.Deserialize<NetworkMessage>(buffer.AsSpan(0, len).ToArray(), SerializationType.Protobuf);
1711
817
  ```
1712
818
 
1713
- Notes:
819
+ **Notes:**
1714
820
 
1715
821
  - Protobuf‑net requires stable field numbers. Annotate with `[ProtoMember(n)]` and never reuse or renumber.
1716
822
  - Unity types supported via surrogates: Vector2/3, Vector2Int/3Int, Quaternion, Color/Color32, Rect/RectInt, Bounds/BoundsInt, Resolution.
1717
823
 
1718
- #### Protobuf Compatibility Tips
1719
-
1720
- - Add fields with new numbers; old clients ignore unknown fields, new clients default missing fields.
1721
- - Do not change field numbers or `oneof` layout; reserve removed numbers if needed.
1722
- - Avoid switching scalar types (e.g., `int32` → `string`) on the same number.
1723
- - Prefer optional/repeated over required; required breaks backward compatibility.
1724
- - Use sensible defaults to keep payloads minimal.
1725
-
1726
- #### SystemBinary Examples (Legacy/Trusted Only)
1727
-
1728
- ```csharp
1729
- using WallstopStudios.UnityHelpers.Core.Serialization;
1730
-
1731
- var obj = new SomeSerializableType();
1732
- byte[] bin = Serializer.BinarySerialize(obj);
1733
- SomeSerializableType roundtrip = Serializer.BinaryDeserialize<SomeSerializableType>(bin);
1734
-
1735
- // Generic
1736
- byte[] bin2 = Serializer.Serialize(obj, SerializationType.SystemBinary);
1737
- var round2 = Serializer.Deserialize<SomeSerializableType>(bin2, SerializationType.SystemBinary);
1738
- ```
1739
-
1740
- Watch‑outs:
1741
-
1742
- - BinaryFormatter is obsolete in modern .NET and not secure for untrusted input.
1743
- - Version changes often break binary round‑trips; use only for same‑version caches.
1744
-
1745
824
  **Features:**
1746
825
 
1747
826
  - Custom converters for Unity types (Vector2/3/4, Color, GameObject, Matrix4x4, Type)
@@ -1749,6 +828,10 @@ Watch‑outs:
1749
828
  - LZMA compression utilities (see `Runtime/Utils/LZMA.cs`)
1750
829
  - Type‑safe serialization and pooled buffers/writers to reduce GC
1751
830
 
831
+ [Full guide: Serialization](Docs/SERIALIZATION.md)
832
+
833
+ ---
834
+
1752
835
  ### Data Structures
1753
836
 
1754
837
  Additional high-performance data structures:
@@ -1788,1408 +871,320 @@ List<string> matches = commandTrie.GetWordsWithPrefix("tel");
1788
871
  // Returns: ["teleport", "tell"]
1789
872
  ```
1790
873
 
1791
- ### Helpers & Extensions
874
+ [Full guide: Data Structures](Docs/DATA_STRUCTURES.md)
1792
875
 
1793
- See also: [Buffering Pattern](#buffering-pattern)
876
+ ---
1794
877
 
1795
- High-level helpers and extension methods that streamline day-to-day Unity work.
878
+ ### Core Math & Extensions
1796
879
 
1797
- #### Context-Aware Logging with Custom Formatters
880
+ Numeric helpers, geometry primitives, Unity extensions, colors, collections, strings, directions.
1798
881
 
1799
- Unity Helpers includes a powerful logging system with automatic context injection, custom formatters, and build-time stripping:
882
+ See the guide: [Core Math & Extensions](Docs/MATH_AND_EXTENSIONS.md).
1800
883
 
1801
- **Key Features:**
884
+ #### At a Glance
1802
885
 
1803
- - **Automatic Context & Timing** Every log includes GameObject name, component type, and timestamp
1804
- - **Exception Overloads** Pass exceptions directly: `this.LogError($"Failed to load {asset}", exception)`
1805
- - **Custom Formatters** Built-in support for colors, bold, italic, JSON, sizing, and extensible with your own
1806
- - **Thread-Safe**Automatically routes logs to Unity main thread when needed
1807
- - **Build Stripping** Controlled via scripting defines (`ENABLE_UBERLOGGING`, `DEBUG_LOGGING`, etc.)
1808
- - **Per-Object Control** Enable/disable logging globally or per-component
886
+ - `PositiveMod`, `WrappedAdd`Safe cyclic arithmetic for indices/angles.
887
+ - `LineHelper.Simplify`Reduce polyline vertices with Douglas–Peucker.
888
+ - `Line2D.Intersects`Robust 2D segment intersection and closest-point helpers.
889
+ - `RectTransform.GetWorldRect`Axis-aligned world bounds for rotated UI.
890
+ - `Camera.OrthographicBounds`Compute visible world bounds for ortho cameras.
891
+ - `Color.GetAverageColor`LAB/HSV/Weighted/Dominant color averaging.
892
+ - `IEnumerable.Infinite` — Cycle sequences without extra allocations.
893
+ - `StringExtensions.LevenshteinDistance` — Edit distance for fuzzy matching.
1809
894
 
1810
- **Quick Example:**
895
+ ---
1811
896
 
1812
- ```csharp
1813
- using WallstopStudios.UnityHelpers.Core.Extension;
897
+ <a id="singleton-utilities-odin-compatible"></a>
1814
898
 
1815
- public class PlayerController : MonoBehaviour
1816
- {
1817
- void Start()
1818
- {
1819
- // Simple logging with automatic context
1820
- this.Log($"Player initialized");
1821
- // Output: 12.34|PlayerController[PlayerController]|Player initialized
1822
-
1823
- // Rich formatting with colors and bold
1824
- int health = 100;
1825
- this.Log($"Health: {"100":b,#green}");
1826
- // Output (in Unity): 12.34|PlayerController[PlayerController]|Health: <b><color=#00FF00FF>100</color></b>
1827
-
1828
- // JSON serialization
1829
- var data = new { x = 10, y = 20 };
1830
- this.LogWarn($"Position data: {data:json}");
1831
- // Output: 12.34|PlayerController[PlayerController]|Position data: {"x":10,"y":20}
1832
-
1833
- // Exception logging
1834
- try
1835
- {
1836
- // ... risky operation
1837
- }
1838
- catch (Exception e)
1839
- {
1840
- this.LogError($"Failed to save game", e);
1841
- // Includes full exception with stack trace
1842
- }
1843
- }
1844
- }
1845
- ```
899
+ ### Singleton Utilities (ODIN‑compatible)
1846
900
 
1847
- **Supported Format Tags:**
901
+ - `RuntimeSingleton<T>` — Global component singleton with optional cross‑scene persistence.
902
+ - `ScriptableObjectSingleton<T>` — Global settings/data singleton loaded from `Resources/`, auto‑created by the editor tool.
1848
903
 
1849
- - `b`, `bold`, `!` **Bold text** (editor only)
1850
- - `i`, `italic`, `_` — _Italic text_ (editor only)
1851
- - `#colorName` or `#RRGGBB` — <span style="color:red">Colored text</span> (editor only)
1852
- - `json` — JSON serialization (works everywhere)
1853
- - `12` or `size=12` — Sized text 1-100 (editor only)
1854
- - Stack multiple: `{value:b,#red,20}` → large, bold, red text
904
+ See the guide: [Singleton Utilities](Docs/SINGLETONS.md) and the tool: [ScriptableObject Singleton Creator](Docs/EDITOR_TOOLS_GUIDE.md#scriptableobject-singleton-creator).
1855
905
 
1856
- **Custom Formatters:**
906
+ ---
1857
907
 
1858
- ```csharp
1859
- using WallstopStudios.UnityHelpers.Core.Helper.Logging;
1860
-
1861
- // Add your own formatter
1862
- UnityLogTagFormatter formatter = WallstopStudiosLogger.LogInstance;
1863
- formatter.AddDecoration(
1864
- match: "upper",
1865
- format: value => value.ToString().ToUpper(),
1866
- tag: "Uppercase"
1867
- );
1868
-
1869
- // Use it
1870
- this.Log($"{"hello":upper}"); // Output: HELLO
1871
- ```
908
+ ### Editor Tools
1872
909
 
1873
- **Build-Time Control:**
910
+ Unity Helpers includes 20+ editor tools to streamline your workflow:
1874
911
 
1875
- ```csharp
1876
- // Logs are enabled by default in:
1877
- // - Development builds (DEVELOPMENT_BUILD)
1878
- // - Debug builds (DEBUG)
1879
- // - Unity Editor (UNITY_EDITOR)
1880
-
1881
- // Override with scripting defines:
1882
- // ENABLE_UBERLOGGING - Enable all logs
1883
- // DEBUG_LOGGING - Enable Log() calls only
1884
- // WARN_LOGGING - Enable LogWarn() calls only
1885
- // ERROR_LOGGING - Enable LogError() calls only
1886
-
1887
- // Runtime control
1888
- this.DisableLogging(); // Disable logs for this component
1889
- this.EnableLogging(); // Re-enable logs for this component
1890
- this.GlobalDisableLogging(); // Disable all logs
1891
- this.GlobalEnableLogging(); // Re-enable all logs
1892
- ```
912
+ - **Sprite Tools**: Cropper, Atlas Generator, Animation Editor, Pivot Adjuster
913
+ - **Texture Tools**: Blur, Resize, Settings Applier, Fit Texture Size
914
+ - **Animation Tools**: Event Editor, Creator, Copier, Sheet Animation Creator
915
+ - **Validation**: Prefab Checker with comprehensive validation rules
916
+ - **Automation**: ScriptableObject Singleton Creator, Attribute Cache Generator
1893
917
 
1894
- **Other Key Helpers:**
918
+ [📖 Complete Editor Tools Documentation](Docs/EDITOR_TOOLS_GUIDE.md)
1895
919
 
1896
- - `Helpers.Find<T>(tag)` and `HasComponent<T>()` — Fewer `GetComponent` calls, cached lookups by tag.
1897
- - `GetOrAddComponent<T>()` — Idempotent component setup in initialization code.
1898
- - `DestroyAllChildren*` and `SmartDestroy()` — Safe destroy patterns across editor/runtime.
1899
- - `StartFunctionAsCoroutine(action, rate, useJitter)` — Simple polling/ticking utilities.
1900
- - `UpdateShapeToSprite()` — Sync `PolygonCollider2D` to `SpriteRenderer` at runtime.
1901
- - `GetAllLayerNames()` and `GetAllSpriteLabelNames()` — Editor integrations and tooling.
1902
- - `GetRandomPointInCircle/Sphere()` — Uniform random positions for spawn/FX.
1903
- - Unity Extensions: conversions (`Rect`⇄`Bounds`), camera `OrthographicBounds()`, physics `Rigidbody2D.Stop()`, input filtering `Vector2.IsNoise()`.
920
+ **Quick Access:**
1904
921
 
1905
- Examples:
1906
-
1907
- ```csharp
1908
- using WallstopStudios.UnityHelpers.Core.Helper;
1909
- using WallstopStudios.UnityHelpers.Core.Extension;
1910
- using UnityEngine;
1911
-
1912
- public class Setup : MonoBehaviour
1913
- {
1914
- void Awake()
1915
- {
1916
- // Component orchestration
1917
- var rb = gameObject.GetOrAddComponent<Rigidbody2D>();
1918
- if (gameObject.HasComponent<SpriteRenderer>())
1919
- {
1920
- // Match collider to current sprite at runtime
1921
- gameObject.UpdateShapeToSprite();
1922
- }
1923
-
1924
- // Destroy patterns
1925
- transform.parent.gameObject.DestroyAllChildrenGameObjects(); // runtime-safe
1926
-
1927
- // Lightweight polling
1928
- this.StartFunctionAsCoroutine(() => Debug.Log("Tick"), 0.5f, useJitter: true);
1929
- }
1930
- }
1931
-
1932
- public class CameraUtils : MonoBehaviour
1933
- {
1934
- void OnDrawGizmosSelected()
1935
- {
1936
- if (Camera.main)
1937
- {
1938
- // Compute world-space orthographic bounds for culling or UI logic
1939
- Bounds view = Camera.main.OrthographicBounds();
1940
- Gizmos.DrawWireCube(view.center, view.size);
1941
- }
1942
- }
1943
- }
1944
- ```
1945
-
1946
- When to use what:
1947
-
1948
- - Prefer `SpatialHash2D` for many moving objects uniformly spread; prefer `QuadTree2D` for static or semi-static content with clustered queries.
1949
- - Use `Helpers.StartFunctionAsCoroutine` for simple, frame-safe polling; prefer `InvokeRepeating` or custom `Update` loops when you need fine-grained frame ordering.
1950
- - Use `SmartDestroy` when writing code that runs in both edit mode and play mode to avoid editor/runtime differences.
1951
-
1952
- ### Choosing Helpers
1953
-
1954
- - Destroy patterns: Use `SmartDestroy` for editor/play safe teardown; `DestroyAllChildren*` to clear hierarchies quickly.
1955
- - Component wiring: Prefer relational attributes (`[SiblingComponent]`, etc.) + `AssignRelationalComponents()` over manual `GetComponent` calls.
1956
- - To avoid first-use reflection overhead, prewarm caches at startup with `RelationalComponentInitializer.Initialize()` or enable “Prewarm Relational On Load” on the AttributeMetadataCache asset.
1957
- - Random placement: Use `Helpers.GetRandomPointInCircle/Sphere` or `RandomExtensions.NextVector2/3(InRange)` for uniform distributions.
1958
- - Asset/tooling: `GetAllLayerNames` and `GetAllSpriteLabelNames` power menu tooling and editor workflows.
1959
- - Math/geometry: `WallMath.PositiveMod/Wrapped*` for robust wrap-around; `LineHelper.Simplify*` to reduce polyline complexity; `Geometry.IsAPointLeftOfVectorOrOnTheLine` for sidedness tests.
1960
-
1961
- ### Editor Tools
1962
-
1963
- Unity Helpers includes 20+ editor tools to streamline your workflow:
1964
-
1965
- - **Sprite Tools**: Cropper, Atlas Generator, Animation Editor, Pivot Adjuster
1966
- - **Texture Tools**: Blur, Resize, Settings Applier, Fit Texture Size
1967
- - **Animation Tools**: Event Editor, Creator, Copier, Sheet Animation Creator
1968
- - **Validation**: Prefab Checker with comprehensive validation rules
1969
- - **Automation**: ScriptableObject Singleton Creator, Attribute Cache Generator
1970
-
1971
- [📖 Complete Editor Tools Documentation](EDITOR_TOOLS_GUIDE.md)
1972
-
1973
- **Quick Access:**
1974
-
1975
- - Menu: `Tools > Wallstop Studios > Unity Helpers`
1976
- - Create Assets: `Assets > Create > Wallstop Studios > Unity Helpers`
1977
-
1978
- <a id="use-cases--examples"></a>
1979
- <a id="use-cases-examples"></a>
1980
-
1981
- ## Use Cases & Examples
1982
-
1983
- ### Case Study: Player Controller with Auto-Wiring
1984
-
1985
- Clean, maintainable character controllers using relational components to eliminate GetComponent boilerplate.
1986
-
1987
- ```csharp
1988
- using UnityEngine;
1989
- using WallstopStudios.UnityHelpers.Core.Attributes;
1990
-
1991
- public class PlayerController : MonoBehaviour
1992
- {
1993
- // Auto-wire components - no GetComponent needed
1994
- [SiblingComponent] private Rigidbody2D rb;
1995
- [SiblingComponent] private SpriteRenderer spriteRenderer;
1996
- [SiblingComponent] private Animator animator;
1997
- [ChildComponent(OnlyDescendants = true)] private Collider2D[] hitboxes;
1998
-
1999
- [Header("Stats")]
2000
- public float moveSpeed = 5f;
2001
- public float jumpForce = 10f;
2002
-
2003
- void Awake()
2004
- {
2005
- // One call wires everything
2006
- this.AssignRelationalComponents();
2007
- }
2008
-
2009
- void Update()
2010
- {
2011
- // Movement
2012
- float horizontal = Input.GetAxis("Horizontal");
2013
- rb.velocity = new Vector2(horizontal * moveSpeed, rb.velocity.y);
2014
-
2015
- // Flip sprite
2016
- if (horizontal != 0)
2017
- spriteRenderer.flipX = horizontal < 0;
2018
-
2019
- // Animate
2020
- animator.SetFloat("Speed", Mathf.Abs(horizontal));
2021
-
2022
- // Jump
2023
- if (Input.GetButtonDown("Jump") && IsGrounded())
2024
- rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
2025
- }
2026
-
2027
- bool IsGrounded()
2028
- {
2029
- // Efficiently check all child colliders
2030
- foreach (var hitbox in hitboxes)
2031
- {
2032
- if (hitbox.IsTouchingLayers(LayerMask.GetMask("Ground")))
2033
- return true;
2034
- }
2035
- return false;
2036
- }
2037
- }
2038
- ```
2039
-
2040
- **Key benefits:**
2041
-
2042
- - **Zero boilerplate:** No GetComponent calls, null checks, or error handling
2043
- - **Self-documenting:** Clear intent with attributes (`[ChildComponent]`)
2044
- - **Compile-time safety:** Typos caught immediately
2045
- - **Scales beautifully:** Works for simple and complex hierarchies
2046
-
2047
- ---
2048
-
2049
- ### Case Study: Buff/Debuff System with Effects
2050
-
2051
- Complete status effect system with zero code - everything configured in the editor.
2052
-
2053
- ```csharp
2054
- using UnityEngine;
2055
- using WallstopStudios.UnityHelpers.Tags;
2056
-
2057
- // 1. Define stats that effects can modify
2058
- public class CharacterStats : AttributesComponent
2059
- {
2060
- public Attribute Speed = 5f;
2061
- public Attribute Damage = 10f;
2062
- public Attribute Defense = 5f;
2063
- public Attribute Health = 100f;
2064
- }
2065
-
2066
- // 2. Use in gameplay
2067
- public class Character : MonoBehaviour
2068
- {
2069
- [SiblingComponent] private CharacterStats stats;
2070
-
2071
- [Header("Effect Prefabs")]
2072
- [SerializeField] private AttributeEffect hasteEffect; // Created in editor
2073
- [SerializeField] private AttributeEffect shieldEffect; // Created in editor
2074
- [SerializeField] private AttributeEffect stunEffect; // Created in editor
2075
-
2076
- void Awake()
2077
- {
2078
- this.AssignRelationalComponents();
2079
- }
2080
-
2081
- void Update()
2082
- {
2083
- // Check status via tags
2084
- if (this.HasTag("Stunned"))
2085
- {
2086
- Debug.Log("Can't act while stunned!");
2087
- return;
2088
- }
2089
-
2090
- // Normal gameplay using dynamic stats
2091
- float currentSpeed = stats.Speed.Value; // Respects all active buffs/debuffs
2092
- transform.position += Vector3.right * currentSpeed * Time.deltaTime;
2093
-
2094
- // Combat
2095
- if (this.HasTag("Invulnerable"))
2096
- return; // Immune to damage
2097
-
2098
- // Apply damage with defense calculation
2099
- float incomingDamage = 20f;
2100
- float actualDamage = Mathf.Max(0, incomingDamage - stats.Defense.Value);
2101
- stats.Health.Value -= actualDamage;
2102
- }
2103
-
2104
- // Apply effects from other systems (items, abilities, enemies)
2105
- public void ApplyBuff(AttributeEffect effect)
2106
- {
2107
- this.ApplyEffect(effect);
2108
- }
2109
-
2110
- public void Cleanse()
2111
- {
2112
- // Remove all debuffs at once
2113
- this.RemoveAllEffectsWithTag("Debuff");
2114
- }
2115
- }
2116
- ```
2117
-
2118
- **In the Unity Editor, create AttributeEffect ScriptableObjects:**
2119
-
2120
- **HasteEffect.asset:**
2121
-
2122
- - Modifications: Speed × 1.5
2123
- - Duration: 5 seconds
2124
- - Tags: "Haste", "Buff"
2125
- - Visual: Speed lines particle effect
2126
-
2127
- **ShieldEffect.asset:**
2128
-
2129
- - Modifications: Defense + 10
2130
- - Duration: 10 seconds
2131
- - Tags: "Shield", "Buff"
2132
- - Grant Tags: "Invulnerable"
2133
- - Visual: Blue shield glow
2134
-
2135
- **StunEffect.asset:**
2136
-
2137
- - Modifications: Speed = 0 (Override)
2138
- - Duration: 3 seconds
2139
- - Tags: "Stun", "Debuff", "CC"
2140
- - Grant Tags: "Stunned"
2141
- - Visual: Stars circling head
2142
-
2143
- **Why this is game-changing:**
2144
-
2145
- - **Zero effect code:** Designers create hundreds of effects without programmer involvement
2146
- - **Instant prototyping:** New buff in 30 seconds (create ScriptableObject, set values)
2147
- - **Perfect stacking:** Multiple effects work together automatically
2148
- - **Visual polish:** Particles spawn/despawn with effects
2149
- - **Gameplay queries:** Check tags for immunity, crowd control, etc.
2150
-
2151
- **Tutorial:** See [Effects System Tutorial](EFFECTS_SYSTEM_TUTORIAL.md) for step-by-step guide.
922
+ - Menu: `Tools > Wallstop Studios > Unity Helpers`
923
+ - Create Assets: `Assets > Create > Wallstop Studios > Unity Helpers`
2152
924
 
2153
925
  ---
2154
926
 
2155
- ### Case Study: Loot System with Weighted Random
2156
-
2157
- Robust loot drops using Unity Helpers' extensive random API - no manual weight calculations.
2158
-
2159
- ```csharp
2160
- using UnityEngine;
2161
- using WallstopStudios.UnityHelpers.Core.Random;
2162
- using WallstopStudios.UnityHelpers.Core.Extension;
2163
-
2164
- public class LootTable : MonoBehaviour
2165
- {
2166
- private IRandom random = PRNG.Instance;
2167
-
2168
- [System.Serializable]
2169
- public class LootEntry
2170
- {
2171
- public GameObject itemPrefab;
2172
- public float dropChance; // 0.0 to 1.0
2173
- public int minCount;
2174
- public int maxCount;
2175
- }
2176
-
2177
- public List<LootEntry> lootEntries;
2178
-
2179
- public List<GameObject> RollLoot()
2180
- {
2181
- List<GameObject> rewards = new();
2182
-
2183
- // Simple weighted selection
2184
- float[] weights = lootEntries.Select(e => e.dropChance).ToArray();
2185
- int rolledIndex = random.NextWeightedIndex(weights);
2186
-
2187
- LootEntry winner = lootEntries[rolledIndex];
2188
- int count = random.Next(winner.minCount, winner.maxCount + 1);
2189
-
2190
- for (int i = 0; i < count; i++)
2191
- rewards.Add(winner.itemPrefab);
2192
-
2193
- return rewards;
2194
- }
2195
-
2196
- public GameObject RollRareItem()
2197
- {
2198
- // Weighted bool for rare drops (20% chance)
2199
- if (random.NextBool(probability: 0.2f))
2200
- {
2201
- // Select from rare items only
2202
- var rareItems = lootEntries.Where(e => e.dropChance < 0.1f).ToArray();
2203
- return random.NextOf(rareItems).itemPrefab;
2204
- }
2205
-
2206
- return null;
2207
- }
2208
-
2209
- public List<GameObject> RollMultipleItems(int rollCount)
2210
- {
2211
- List<GameObject> rewards = new();
2212
-
2213
- // Each entry can drop independently
2214
- foreach (var entry in lootEntries)
2215
- {
2216
- if (random.NextFloat() < entry.dropChance)
2217
- {
2218
- int count = random.Next(entry.minCount, entry.maxCount + 1);
2219
- for (int i = 0; i < count; i++)
2220
- rewards.Add(entry.itemPrefab);
2221
- }
2222
- }
2223
-
2224
- // Shuffle for variety
2225
- return random.Shuffle(rewards).ToList();
2226
- }
2227
- }
2228
- ```
2229
-
2230
- **Why Unity Helpers' random API shines here:**
2231
-
2232
- - **NextWeightedIndex():** Handles normalization automatically
2233
- - **NextBool(probability):** Cleaner than `NextFloat() < 0.2f`
2234
- - **NextOf(array):** Direct selection without manual indexing
2235
- - **Shuffle():** Built-in for random order
2236
- - **10-15x faster** than UnityEngine.Random
2237
-
2238
- ---
2239
-
2240
- ### Case Study: Procedural Level Generation
2241
-
2242
- Deterministic terrain using Perlin noise, Gaussian distributions, and seeded random.
2243
-
2244
- ```csharp
2245
- using UnityEngine;
2246
- using WallstopStudios.UnityHelpers.Core.Random;
2247
- using WallstopStudios.UnityHelpers.Core.Extension;
2248
-
2249
- public class LevelGenerator : MonoBehaviour
2250
- {
2251
- private IRandom random;
2252
-
2253
- public void GenerateLevel(int seed)
2254
- {
2255
- random = new PcgRandom(seed); // Deterministic - same seed = same level
2256
-
2257
- // Generate noise map for terrain height
2258
- float[,] heightMap = random.NextNoiseMap(
2259
- width: 256,
2260
- height: 256,
2261
- octaves: 4,
2262
- persistence: 0.5f,
2263
- lacunarity: 2f
2264
- );
2265
-
2266
- // Place terrain features based on height
2267
- for (int x = 0; x < 256; x++)
2268
- {
2269
- for (int y = 0; y < 256; y++)
2270
- {
2271
- float height = heightMap[x, y];
2272
-
2273
- if (height > 0.7f) PlaceMountain(x, y);
2274
- else if (height > 0.4f) PlaceTree(x, y);
2275
- else if (height < 0.3f) PlaceWater(x, y);
2276
- }
2277
- }
2278
-
2279
- // Spawn enemy clusters using Gaussian distribution
2280
- int clusterCount = random.Next(5, 10);
2281
- for (int i = 0; i < clusterCount; i++)
2282
- {
2283
- Vector2 clusterCenter = random.NextVector2() * 256f;
2284
- int enemiesInCluster = random.Next(3, 8);
2285
-
2286
- for (int j = 0; j < enemiesInCluster; j++)
2287
- {
2288
- // Cluster tightly around center
2289
- Vector2 offset = new Vector2(
2290
- random.NextGaussian(mean: 0f, stdDev: 10f),
2291
- random.NextGaussian(mean: 0f, stdDev: 10f)
2292
- );
2293
-
2294
- SpawnEnemy(clusterCenter + offset);
2295
- }
2296
- }
2297
-
2298
- // Place collectibles with distance requirements
2299
- List<Vector2> itemPositions = new();
2300
- int itemCount = random.Next(20, 30);
2301
-
2302
- for (int i = 0; i < itemCount; i++)
2303
- {
2304
- Vector2 pos;
2305
- int attempts = 0;
2306
-
2307
- // Ensure minimum spacing between items
2308
- do
2309
- {
2310
- pos = random.NextVector2() * 256f;
2311
- attempts++;
2312
- }
2313
- while (attempts < 10 && itemPositions.Any(p => Vector2.Distance(p, pos) < 15f));
2314
-
2315
- itemPositions.Add(pos);
2316
- PlaceCollectible(pos);
2317
- }
2318
- }
2319
- }
2320
- ```
2321
-
2322
- **Advanced features showcased:**
2323
-
2324
- - **NextNoiseMap():** Complete Perlin noise implementation in one call
2325
- - **NextGaussian():** Natural clustering (bell curve distribution)
2326
- - **NextVector2():** Cleaner than `new Vector2(random.NextFloat(), random.NextFloat())`
2327
- - **Seedable:** Perfect for networked games or replay systems
2328
-
2329
- ---
2330
-
2331
- ### Case Study: AI Behavior with Spatial Queries
2332
-
2333
- Efficient enemy AI that scales to hundreds of units using spatial trees.
2334
-
2335
- ```csharp
2336
- using UnityEngine;
2337
- using WallstopStudios.UnityHelpers.Core.DataStructure;
2338
- using WallstopStudios.UnityHelpers.Core.Attributes;
2339
- using WallstopStudios.UnityHelpers.Core.Random;
2340
-
2341
- public class AIController : MonoBehaviour
2342
- {
2343
- [SiblingComponent] private NavMeshAgent agent;
2344
- [SiblingComponent] private Animator animator;
2345
-
2346
- private IRandom random;
2347
- private QuadTree2D<Enemy> enemyTree;
2348
- private List<Enemy> nearbyBuffer = new(32); // Reusable buffer
2349
-
2350
- void Start()
2351
- {
2352
- this.AssignRelationalComponents();
2353
-
2354
- // Deterministic AI with seeded random
2355
- random = new PcgRandom(seed: GetInstanceID());
2356
-
2357
- // Build spatial tree for O(log n) queries
2358
- enemyTree = new QuadTree2D<Enemy>(
2359
- FindObjectsOfType<Enemy>(),
2360
- e => e.transform.position
2361
- );
2362
- }
2363
-
2364
- void Update()
2365
- {
2366
- nearbyBuffer.Clear();
2367
-
2368
- // Fast O(log n) query instead of O(n) distance checks
2369
- enemyTree.GetElementsInRange(transform.position, 20f, nearbyBuffer);
2370
-
2371
- if (nearbyBuffer.Count > 0)
2372
- {
2373
- // Weighted selection - prefer closer targets
2374
- float[] weights = nearbyBuffer.Select(e =>
2375
- 1f / Vector2.Distance(transform.position, e.transform.position)
2376
- ).ToArray();
2377
-
2378
- int targetIndex = random.NextWeightedIndex(weights);
2379
- Enemy target = nearbyBuffer[targetIndex];
2380
-
2381
- agent.SetDestination(target.transform.position);
2382
- animator.SetBool("IsChasing", true);
2383
- }
2384
- else
2385
- {
2386
- animator.SetBool("IsChasing", false);
2387
- }
2388
- }
2389
- }
2390
- ```
2391
-
2392
- **Performance wins:**
2393
-
2394
- - **O(log n) queries:** Find nearby enemies without checking every object
2395
- - **Buffering pattern:** Reuse `nearbyBuffer` to avoid GC
2396
- - **Scales to 1000+ units:** QuadTree keeps queries fast even with many objects
2397
-
2398
- **When to use spatial trees:**
2399
-
2400
- - Many moving objects (enemies, bullets, particles)
2401
- - Frequent proximity checks (AI awareness, collision)
2402
- - Large open worlds (visibility culling)
2403
-
2404
- **Guides:** [2D Spatial Trees](SPATIAL_TREES_2D_GUIDE.md) | [Performance Benchmarks](SPATIAL_TREE_2D_PERFORMANCE.md)
2405
-
2406
- ## Hidden Gems: Underrated Killer Features
2407
-
2408
- These powerful utilities solve common game development problems but might not be immediately obvious from the feature list.
2409
-
2410
- ### Predictive Targeting: Hit Moving Targets
2411
-
2412
- Perfect ballistics in one line. Calculates the intercept point for hitting a moving target with a projectile of known speed.
2413
-
2414
- ```csharp
2415
- using WallstopStudios.UnityHelpers.Core.Helper;
2416
-
2417
- public class Turret : MonoBehaviour
2418
- {
2419
- public float projectileSpeed = 25f;
2420
-
2421
- void Update()
2422
- {
2423
- GameObject target = FindTarget();
2424
- if (target == null) return;
2425
-
2426
- // Estimate target velocity (or track it)
2427
- Vector2 targetVelocity = EstimateVelocity(target);
2428
-
2429
- // Calculate perfect aim point accounting for projectile travel time
2430
- Vector2 aimPoint = target.PredictCurrentTarget(
2431
- launchLocation: transform.position,
2432
- projectileSpeed: projectileSpeed,
2433
- predictiveFiring: true,
2434
- targetVelocity: targetVelocity
2435
- );
2436
-
2437
- // Aim and fire
2438
- transform.up = (aimPoint - (Vector2)transform.position).normalized;
2439
- Fire();
2440
- }
2441
- }
2442
- ```
2443
-
2444
- **Why this is a game-changer:**
2445
-
2446
- - Solves quadratic intercept equation with robust fallbacks for edge cases
2447
- - Handles fast/slow projectiles, moving/stationary targets automatically
2448
- - Perfect for: turrets, homing missiles, AI prediction, physics-based games
2449
- - Eliminates need for iterative aiming or complex prediction logic
2450
-
2451
- **Real-world use case:** Tower defense games where enemies move along paths - turrets lead the target perfectly without any tuning.
2452
-
2453
- ---
2454
-
2455
- ### ReflectionHelpers: Blazing-Fast Reflection
2456
-
2457
- High-performance reflection using IL emission and expression compilation. 10-100x faster than System.Reflection for hot paths.
2458
-
2459
- ```csharp
2460
- using WallstopStudios.UnityHelpers.Core.Helper;
2461
- using System.Reflection;
2462
-
2463
- // ONE-TIME: Create cached delegates (do this at initialization)
2464
- FieldInfo healthField = typeof(Enemy).GetField("_health", BindingFlags.NonPublic | BindingFlags.Instance);
2465
- var getHealth = ReflectionHelpers.GetFieldGetter(healthField); // Cached
2466
- var setHealth = ReflectionHelpers.GetFieldSetter(healthField); // Cached
2467
-
2468
- // HOT PATH: Use the delegates (10-100x faster than reflection)
2469
- void ProcessEnemies(List<object> enemies)
2470
- {
2471
- foreach (object enemy in enemies)
2472
- {
2473
- float health = (float)getHealth(enemy);
2474
- setHealth(enemy, health - 10f);
2475
- }
2476
- }
2477
- ```
2478
-
2479
- **Advanced: Typed accessors for zero boxing**
2480
-
2481
- ```csharp
2482
- // For structs or when you need maximum performance
2483
- FieldInfo scoreField = typeof(Player).GetField("Score");
2484
- var getScore = ReflectionHelpers.GetFieldGetter<Player, int>(scoreField);
2485
- var setScore = ReflectionHelpers.GetFieldSetter<Player, int>(scoreField);
2486
-
2487
- Player player = new Player();
2488
- setScore(ref player, 100); // No boxing, direct struct mutation
2489
- int score = getScore(ref player);
2490
- ```
2491
-
2492
- **Why this is essential:**
2493
-
2494
- - **Serialization systems**: Deserialize thousands of objects per frame
2495
- - **Data binding**: UI systems that update from model properties
2496
- - **Modding APIs**: Safe access to private fields without making everything public
2497
- - **ECS-style systems**: Generic component access without inheritance
2498
- - **IL2CPP safe**: Works with Unity's aggressive compilation
2499
-
2500
- **Performance numbers:** GetField: ~2ns vs ~200ns (100x), SetField: ~3ns vs ~150ns (50x)
2501
-
2502
- ---
927
+ ## Buffering Pattern
2503
928
 
2504
929
  ### Professional-Grade Object Pooling
2505
930
 
2506
- Thread-safe pooling for Lists, Arrays, Dictionaries, and custom types with automatic cleanup via IDisposable pattern.
931
+ Zero-allocation queries with automatic cleanup and thread-safe pooling.
2507
932
 
2508
933
  ```csharp
2509
934
  using WallstopStudios.UnityHelpers.Utils;
2510
935
  using WallstopStudios.UnityHelpers.Core.DataStructure;
2511
936
 
2512
- public class ParticleSystem : MonoBehaviour
937
+ // Example: Use pooled buffer for spatial query
938
+ void FindNearbyEnemies(QuadTree2D<Enemy> tree, Vector2 position)
2513
939
  {
2514
- void Update()
2515
- {
2516
- // Get pooled list - automatically returned on scope exit
2517
- using var lease = Buffers<Vector3>.List.Get(out List<Vector3> positions);
2518
-
2519
- // Use it freely
2520
- CalculateParticlePositions(positions);
2521
-
2522
- // Do spatial query with pooled buffer
2523
- using var enemiesLease = Buffers<Enemy>.List.Get(out List<Enemy> enemies);
2524
- enemyTree.GetElementsInRange(transform.position, 10f, enemies);
940
+ // Get pooled list - automatically returned when scope exits
941
+ using var lease = Buffers<Enemy>.List.Get(out List<Enemy> buffer);
2525
942
 
2526
- foreach (Enemy enemy in enemies)
2527
- {
2528
- enemy.TakeDamage(1f);
2529
- }
943
+ // Use it with spatial query - combines zero-alloc query + pooled buffer!
944
+ tree.GetElementsInRange(position, 10f, buffer);
2530
945
 
2531
- // Both lists automatically returned to pool here - zero cleanup code
946
+ foreach (Enemy enemy in buffer)
947
+ {
948
+ enemy.TakeDamage(5f);
2532
949
  }
950
+ // buffer automatically returned to pool here
2533
951
  }
2534
- ```
2535
-
2536
- **Advanced: Pooled arrays for high-frequency operations**
2537
952
 
2538
- ```csharp
2539
- void ProcessFrame(int vertexCount)
953
+ // Array pooling example
954
+ void ProcessLargeDataset(int size)
2540
955
  {
2541
- // Rent array from pool
2542
- using var lease = WallstopArrayPool<Vector3>.Get(vertexCount, out Vector3[] vertices);
2543
-
2544
- // Use it for processing
2545
- mesh.GetVertices(vertices);
2546
- TransformVertices(vertices);
2547
- mesh.SetVertices(vertices);
2548
-
2549
- // Array automatically returned and cleared
2550
- }
2551
- ```
2552
-
2553
- **Why this matters:**
2554
-
2555
- - **Zero GC spikes**: Reuse allocations instead of creating garbage
2556
- - **Automatic cleanup**: IDisposable pattern ensures returns even on exceptions
2557
- - **Thread-safe**: ConcurrentStack backing for multi-threaded scenarios
2558
- - **Type-safe**: Generic pooling with full type safety
2559
- - **Customizable**: Create pools for your own types with custom lifecycle callbacks
2560
-
2561
- **Perfect for:**
2562
-
2563
- - AI systems querying neighbors every frame
2564
- - Particle systems with thousands of particles
2565
- - Physics raycasts returning hit arrays
2566
- - UI systems updating hundreds of elements
2567
- - Any system doing frequent spatial queries
2568
-
2569
- ---
2570
-
2571
- ### Lifecycle Helpers: No More DestroyImmediate Bugs
2572
-
2573
- Safe object destruction that works correctly in both edit mode and play mode, preventing scene corruption.
2574
-
2575
- ```csharp
2576
- using WallstopStudios.UnityHelpers.Core.Helper;
956
+ using var lease = WallstopArrayPool<float>.Get(size, out float[] buffer);
2577
957
 
2578
- public class DynamicUI : MonoBehaviour
2579
- {
2580
- void RebuildUI()
958
+ // Use buffer for temporary processing
959
+ for (int i = 0; i < size; i++)
2581
960
  {
2582
- // WRONG: Can corrupt scenes in edit mode
2583
- // foreach (Transform child in transform)
2584
- // DestroyImmediate(child.gameObject);
2585
-
2586
- // ✅ RIGHT: Works safely in both modes
2587
- transform.gameObject.DestroyAllChildrenGameObjects();
961
+ buffer[i] = ComputeValue(i);
2588
962
  }
2589
963
 
2590
- void CleanupEffect(GameObject effect)
2591
- {
2592
- // Automatically uses DestroyImmediate in editor, Destroy at runtime
2593
- effect.SmartDestroy();
2594
-
2595
- // Or with delay (runtime only)
2596
- effect.SmartDestroy(afterTime: 2f);
2597
- }
964
+ // buffer automatically returned to pool here
2598
965
  }
2599
966
  ```
2600
967
 
2601
- **GetOrAddComponent: Idempotent Component Setup**
968
+ **Do / Don'ts:**
2602
969
 
2603
- ```csharp
2604
- public class PlayerSetup : MonoBehaviour
2605
- {
2606
- void Initialize()
2607
- {
2608
- // Always safe - adds only if missing
2609
- Rigidbody2D rb = gameObject.GetOrAddComponent<Rigidbody2D>();
2610
- rb.gravityScale = 0;
970
+ - Do reuse buffers per system or component.
971
+ - Do treat buffers as temporary scratch space (APIs clear them first).
972
+ - Don't keep references to pooled lists beyond their lease lifetime.
973
+ - Don't share the same buffer across overlapping async/coroutine work.
2611
974
 
2612
- // Works with non-generic too
2613
- Component collider = gameObject.GetOrAddComponent(typeof(CircleCollider2D));
2614
- }
2615
- }
2616
- ```
975
+ <a id="pooling-utilities"></a>
2617
976
 
2618
- **Why these are essential:**
977
+ **Pooling utilities:**
2619
978
 
2620
- - `SmartDestroy`: Prevents "Destroying assets is not permitted" errors in editor
2621
- - `DestroyAllChildren*`: Cleans hierarchies without index shifting bugs
2622
- - `GetOrAddComponent`: Initialization code that's safe to run multiple times
2623
- - All methods handle null checks and edge cases
2624
- - Works correctly with prefab editing mode
979
+ - `Buffers<T>` pooled collections (List/Stack/Queue/HashSet) with `PooledResource` leases.
980
+ - Lists: `using var lease = Buffers<Foo>.List.Get(out List<Foo> list);`
981
+ - Stacks: `using var lease = Buffers<Foo>.Stack.Get(out Stack<Foo> stack);`
982
+ - HashSets: `using var lease = Buffers<Foo>.HashSet.Get(out HashSet<Foo> set);`
983
+ - Pattern: acquire use Dispose (returns to pool, clears collection).
2625
984
 
2626
- **Common scenarios:**
985
+ - `WallstopArrayPool<T>` — rent arrays by length with automatic return on dispose.
986
+ - Example: `using var lease = WallstopArrayPool<int>.Get(1024, out int[] buffer);`
987
+ - Use for temporary processing buffers, sorting, or interop with APIs that require arrays.
2627
988
 
2628
- - Editor tools that modify hierarchies
2629
- - Runtime UI builders
2630
- - Procedural content generation
2631
- - Testing/setup code that runs multiple times
989
+ - `WallstopFastArrayPool<T>` fast array pool specialized for frequent short‑lived arrays.
990
+ - Example: `using var lease = WallstopFastArrayPool<string>.Get(count, out string[] buffer);`
991
+ - Used throughout Helpers for high‑frequency editor/runtime operations (e.g., asset searches).
2632
992
 
2633
- ---
993
+ **How pooling + buffering help APIs:**
2634
994
 
2635
- ### Convex & Concave Hull Generation
995
+ - Spatial queries: pass a reusable `List<T>` to `GetElementsInRange/GetElementsInBounds` and iterate results without allocations.
996
+ - Component queries: `GetComponents(buffer)` clears and fills your buffer instead of allocating arrays.
997
+ - Editor utilities: temporary arrays/lists from pools keep import/scan tools snappy, especially inside loops.
2636
998
 
2637
- Production-ready implementations of hull generation algorithms for complex shapes from point clouds.
999
+ ---
2638
1000
 
2639
- ```csharp
2640
- using WallstopStudios.UnityHelpers.Core.Extension;
2641
- using System.Collections.Generic;
1001
+ ## Dependency Injection Integrations
2642
1002
 
2643
- public class TerrainOutline : MonoBehaviour
2644
- {
2645
- void GenerateOutline(List<Vector2> terrainPoints)
2646
- {
2647
- // Convex hull (fast, for simple outer bounds)
2648
- List<Vector2> convexHull = terrainPoints.BuildConvexHull(
2649
- algorithm: UnityExtensions.ConvexHullAlgorithm.MonotoneChain
2650
- );
2651
-
2652
- // Concave hull (slower, but follows terrain shape closely)
2653
- List<FastVector3Int> gridPositions = GetTerrainGridPositions();
2654
- var options = new UnityExtensions.ConcaveHullOptions
2655
- {
2656
- Strategy = UnityExtensions.ConcaveHullStrategy.Knn,
2657
- NearestNeighbors = 5 // Lower = tighter fit, higher = smoother
2658
- };
2659
- List<FastVector3Int> concaveHull = gridPositions.BuildConcaveHull(
2660
- grid: GetComponent<Grid>(),
2661
- options: options
2662
- );
2663
-
2664
- // Use for collider generation, fog of war, territory borders, etc.
2665
- }
2666
- }
2667
- ```
1003
+ **Auto-detected packages:**
2668
1004
 
2669
- **Why this is powerful:**
1005
+ - Zenject/Extenject: `com.extenject.zenject`, `com.modesttree.zenject`, `com.svermeulen.extenject`
1006
+ - VContainer: `jp.cysharp.vcontainer`, `jp.hadashikick.vcontainer`
1007
+ - Reflex: `com.gustavopsantos.reflex`
2670
1008
 
2671
- - **Convex hulls**: Perfect for collision bounds, fog of war outer limits, vision cones
2672
- - **Concave hulls**: Detailed territory borders, minimap fog, destructible terrain
2673
- - Multiple algorithms: MonotoneChain (fast), Jarvis (simple), Knn/EdgeSplit (concave)
2674
- - Grid-aware: Works with Unity Tilemap/Grid systems out of the box
1009
+ **Manual or source imports (no UPM):**
2675
1010
 
2676
- **Real-world uses:**
1011
+ - Add scripting defines in `Project Settings > Player > Other Settings > Scripting Define Symbols`:
1012
+ - `ZENJECT_PRESENT` when Zenject/Extenject is present
1013
+ - `VCONTAINER_PRESENT` when VContainer is present
1014
+ - `REFLEX_PRESENT` when Reflex is present
1015
+ - Add the define per target platform (e.g., Standalone, Android, iOS).
2677
1016
 
2678
- - RTS territory visualization
2679
- - Fog of war boundaries
2680
- - Destructible terrain collision
2681
- - Minimap zone outlines
2682
- - Vision cone generation
1017
+ **Notes:**
2683
1018
 
2684
- ---
1019
+ - When the define is present, optional assemblies under `Runtime/Integrations/*` compile automatically and expose helpers like `RelationalComponentsInstaller` (Zenject/Reflex) and `RegisterRelationalComponents()` (VContainer).
1020
+ - If you use UPM, no manual defines are required — the package IDs above trigger symbols via `versionDefines` in the asmdefs.
1021
+ - For test scenarios without LifetimeScope (VContainer) or SceneContext (Zenject), see [DI Integrations: Testing and Edge Cases](Docs/RELATIONAL_COMPONENTS.md#di-integrations-testing-and-edge-cases) for step‑by‑step patterns.
2685
1022
 
2686
- ### Coroutine Timing with Jitter
1023
+ **Quick start:**
2687
1024
 
2688
- Production-ready timing utilities with staggered starts to prevent frame spikes.
1025
+ - **VContainer**: in your `LifetimeScope.Configure`, call `builder.RegisterRelationalComponents()`.
1026
+ - **Zenject/Extenject**: add `RelationalComponentsInstaller` to your `SceneContext` (toggle scene scan if desired).
1027
+ - **Reflex**: place `RelationalComponentsInstaller` on the same GameObject as your `SceneScope` to bind the assigner, run the scene scan, and (optionally) listen for additive scenes. Use `container.InjectWithRelations(...)` / `InstantiateGameObjectWithRelations(...)` for DI-friendly hydration.
2689
1028
 
2690
1029
  ```csharp
2691
- using WallstopStudios.UnityHelpers.Core.Helper;
1030
+ // VContainer — LifetimeScope
1031
+ using VContainer;
1032
+ using VContainer.Unity;
1033
+ using WallstopStudios.UnityHelpers.Integrations.VContainer;
2692
1034
 
2693
- public class HealthRegen : MonoBehaviour
1035
+ protected override void Configure(IContainerBuilder builder)
2694
1036
  {
2695
- void Start()
2696
- {
2697
- // Poll every 0.5s with random initial delay (prevents all enemies syncing)
2698
- this.StartFunctionAsCoroutine(
2699
- action: RegenHealth,
2700
- updateRate: 0.5f,
2701
- useJitter: true // Adds 0-0.5s random initial delay
2702
- );
2703
- }
2704
-
2705
- void RegenHealth()
2706
- {
2707
- health += regenPerTick;
2708
- }
1037
+ // Register assigner + one-time scene scan + additive listener (default)
1038
+ builder.RegisterRelationalComponents(
1039
+ RelationalSceneAssignmentOptions.Default,
1040
+ enableAdditiveSceneListener: true
1041
+ );
2709
1042
  }
2710
- ```
2711
-
2712
- **Why jitter matters:**
2713
1043
 
2714
- - **Prevents frame spikes**: 100 enemies all polling at once = lag spike
2715
- - **Distributes load**: Staggers work across multiple frames
2716
- - **Simple API**: One parameter prevents performance issues
2717
-
2718
- **Other timing helpers:**
1044
+ // Zenject prefab instantiation with DI + relations
1045
+ using Zenject;
1046
+ using WallstopStudios.UnityHelpers.Integrations.Zenject;
2719
1047
 
2720
- ```csharp
2721
- // Execute after delay
2722
- this.ExecuteFunctionAfterDelay(() => SpawnBoss(), delay: 3f);
2723
-
2724
- // Execute next frame
2725
- this.ExecuteFunctionNextFrame(() => RefreshUI());
2726
-
2727
- // Execute at end of frame (after rendering)
2728
- this.ExecuteFunctionAfterFrame(() => CaptureScreenshot());
2729
-
2730
- // Execute N times over duration
2731
- StartCoroutine(Helpers.ExecuteOverTime(
2732
- action: () => SpawnMinion(),
2733
- totalCount: 10,
2734
- duration: 5f,
2735
- delay: true // Space evenly over duration
2736
- ));
1048
+ var enemy = Container.InstantiateComponentWithRelations(enemyPrefab, parent);
2737
1049
  ```
2738
1050
 
2739
- ---
2740
-
2741
- ### Cached Lookups: Find<T> with Tag Caching
2742
-
2743
- Eliminates repeated GameObject.FindGameObjectWithTag calls with automatic caching.
2744
-
2745
- ```csharp
2746
- using WallstopStudios.UnityHelpers.Core.Helper;
2747
-
2748
- public class GameController : MonoBehaviour
2749
- {
2750
- void Update()
2751
- {
2752
- // First call: searches scene and caches
2753
- // Subsequent calls: instant lookup from cache
2754
- AudioManager audio = Helpers.Find<AudioManager>("AudioManager");
2755
- audio.PlaySound("Click");
2756
-
2757
- // From within a component (for logging context)
2758
- PlayerData data = this.Find<PlayerData>("PlayerData");
2759
- }
2760
- }
2761
- ```
1051
+ See the full guide with scenarios, troubleshooting, and testing patterns: [Relational Components Guide](Docs/RELATIONAL_COMPONENTS.md)
2762
1052
 
2763
- **Why this helps:**
1053
+ ### Additional Helpers
2764
1054
 
2765
- - **Automatic caching**: First call populates cache, subsequent calls are O(1)
2766
- - **Fail-fast**: Logs warnings when tags are missing (can disable)
2767
- - **Memory efficient**: Only caches what you actually use
2768
- - **Invalidation safe**: Removes stale entries automatically
1055
+ - VContainer:
1056
+ - `resolver.InjectWithRelations(component)` inject + assign a single instance
1057
+ - `resolver.InstantiateComponentWithRelations(prefab, parent)` instantiate + inject + assign
1058
+ - `resolver.InjectGameObjectWithRelations(root, includeInactiveChildren)` inject hierarchy + assign
1059
+ - `resolver.InstantiateGameObjectWithRelations(prefab, parent)` — instantiate GO + inject + assign
2769
1060
 
2770
- ---
1061
+ - Zenject:
1062
+ - `container.InjectWithRelations(component)` — inject + assign a single instance
1063
+ - `container.InstantiateComponentWithRelations(prefab, parent)` — instantiate + assign
1064
+ - `container.InjectGameObjectWithRelations(root, includeInactiveChildren)` — inject hierarchy + assign
1065
+ - `container.InstantiateGameObjectWithRelations(prefab, parent)` — instantiate GO + inject + assign
2771
1066
 
2772
- ### UI Toolkit Progress Bars
1067
+ ### Additive Scene Loads
2773
1068
 
2774
- Professional progress bar components with multiple visual styles, ready to use.
1069
+ - VContainer: `RegisterRelationalComponents(..., enableAdditiveSceneListener: true)` registers a listener that hydrates components in newly loaded scenes.
1070
+ - Zenject: `RelationalComponentsInstaller` exposes a toggle “Listen For Additive Scenes” to register the same behavior.
1071
+ - Only the newly loaded scene is processed; other loaded scenes are not re‑scanned.
2775
1072
 
2776
- ```csharp
2777
- // Available styles:
2778
- // - CircularProgressBar: Circular/radial progress
2779
- // - RegularProgressBar: Traditional horizontal/vertical
2780
- // - LiquidProgressBar: Liquid/wave effect
2781
- // - GlitchProgressBar: Glitch/cyberpunk effect
2782
- // - MarchingAntsProgressBar: Animated border
2783
- // - WigglyProgressBar: Wavy animation
2784
- // - ArcedProgressBar: Partial arc/gauge
2785
-
2786
- // Use in UXML or code
2787
- var progressBar = new CircularProgressBar
2788
- {
2789
- Progress = 0.75f,
2790
- Radius = 50f,
2791
- Thickness = 10f,
2792
- Direction = CircularProgressBar.FillDirection.Clockwise,
2793
- TrackColor = Color.gray,
2794
- ProgressColor = Color.green
2795
- };
2796
- ```
1073
+ ### Performance Options
2797
1074
 
2798
- **Located in:** `Styles/Elements/Progress/`
1075
+ - One-time scene scan runs after container build; additive scenes are handled incrementally.
1076
+ - Single-pass scan (default) reduces `FindObjectsOfType` calls by scanning once and checking type ancestry.
1077
+ - VContainer: `new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)`
1078
+ - Zenject: `new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)`
1079
+ - Per-object paths (instantiate/inject helpers, pools) avoid global scans entirely for objects created via DI.
2799
1080
 
2800
1081
  ---
2801
1082
 
2802
1083
  ## Performance
2803
1084
 
2804
- Unity Helpers is built with performance as a top priority. Here are some key metrics:
1085
+ Unity Helpers is built with performance as a top priority:
2805
1086
 
2806
- ### Random Number Generation
1087
+ **Random Number Generation:**
2807
1088
 
2808
- Unity Helpers' random number generators are **10-15x faster** than Unity's built-in `UnityEngine.Random`:
1089
+ - 10-15x faster than Unity.Random (655-885M ops/sec vs 65-85M ops/sec)
1090
+ - Zero GC pressure with thread-local instances
1091
+ - [📊 Full Random Performance Benchmarks](Docs/RANDOM_PERFORMANCE.md)
2809
1092
 
2810
- - **UnityRandom**: 83M operations/sec
2811
- - **IllusionFlow** (PRNG.Instance): 609M operations/sec (**7x faster**)
2812
- - **RomuDuo** (fastest): 877M operations/sec (**10.5x faster**)
1093
+ **Spatial Queries:**
2813
1094
 
2814
- [📊 Full Random Performance Benchmarks](RANDOM_PERFORMANCE.md)
2815
-
2816
- ### Spatial Trees
1095
+ - O(log n) tree queries vs O(n) linear search
1096
+ - 100-1000x faster for large datasets
1097
+ - QuadTree2D: 10,000 objects = ~13 checks vs 10,000 checks
1098
+ - [📊 2D Performance Benchmarks](Docs/SPATIAL_TREE_2D_PERFORMANCE.md)
1099
+ - [📊 3D Performance Benchmarks](Docs/SPATIAL_TREE_3D_PERFORMANCE.md)
2817
1100
 
2818
- Spatial queries are dramatically faster than linear searches:
1101
+ **Memory Management:**
2819
1102
 
2820
- | Objects | Linear Search | QuadTree2D | Speedup |
2821
- | --------- | ------------- | ------------ | ------------ |
2822
- | 1,000 | 1M ops/sec | 283M ops/sec | **283x** |
2823
- | 10,000 | 100K ops/sec | 233M ops/sec | **2,330x** |
2824
- | 100,000 | 10K ops/sec | 174M ops/sec | **17,400x** |
2825
- | 1,000,000 | 1K ops/sec | 141M ops/sec | **141,000x** |
2826
-
2827
- _Measurements for small radius queries (1 unit)_
2828
-
2829
- [📊 2D Spatial Tree Benchmarks](SPATIAL_TREE_2D_PERFORMANCE.md) | [📊 3D Spatial Tree Benchmarks](SPATIAL_TREE_3D_PERFORMANCE.md)
2830
-
2831
- ### Editor Performance
2832
-
2833
- Editor tools use optimizations like:
2834
-
2835
- - Parallel processing for image operations
2836
- - Cached reflection for attribute systems
2837
- - Batch asset database operations
2838
- - Progress bars for long operations
2839
-
2840
- ## Contributing
1103
+ - Zero-allocation buffering pattern eliminates GC spikes
1104
+ - Professional-grade pooling for List, HashSet, Stack, Queue, Arrays
1105
+ - 5-10 FPS improvement in complex scenes from stable GC
2841
1106
 
2842
- We welcome contributions! This project uses [CSharpier](https://csharpier.com/) for consistent code formatting.
1107
+ **Reflection:**
2843
1108
 
2844
- ### Before Contributing
2845
-
2846
- 1. Install [CSharpier](https://csharpier.com/) (via editor plugin or CLI)
2847
- 2. Format changed files before committing
2848
- 3. Consider installing the [pre-commit hook](https://pre-commit.com/#3-install-the-git-hook-scripts)
2849
-
2850
- ### How to Contribute
2851
-
2852
- 1. Fork the repository
2853
- 2. Create a feature branch
2854
- 3. Make your changes and format with CSharpier
2855
- 4. Write/update tests if applicable
2856
- 5. Submit a pull request
2857
-
2858
- ### Reporting Issues
2859
-
2860
- Found a bug or have a feature request? [Open an issue](https://github.com/wallstop/unity-helpers/issues) on GitHub.
2861
-
2862
- ## License
2863
-
2864
- [MIT License](LICENSE)
1109
+ - IL-emitted delegates 10-100x faster than System.Reflection
1110
+ - Safe for IL2CPP and AOT platforms
1111
+ - [📊 Reflection Performance](Docs/REFLECTION_HELPERS.md)
2865
1112
 
2866
1113
  ---
2867
1114
 
2868
- ## Relational Components Guide
2869
-
2870
- For a complete, user-friendly walkthrough of `[ParentComponent]`, `[ChildComponent]`, and `[SiblingComponent]` including examples, recipes, and FAQs, see:
2871
-
2872
- - [Relational Components](RELATIONAL_COMPONENTS.md)
2873
-
2874
- Troubleshooting common issues (runtime-only assignment, filters, depth, inactive objects):
2875
-
2876
- - [Relational Components — Troubleshooting](RELATIONAL_COMPONENTS.md#troubleshooting)
2877
-
2878
- ---
1115
+ ## Documentation Index
2879
1116
 
2880
- ## Additional Resources
1117
+ **Start Here:**
2881
1118
 
2882
- - [Editor Tools Guide](EDITOR_TOOLS_GUIDE.md) - Complete documentation for all editor tools
2883
- - [Relational Components Guide](RELATIONAL_COMPONENTS.md) - Sibling/Parent/Child attributes with examples
2884
- - [Random Performance](RANDOM_PERFORMANCE.md) - Detailed RNG benchmarks
2885
- - [2D Spatial Trees](SPATIAL_TREE_2D_PERFORMANCE.md) - 2D spatial tree benchmarks
2886
- - [3D Spatial Trees](SPATIAL_TREE_3D_PERFORMANCE.md) - 3D spatial tree benchmarks
2887
- - [GitHub Repository](https://github.com/wallstop/unity-helpers)
2888
- - [Issue Tracker](https://github.com/wallstop/unity-helpers/issues)
2889
- - [NPM Package](https://www.npmjs.com/package/com.wallstop-studios.unity-helpers)
2890
-
2891
- ---
2892
-
2893
- ---
2894
-
2895
- ## 📚 Related Documentation
2896
-
2897
- **Quick Start:**
2898
-
2899
- - [Getting Started Guide](GETTING_STARTED.md) - Your first 5 minutes with Unity Helpers
2900
- - [Feature Index](INDEX.md) - Alphabetical reference of all features
2901
- - [Glossary](GLOSSARY.md) - Term definitions and concepts
1119
+ - 🚀 Getting Started — [Getting Started Guide](Docs/GETTING_STARTED.md)
1120
+ - 🔍 Feature Index — [Complete A-Z Index](Docs/INDEX.md)
1121
+ - 📖 Glossary — [Term Definitions](Docs/GLOSSARY.md)
2902
1122
 
2903
1123
  **Core Guides:**
2904
1124
 
2905
- - [Relational Components](RELATIONAL_COMPONENTS.md) - Auto-wiring component references
2906
- - [Effects System](EFFECTS_SYSTEM.md) - Data-driven buff/debuff system
2907
- - [Serialization](SERIALIZATION.md) - Save systems and networking
2908
- - [Editor Tools](EDITOR_TOOLS_GUIDE.md) - Asset pipeline automation
2909
- - [Math & Extensions](MATH_AND_EXTENSIONS.md) - Core utilities and helpers
1125
+ - Serialization Guide — [Serialization](Docs/SERIALIZATION.md)
1126
+ - Editor Tools Guide — [Editor Tools](Docs/EDITOR_TOOLS_GUIDE.md)
1127
+ - Math & Extensions — [Core Math & Extensions](Docs/MATH_AND_EXTENSIONS.md)
1128
+ - Singletons — [Singleton Utilities](Docs/SINGLETONS.md)
1129
+ - Relational Components — [Relational Components](Docs/RELATIONAL_COMPONENTS.md)
1130
+ - Effects System — [Effects System](Docs/EFFECTS_SYSTEM.md)
1131
+ - Data Structures — [Data Structures](Docs/DATA_STRUCTURES.md)
2910
1132
 
2911
1133
  **Spatial Trees:**
2912
1134
 
2913
- - [2D Spatial Trees Guide](SPATIAL_TREES_2D_GUIDE.md) - QuadTree, KDTree, RTree
2914
- - [3D Spatial Trees Guide](SPATIAL_TREES_3D_GUIDE.md) - OctTree, KDTree3D, RTree3D
2915
- - [Spatial Tree Semantics](SPATIAL_TREE_SEMANTICS.md) - Boundary behavior details
2916
- - [2D Performance](SPATIAL_TREE_2D_PERFORMANCE.md) | [3D Performance](SPATIAL_TREE_3D_PERFORMANCE.md)
2917
-
2918
- **Advanced:**
1135
+ - 2D Spatial Trees Guide — [2D Spatial Trees Guide](Docs/SPATIAL_TREES_2D_GUIDE.md)
1136
+ - 3D Spatial Trees Guide — [3D Spatial Trees Guide](Docs/SPATIAL_TREES_3D_GUIDE.md)
1137
+ - Spatial Tree Semantics — [Spatial Tree Semantics](Docs/SPATIAL_TREE_SEMANTICS.md)
1138
+ - Spatial Tree 2D Performance [Spatial Tree 2D Performance](Docs/SPATIAL_TREE_2D_PERFORMANCE.md)
1139
+ - Spatial Tree 3D Performance — [Spatial Tree 3D Performance](Docs/SPATIAL_TREE_3D_PERFORMANCE.md)
1140
+ - Hulls (Convex vs Concave) — [Hulls](Docs/HULLS.md)
2919
1141
 
2920
- - [Reflection Helpers](REFLECTION_HELPERS.md) - High-performance reflection
2921
- - [Data Structures](DATA_STRUCTURES.md) - Heaps, tries, sparse sets
2922
- - [Singletons](SINGLETONS.md) - Runtime and ScriptableObject patterns
1142
+ **Performance & Reference:**
2923
1143
 
2924
- **DI Integration:**
1144
+ - Random Performance — [Random Performance](Docs/RANDOM_PERFORMANCE.md)
1145
+ - Reflection Helpers — [Reflection Helpers](Docs/REFLECTION_HELPERS.md)
2925
1146
 
2926
- - [VContainer Sample](Samples~/DI%20-%20VContainer/README.md) - VContainer integration
2927
- - [Zenject Sample](Samples~/DI%20-%20Zenject/README.md) - Zenject integration
1147
+ **Project Info:**
2928
1148
 
2929
- **Need help?** [Open an issue](https://github.com/wallstop/unity-helpers/issues) | [Discussions](https://github.com/wallstop/unity-helpers/discussions)
1149
+ - Changelog [Changelog](Docs/CHANGELOG.md)
1150
+ - License — [License](Docs/LICENSE.md)
1151
+ - Third‑Party Notices — [Third‑Party Notices](Docs/THIRD_PARTY_NOTICES.md)
1152
+ - Contributing — [Contributing](Docs/CONTRIBUTING.md)
2930
1153
 
2931
1154
  ---
2932
1155
 
2933
- **Made with ❤️ by [Wallstop Studios](https://wallstopstudios.com)**
2934
-
2935
- _Unity Helpers is production-ready and actively maintained. Star the repo if you find it useful!_
2936
-
2937
- ## API Index
2938
-
2939
- - Namespaces
2940
- - `WallstopStudios.UnityHelpers.Core.Helper`
2941
- - `Helpers` — General utilities (layers, sprites, components, math, pooling)
2942
- - `Objects` — Unity-aware null checks and deterministic hashing
2943
- - `WallMath` — Positive modulo, wrapped add/increment, bounded floats/doubles
2944
- - `LineHelper` — Douglas–Peucker polyline simplification
2945
- - `Geometry` — Rect accumulation, sidedness tests
2946
- - `PathHelper`/`FileHelper`/`DirectoryHelper` — File and path utilities (editor/runtime)
2947
- - `SceneHelper` — Scene discovery and object retrieval (with disposal scope)
2948
- - `SpriteHelpers` — Make textures readable (editor)
2949
- - `UnityMainThreadDispatcher` — Enqueue work for main thread
2950
- - [`ReflectionHelpers`](REFLECTION_HELPERS.md) — High-performance field/property/method/ctor access and type scanning
2951
- - `FormattingHelpers` — Human-friendly sizes (e.g., bytes)
2952
- - `IterationHelpers` — 2D/3D array index enumeration
2953
- - `FuncBasedComparer`/`ReverseComparer` — Comparer utilities
2954
- - `StringInList` — Inspector dropdown for strings
2955
- - `WallstopStudios.UnityHelpers.Core.Extension`
2956
- - `UnityExtensions` — Unity-centric extensions (Rect/Bounds, Camera, Rigidbody2D, vectors)
2957
- - `RandomExtensions` — Random vectors/quaternions/colors and selections
2958
- - `StringExtensions` — Case transforms, UTF-8, JSON, Levenshtein
2959
- - `IEnumerableExtensions` — Collection conversions, infinite sequences, shuffle
2960
- - `IListExtensions`/`IReadonlyListExtensions` — Shuffle/shift/sort/search utilities
2961
- - `DictionaryExtensions` — GetOrAdd, GetOrElse helpers
2962
- - `AnimatorExtensions` — ResetTriggers convenience
2963
- - `AsyncOperationExtensions` — Await `AsyncOperation` (Task/ValueTask)
2964
- - `WallstopStudios.UnityHelpers.Core.DataStructure`
2965
- - Point/Bounds trees: `QuadTree2D<T>`, `KdTree2D<T>`, `KdTree3D<T>`, `RTree2D<T>`, `RTree3D<T>`
2966
- - Spatial hashes: `SpatialHash2D<T>`, `SpatialHash3D<T>`
2967
- - General: `Heap<T>`, `PriorityQueue<T>`, `Deque<T>`, `Trie`, `BitSet`, `CyclicBuffer<T>`, `SparseSet<T>`
2968
-
2969
- ### Quick Start: ReflectionHelpers
2970
-
2971
- ```csharp
2972
- using System;
2973
- using System.Collections;
2974
- using System.Collections.Generic;
2975
- using System.Reflection;
2976
- using WallstopStudios.UnityHelpers.Core.Helper;
2977
-
2978
- // 1) Fast field get/set (boxed)
2979
- public sealed class Player { public int Score; }
2980
- FieldInfo score = typeof(Player).GetField("Score");
2981
- var getScore = ReflectionHelpers.GetFieldGetter(score); // object -> object
2982
- var setScore = ReflectionHelpers.GetFieldSetter(score); // (object, object) -> void
2983
- var p = new Player();
2984
- setScore(p, 42);
2985
- UnityEngine.Debug.Log((int)getScore(p)); // 42
2986
-
2987
- // 2) Struct note: prefer typed ref setter
2988
- public struct Stat { public int Value; }
2989
- FieldInfo valueField = typeof(Stat).GetField("Value");
2990
- var setValue = ReflectionHelpers.GetFieldSetter<Stat, int>(valueField);
2991
- Stat s = default;
2992
- setValue(ref s, 100); // s.Value == 100
2993
-
2994
- // 3) Typed property getter
2995
- PropertyInfo prop = typeof(UnityEngine.Camera).GetProperty("orthographicSize");
2996
- var getSize = ReflectionHelpers.GetPropertyGetter<UnityEngine.Camera, float>(prop);
2997
- float size = getSize(UnityEngine.Camera.main);
2998
-
2999
- // 4) Typed static method invoker (two params)
3000
- MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) });
3001
- var concat2 = ReflectionHelpers.GetStaticMethodInvoker<string, string, string>(concat);
3002
- string joined = concat2("Hello ", "World");
3003
-
3004
- // 5) Low-allocation constructors and collections
3005
- var newList = ReflectionHelpers.GetParameterlessConstructor<List<int>>();
3006
- List<int> list = newList();
3007
-
3008
- var makeVec3Array = ReflectionHelpers.GetArrayCreator(typeof(UnityEngine.Vector3));
3009
- Array positions = makeVec3Array(256); // Vector3[256]
3010
-
3011
- IList names = ReflectionHelpers.CreateList(typeof(string), 64); // List<string>
3012
-
3013
- object intSet = ReflectionHelpers.CreateHashSet(typeof(int), 0); // HashSet<int>
3014
- var add = ReflectionHelpers.GetHashSetAdder(typeof(int));
3015
- add(intSet, 1);
3016
- add(intSet, 2);
3017
- ```
3018
-
3019
- Tip: Most collection-based APIs accept and fill buffers you provide (List<T>, arrays) to minimize allocations. Prefer passing a preallocated buffer for hot paths.
3020
-
3021
- ### Buffering Pattern
3022
-
3023
- Many APIs accept a caller-provided buffer (e.g., `List<T>`) and clear it before writing results. Reuse these buffers to avoid per-frame allocations and reduce GC pressure.
3024
-
3025
- Why it helps
3026
-
3027
- - Prevents transient allocations in tight loops (AI queries, physics scans).
3028
- - Keeps GC stable in gameplay spikes (hundreds/thousands of queries).
3029
-
3030
- Basics
3031
-
3032
- - Create buffers once per system and reuse them.
3033
- - APIs that take a `List<T>` will clear it before use and return the same list for chaining.
3034
- - **Ergonomic benefit**: Because these APIs return the same list you pass in, you can use them directly in `foreach` loops for maximum convenience.
3035
- - Don't share a single buffer across concurrent operations; allocate one per caller or use pooling.
3036
-
3037
- **Getting buffers easily:**
3038
-
3039
- - Use `Buffers<T>.List.Get()` for pooled `List<T>` with automatic return via `Dispose`
3040
- - Use `WallstopArrayPool<T>.Get()` for pooled arrays with automatic return
3041
- - Use `WallstopFastArrayPool<T>.Get()` for frequently-used short-lived arrays
3042
- - See [Pooling utilities](#pooling-utilities) below for detailed examples
3043
-
3044
- Examples
3045
-
3046
- ```csharp
3047
- // 2D tree query reuse
3048
- readonly List<Enemy> _enemiesBuffer = new(capacity: 256);
3049
-
3050
- void Scan(QuadTree2D<Enemy> tree, Vector2 position, float radius)
3051
- {
3052
- tree.GetElementsInRange(position, radius, _enemiesBuffer);
3053
- for (int i = 0; i < _enemiesBuffer.Count; ++i)
3054
- {
3055
- Enemy e = _enemiesBuffer[i];
3056
- // ... process
3057
- }
3058
- }
3059
-
3060
- // Ergonomic pattern: use the returned list directly in foreach
3061
- readonly List<Enemy> _nearbyBuffer = new(capacity: 128);
3062
-
3063
- void ProcessNearbyEnemies(QuadTree2D<Enemy> tree, Vector2 position, float radius)
3064
- {
3065
- // The API returns the same buffer, so you can chain it into foreach
3066
- foreach (Enemy enemy in tree.GetElementsInRange(position, radius, _nearbyBuffer))
3067
- {
3068
- enemy.ApplyDamage(10f);
3069
- }
3070
- }
3071
-
3072
- // Spatial hash with distinct results, approximate distance
3073
- readonly List<Unit> _units = new(512);
3074
- hash.Query(center, 10f, _units, distinct: true, exactDistance: false);
3075
-
3076
- // Components without allocations
3077
- readonly List<BoxCollider2D> _colliders = new(32);
3078
- gameObject.GetComponents(_colliders); // buffer is cleared by the API
3079
- ```
3080
-
3081
- Using the built‑in pool (advanced)
3082
-
3083
- ```csharp
3084
- using WallstopStudios.UnityHelpers.Utils;
3085
- using WallstopStudios.UnityHelpers.Core.DataStructure;
3086
-
3087
- // Get a pooled List<T> and return it automatically via Dispose
3088
- using PooledResource<List<int>> lease = Buffers<int>.List.Get(out List<int> list);
3089
-
3090
- // Use list here ...
3091
-
3092
- // On dispose, list is cleared and returned to the pool
3093
- ```
3094
-
3095
- Pooling + Buffering combined
3096
-
3097
- ```csharp
3098
- using WallstopStudios.UnityHelpers.Utils;
3099
- using WallstopStudios.UnityHelpers.Core.DataStructure;
3100
-
3101
- // Example: Use pooled buffer for spatial query
3102
- void FindNearbyEnemies(QuadTree2D<Enemy> tree, Vector2 position)
3103
- {
3104
- // Get pooled list - automatically returned when scope exits
3105
- using var lease = Buffers<Enemy>.List.Get(out List<Enemy> buffer);
3106
-
3107
- // Use it with spatial query - combines zero-alloc query + pooled buffer!
3108
- foreach (Enemy enemy in tree.GetElementsInRange(position, 10f, buffer))
3109
- {
3110
- enemy.TakeDamage(5f);
3111
- }
3112
- // buffer automatically returned to pool here
3113
- }
3114
-
3115
- // Array pooling example
3116
- void ProcessLargeDataset(int size)
3117
- {
3118
- using var lease = WallstopArrayPool<float>.Get(size, out float[] buffer);
3119
-
3120
- // Use buffer for temporary processing
3121
- for (int i = 0; i < size; i++)
3122
- {
3123
- buffer[i] = ComputeValue(i);
3124
- }
1156
+ ## Contributing
3125
1157
 
3126
- // buffer automatically returned to pool here
3127
- }
3128
- ```
1158
+ Contributions are welcome! Please feel free to submit a Pull Request.
3129
1159
 
3130
- Do / Don’t
1160
+ ### Formatting Assistance
3131
1161
 
3132
- - Do reuse buffers per system or component.
3133
- - Do treat buffers as temporary scratch space (APIs clear them first).
3134
- - Don’t keep references to pooled lists beyond their lease lifetime.
3135
- - Don’t share the same buffer across overlapping async/coroutine work.
1162
+ - Dependabot PRs: Formatting fixes (CSharpier + Prettier + markdownlint) are applied automatically by CI.
1163
+ - Contributor PRs: Opt-in formatting is available.
1164
+ - Comment on the PR with `/format` (aliases: `/autofix`, `/lint-fix`).
1165
+ - If the PR branch is in this repo, the bot pushes a commit with fixes.
1166
+ - If the PR is from a fork, the bot opens a formatting PR targeting the base branch.
1167
+ - The commenter must be the PR author or a maintainer/collaborator.
1168
+ - Or run the Actions workflow manually: Actions → "Opt-in Formatting" → Run workflow → enter the PR number.
1169
+ - Not everything is auto-fixable: link checks and YAML linting may still require manual changes.
3136
1170
 
3137
- <a id="pooling-utilities"></a>
3138
- Pooling utilities
1171
+ See more details in [CONTRIBUTING](Docs/CONTRIBUTING.md).
3139
1172
 
3140
- - `Buffers<T>` — pooled collections (List/Stack/Queue/HashSet) with `PooledResource` leases.
3141
- - Lists: `using var lease = Buffers<Foo>.List.Get(out List<Foo> list);`
3142
- - Stacks: `using var lease = Buffers<Foo>.Stack.Get(out Stack<Foo> stack);`
3143
- - HashSets: `using var lease = Buffers<Foo>.HashSet.Get(out HashSet<Foo> set);`
3144
- - Pattern: acquire → use → Dispose (returns to pool, clears collection).
3145
-
3146
- - `WallstopArrayPool<T>` — rent arrays by length with automatic return on dispose.
3147
- - Example: `using var lease = WallstopArrayPool<int>.Get(1024, out int[] buffer);`
3148
- - Use for temporary processing buffers, sorting, or interop with APIs that require arrays.
3149
-
3150
- - `WallstopFastArrayPool<T>` — fast array pool specialized for frequent short‑lived arrays.
3151
- - Example: `using var lease = WallstopFastArrayPool<string>.Get(count, out string[] buffer);`
3152
- - Used throughout Helpers for high‑frequency editor/runtime operations (e.g., asset searches).
1173
+ ---
3153
1174
 
3154
- How pooling + buffering help APIs
1175
+ ## License
3155
1176
 
3156
- - Spatial queries: pass a reusable `List<T>` to `GetElementsInRange/GetElementsInBounds` and iterate results without allocations.
3157
- - Component queries: `GetComponents(buffer)` clears and fills your buffer instead of allocating arrays.
3158
- - Editor utilities: temporary arrays/lists from pools keep import/scan tools snappy, especially inside loops.
1177
+ This project is licensed under the MIT License - see the [LICENSE](Docs/LICENSE.md) file for details.
3159
1178
 
3160
- ## Dependency Injection Integrations
1179
+ ## 2.0 Release Notes (Highlights)
3161
1180
 
3162
- - Auto-detected packages
3163
- - Zenject/Extenject: `com.extenject.zenject`, `com.modesttree.zenject`, `com.svermeulen.extenject`
3164
- - VContainer: `jp.cysharp.vcontainer`, `jp.hadashikick.vcontainer`
3165
- - Manual or source imports (no UPM)
3166
- - Add scripting defines in `Project Settings > Player > Other Settings > Scripting Define Symbols`:
3167
- - `ZENJECT_PRESENT` when Zenject/Extenject is present
3168
- - `VCONTAINER_PRESENT` when VContainer is present
3169
- - Add the define per target platform (e.g., Standalone, Android, iOS).
3170
- - Notes
3171
- - When the define is present, optional assemblies under `Runtime/Integrations/*` compile automatically and expose helpers like `RelationalComponentsInstaller` (Zenject) and `RegisterRelationalComponents()` (VContainer).
3172
- - If you use UPM, no manual defines are required — the package IDs above trigger symbols via `versionDefines` in the asmdefs.
3173
- - For test scenarios without LifetimeScope (VContainer) or SceneContext (Zenject), see [DI Integrations: Testing and Edge Cases](RELATIONAL_COMPONENTS.md#di-integrations-testing-and-edge-cases) for step‑by‑step patterns.
3174
-
3175
- - Quick start
3176
- - VContainer: in your `LifetimeScope.Configure`, call `builder.RegisterRelationalComponents()`.
3177
- - Zenject: add `RelationalComponentsInstaller` to your `SceneContext` (toggle scene scan if desired).
1181
+ - BinaryFormatter deprecated, still functional for trusted/legacy data:
1182
+ - `SerializationType.SystemBinary` is `[Obsolete]`. Use `SerializationType.Json` (System.Text.Json + Unity converters) or `SerializationType.Protobuf` (protobuf-net) for new work. Keep BinaryFormatter for trusted, non‑portable data only.
3178
1183
 
3179
- ```csharp
3180
- // VContainer LifetimeScope
3181
- using VContainer;
3182
- using VContainer.Unity;
3183
- using WallstopStudios.UnityHelpers.Integrations.VContainer;
3184
- protected override void Configure(IContainerBuilder builder)
3185
- {
3186
- builder.RegisterRelationalComponents();
3187
- }
1184
+ - GameObject JSON converter outputs structured JSON:
1185
+ - `GameObjectConverter` now writes a JSON object with `name`, `type` (assembly-qualified), and `instanceId` rather than a stringified placeholder.
3188
1186
 
3189
- // Zenject prefab instantiation with DI + relations
3190
- using Zenject;
3191
- using WallstopStudios.UnityHelpers.Integrations.Zenject;
3192
- var enemy = Container.InstantiateComponentWithRelations(enemyPrefab, parent);
3193
- ```
1187
+ - Minor robustness improvements:
1188
+ - Guarded stray `UnityEditor` imports in runtime files to ensure clean player builds.
3194
1189
 
3195
- See the full guide with scenarios, troubleshooting, and testing patterns: [Relational Components Guide](RELATIONAL_COMPONENTS.md)
1190
+ See [Serialization guide](Docs/SERIALIZATION.md) for AOT/IL2CPP guidance and Unity JSON options, and [Editor tools guide](Docs/EDITOR_TOOLS_GUIDE.md) for Editor tool usage details.