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.
- package/.editorconfig +1 -1
- package/.gitattributes +1 -1
- package/.githooks/pre-commit +31 -5
- package/.githooks/pre-push +50 -0
- package/.github/dependabot.yml +24 -2
- package/.github/scripts/check-markdown-links.ps1 +77 -0
- package/.github/scripts/check_markdown_links.py +89 -0
- package/.github/scripts/check_markdown_url_encoding.py +74 -0
- package/.github/scripts/validate_markdown_links.py +194 -0
- package/.github/workflows/csharpier-autofix.yml +152 -0
- package/.github/workflows/format-on-demand.yml +305 -0
- package/.github/workflows/lint-doc-links.yml +8 -5
- package/.github/workflows/markdown-json.yml +6 -2
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +195 -0
- package/.github/workflows/update-dotnet-tools.yml +80 -0
- package/.github/workflows/yaml-format-lint.yml +41 -0
- package/.lychee.toml +4 -4
- package/.markdownlint.jsonc +21 -0
- package/.pre-commit-config.yaml +11 -3
- package/.yamllint.yaml +31 -0
- package/AGENTS.md +5 -1
- package/Docs/CHANGELOG.md +11 -0
- package/Docs/CONTRIBUTING.md +49 -0
- package/Docs/CONTRIBUTING.md.meta +7 -0
- package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +4 -0
- package/Docs/EFFECTS_SYSTEM.md +1316 -0
- package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
- package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +50 -64
- package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
- package/Docs/HELPER_UTILITIES.md +885 -0
- package/Docs/HELPER_UTILITIES.md.meta +7 -0
- package/{INDEX.md → Docs/INDEX.md} +107 -62
- package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
- package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
- package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +111 -84
- package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +15 -0
- package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
- package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +241 -0
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +243 -0
- package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +1 -1
- package/Docs/UTILITY_COMPONENTS.md +906 -0
- package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
- package/Docs/VISUAL_COMPONENTS.md +337 -0
- package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
- package/Editor/AnimationEventEditor.cs +337 -160
- package/Editor/Core/Helper/AnimationEventHelpers.cs +178 -152
- package/Editor/CustomEditors/PersistentDirectoryGUI.cs +20 -11
- package/Editor/CustomEditors/TexturePlatformOverrideEntryDrawer.cs +11 -2
- package/Editor/FitTextureSizeWindow.cs +43 -19
- package/Editor/PersistentDirectorySettings.cs +64 -12
- package/Editor/PrefabChecker.cs +72 -5
- package/Editor/Sprites/AnimationCopier.cs +131 -55
- package/Editor/Sprites/AnimationCreator.cs +63 -22
- package/Editor/Sprites/AnimationViewerWindow.cs +42 -6
- package/Editor/Sprites/TexturePlatformNameHelper.cs +50 -39
- package/Editor/Sprites/TextureResizerWizard.cs +23 -1
- package/Editor/Sprites/TextureSettingsApplierWindow.cs +148 -85
- package/Editor/Tools/ImageBlurTool.cs +81 -10
- package/Editor/Utils/EditorUi.cs +1 -1
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +1 -1
- package/README.md +428 -2433
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/Core/Attributes/NotNullAttribute.cs +1 -3
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +50 -5
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +0 -1
- package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
- package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
- package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
- package/Runtime/Core/Extension/RandomExtensions.cs +68 -0
- package/Runtime/Core/Extension/WallstopStudiosLogger.cs +16 -0
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +4 -1
- package/Runtime/Core/Helper/ReflectionHelpers.cs +21 -10
- package/Runtime/Core/Helper/SpriteHelpers.cs +3 -1
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +45 -1
- package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +13 -5
- package/Runtime/Core/Serialization/JsonConverters/ResolutionConverter.cs +1 -1
- package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +1 -1
- package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
- package/Runtime/Core/Serialization/Serializer.cs +101 -0
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
- package/Runtime/Integrations/Reflex.meta +8 -0
- package/Runtime/Integrations/VContainer/AssemblyInfo.cs +9 -0
- package/Runtime/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Integrations/VContainer/ObjectResolverRelationalExtensions.cs +96 -0
- package/Runtime/Integrations/VContainer/RelationalComponentEntryPoint.cs +90 -10
- package/Runtime/Integrations/VContainer/RelationalComponentsBuilderExtensions.cs +13 -1
- package/Runtime/Integrations/VContainer/RelationalObjectPools.cs +114 -0
- package/Runtime/Integrations/VContainer/RelationalObjectPools.cs.meta +11 -0
- package/Runtime/Integrations/VContainer/RelationalSceneAssignmentOptions.cs +16 -4
- package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs +241 -0
- package/Runtime/Integrations/VContainer/RelationalSceneLoadListener.cs.meta +11 -0
- package/Runtime/Integrations/Zenject/AssemblyInfo.cs +9 -0
- package/Runtime/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Integrations/Zenject/DiContainerRelationalExtensions.cs +69 -2
- package/Runtime/Integrations/Zenject/RelationalComponentSceneInitializer.cs +89 -12
- package/Runtime/Integrations/Zenject/RelationalComponentsInstaller.cs +23 -1
- package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs +44 -0
- package/Runtime/Integrations/Zenject/RelationalMemoryPools.cs.meta +11 -0
- package/Runtime/Integrations/Zenject/RelationalSceneAssignmentOptions.cs +16 -10
- package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs +243 -0
- package/Runtime/Integrations/Zenject/RelationalSceneLoadListener.cs.meta +11 -0
- package/Runtime/Tags/AttributeMetadataCache.cs +1 -4
- package/Runtime/Utils/Buffers.cs +4 -4
- package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -2
- package/Runtime/Utils/SetTextureImportData.cs +3 -1
- package/Runtime/Utils/TextureScale.cs +10 -2
- package/Runtime/Visuals/UGUI/EnhancedImage.cs +6 -0
- package/Runtime/Visuals/UIToolkit/LayeredImage.cs +4 -1
- package/Samples~/DI - Reflex/README.md +527 -0
- package/Samples~/DI - Reflex/README.md.meta +7 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
- package/Samples~/DI - Reflex/Scripts.meta +8 -0
- package/Samples~/DI - Reflex.meta +8 -0
- package/Samples~/DI - VContainer/README.md +238 -56
- package/Samples~/DI - VContainer/Scripts/GameLifetimeScope.cs +22 -4
- package/Samples~/DI - VContainer/Scripts/RelationalConsumer.cs +5 -2
- package/Samples~/DI - VContainer/Scripts/Spawner.cs +113 -4
- package/Samples~/DI - Zenject/README.md +223 -58
- package/Samples~/DI - Zenject/Scripts/RelationalConsumer.cs +3 -0
- package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs +37 -0
- package/Samples~/DI - Zenject/Scripts/RelationalConsumerPool.cs.meta +12 -0
- package/Samples~/DI - Zenject/Scripts/SpawnerZenject.cs +74 -3
- package/Samples~/Random - PRNG/README.md +2 -1
- package/Samples~/Relational Components - Basic/README.md +3 -1
- package/Samples~/Serialization - JSON/README.md +2 -1
- package/Samples~/Spatial Structures - 2D and 3D/README.md +2 -1
- package/Samples~/UGUI - EnhancedImage/README.md +2 -1
- package/Samples~/UI Toolkit - MultiFile Selector (Editor)/README.md +2 -1
- package/Tests/Editor/Attributes/AnimationEventHelpersTests.cs +16 -0
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +32 -34
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
- package/Tests/Editor/Integrations/Reflex.meta +8 -0
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +21 -18
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +164 -0
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/VContainer/WallstopStudios.UnityHelpers.Tests.Editor.VContainer.asmdef +2 -1
- package/Tests/Editor/Integrations/Zenject/WallstopStudios.UnityHelpers.Tests.Editor.Zenject.asmdef +3 -2
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +127 -0
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +25 -23
- package/Tests/Editor/PersistentDirectorySettingsTests.cs +58 -0
- package/Tests/Editor/PersistentDirectorySettingsTests.cs.meta +11 -0
- package/Tests/Editor/PrefabCheckerReportTests.cs +32 -0
- package/Tests/Editor/PrefabCheckerReportTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +63 -0
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +1 -1
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +38 -0
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs.meta +11 -0
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +1 -1
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +12 -12
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +9 -9
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +3 -3
- package/Tests/Editor/Sprites/TexturePlatformNameHelperTests.cs +18 -0
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +5 -5
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +3 -3
- package/Tests/Editor/Sprites/TextureSettingsApplierWizardAdditionalTests.cs +4 -4
- package/Tests/Editor/Sprites/TextureSettingsApplierWizardTests.cs +4 -4
- package/Tests/Editor/Tools/ImageBlurToolTests.cs +22 -110
- package/Tests/Editor/Utils/CommonTestBase.cs +60 -1
- package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +6 -6
- package/Tests/Editor/Windows/FitTextureSizeWindowTests.cs +66 -74
- package/Tests/Runtime/Attributes/RelationalComponentInitializerTests.cs +4 -15
- package/Tests/Runtime/DataStructures/SpatialTree3DBoundsConsistencyTests.cs +29 -29
- package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +257 -221
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +91 -0
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +251 -233
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +6 -1
- package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +4 -1
- package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs +30 -0
- package/Tests/Runtime/Serialization/JsonConverterAdditionalTests.cs.meta +11 -0
- package/Tests/Runtime/Serialization/JsonConverterTests.cs +8 -12
- package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +16 -5
- package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
- package/Tests/Runtime/Serialization/SerializerAdditionalTests.cs +12 -0
- package/Tests/Runtime/Serialization/SerializerFileIoTests.cs +105 -0
- package/Tests/Runtime/Serialization/SerializerFileIoTests.cs.meta +11 -0
- package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs +247 -0
- package/Tests/Runtime/Serialization/UnityEngineObjectJsonTests.cs.meta +11 -0
- package/Tests/Runtime/TestUtils/CommonTestBase.cs +99 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
- package/Tests/Runtime/Utils/CoroutineHandlerTests.cs +1 -1
- package/Tests/Runtime/Utils/LZMAComprehensiveTests.cs +1 -1
- package/Tests/Runtime/Utils/LZMATests.cs +1 -1
- package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +5 -5
- package/Tests/Runtime/Visuals/EnhancedImageTests.cs +25 -56
- package/Tests/Runtime/Visuals/VisualsTestHelpers.cs +1 -8
- package/Tests/TestUtils.meta +8 -0
- package/package-lock.json.meta +7 -0
- package/package.json +13 -4
- package/scripts/check-eol.ps1 +4 -5
- package/scripts/lint-tests.ps1 +156 -0
- package/scripts/lint-tests.ps1.meta +7 -0
- package/scripts/normalize-eol.ps1 +6 -9
- package/.github/workflows/csharpier.yml +0 -135
- package/CHANGELOG.md +0 -0
- package/EFFECTS_SYSTEM.md +0 -242
- package/MATH_AND_EXTENSIONS.md +0 -316
- package/SPATIAL_TREE_2D_PERFORMANCE.md +0 -238
- package/SPATIAL_TREE_3D_PERFORMANCE.md +0 -240
- /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
- /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
- /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
- /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
- /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
- /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
- /package/{HULLS.md → Docs/HULLS.md} +0 -0
- /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
- /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
- /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
- /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
- /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
- /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
- /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
- /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
- /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
- /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
- /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
- /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
- /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
- /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
package/README.md
CHANGED
|
@@ -1,127 +1,61 @@
|
|
|
1
1
|
# Unity Helpers
|
|
2
2
|
|
|
3
|
-
[](https://unity.com/releases/
|
|
4
|
-
[](LICENSE)
|
|
5
|
-
[](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier.yml)
|
|
6
|
-
[](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml)
|
|
7
|
-
[](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml)
|
|
3
|
+
[](https://unity.com/releases/2021-lts)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/wallstop/unity-helpers/actions/workflows/csharpier-autofix.yml)
|
|
6
|
+
[](https://github.com/wallstop/unity-helpers/actions/workflows/markdown-json.yml)
|
|
7
|
+
[](https://github.com/wallstop/unity-helpers/actions/workflows/lint-doc-links.yml)
|
|
8
8
|
[](https://github.com/wallstop/unity-helpers/actions/workflows/npm-publish.yml)
|
|
9
9
|
|
|
10
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
+
**🔍 Looking for something specific?** Check the [Feature Index](Docs/INDEX.md)
|
|
77
28
|
|
|
78
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
**You want:** Understand integration points and architectural patterns
|
|
33
|
+
## 👋 First Time Here?
|
|
97
34
|
|
|
98
|
-
**
|
|
35
|
+
**Pick your starting point based on your biggest pain point:**
|
|
99
36
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
50
|
+
## ⚡ Top 5 Time-Savers
|
|
117
51
|
|
|
118
|
-
|
|
52
|
+
These features eliminate entire categories of repetitive work. Pick one that solves your immediate pain:
|
|
119
53
|
|
|
120
|
-
### 1. 🔌 Auto-Wire Components
|
|
54
|
+
### 1. 🔌 Auto-Wire Components
|
|
121
55
|
|
|
122
|
-
|
|
56
|
+
#### ⏱️ 10-20 min/script × 100 scripts = 20+ hours saved
|
|
123
57
|
|
|
124
|
-
Stop writing GetComponent boilerplate forever. Replace 20+ lines
|
|
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
|
|
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
|
|
86
|
+
### 2. 🎮 Data-Driven Effects
|
|
153
87
|
|
|
154
|
-
|
|
88
|
+
#### ⏱️ 2-4 hours/effect × 50 effects = 150+ hours saved
|
|
155
89
|
|
|
156
|
-
Designers create buffs/debuffs
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
145
|
+
### 4. 🎱 Professional Pooling
|
|
210
146
|
|
|
211
|
-
|
|
147
|
+
#### ⏱️ Eliminates GC spikes = 5-10 FPS in complex scenes
|
|
212
148
|
|
|
213
|
-
Zero-allocation queries with automatic cleanup. Thread-safe
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
###
|
|
201
|
+
### Hierarchy Traversal (Optimized)
|
|
311
202
|
|
|
312
203
|
```csharp
|
|
313
|
-
//
|
|
314
|
-
|
|
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
|
-
//
|
|
320
|
-
|
|
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
|
-
//
|
|
327
|
-
|
|
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
|
-
**
|
|
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
|
-
//
|
|
346
|
-
|
|
219
|
+
// Color averaging (4 methods: LAB, HSV, Weighted, Dominant)
|
|
220
|
+
Color teamColor = sprite.GetAverageColor(ColorAveragingMethod.LAB); // Perceptually accurate
|
|
347
221
|
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
//
|
|
360
|
-
|
|
225
|
+
// Smooth rotation in one line
|
|
226
|
+
transform.rotation = transform.GetAngleWithSpeed(target, rotationSpeed, Time.deltaTime);
|
|
361
227
|
|
|
362
|
-
|
|
228
|
+
// Safe destruction (works in editor AND runtime)
|
|
229
|
+
gameObject.SmartDestroy(); // No more #if UNITY_EDITOR everywhere
|
|
363
230
|
|
|
364
|
-
|
|
365
|
-
|
|
231
|
+
// Camera world bounds
|
|
232
|
+
Bounds visibleArea = Camera.main.OrthographicBounds(); // Perfect for culling/spawning
|
|
366
233
|
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
455
|
-
|
|
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
|
-
|
|
462
|
-
|
|
252
|
+
// Polyline simplification (Douglas–Peucker)
|
|
253
|
+
List<Vector2> simplified = LineHelper.Simplify(path, epsilon: 0.5f); // Reduce pathfinding waypoints
|
|
463
254
|
```
|
|
464
255
|
|
|
465
|
-
|
|
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
|
-
//
|
|
481
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
//
|
|
568
|
-
|
|
265
|
+
// String similarity for fuzzy search
|
|
266
|
+
int distance = playerName.LevenshteinDistance("jon"); // "john" = 1, close match!
|
|
569
267
|
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
290
|
+
## Why Teams Choose Unity Helpers
|
|
620
291
|
|
|
621
|
-
|
|
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
|
-
|
|
294
|
+
**Built for Real Projects:**
|
|
624
295
|
|
|
625
|
-
- **
|
|
626
|
-
- **
|
|
627
|
-
- **
|
|
628
|
-
-
|
|
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
|
-
|
|
302
|
+
**Who This Is For:**
|
|
631
303
|
|
|
632
|
-
- **
|
|
633
|
-
- **
|
|
634
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
445
|
+
## Quick Start Guide
|
|
1085
446
|
|
|
1086
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1094
|
-
using WallstopStudios.UnityHelpers.Core.Serialization;
|
|
451
|
+
---
|
|
1095
452
|
|
|
1096
|
-
|
|
1097
|
-
var pretty = Serializer.CreatePrettyJsonOptions();
|
|
1098
|
-
var fast = Serializer.CreateFastJsonOptions();
|
|
1099
|
-
var fastPOCO = Serializer.CreateFastPocoJsonOptions();
|
|
453
|
+
## Core Features
|
|
1100
454
|
|
|
1101
|
-
|
|
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
|
-
|
|
457
|
+
Unity Helpers includes **12 high-quality random number generators**, all implementing a rich `IRandom` interface:
|
|
1108
458
|
|
|
1109
|
-
|
|
1110
|
-
- Hot loops/large arrays: Fast or FastPOCO (POCO-only graphs)
|
|
1111
|
-
- Mixed graphs with Unity types: Fast
|
|
459
|
+
#### Available Generators
|
|
1112
460
|
|
|
1113
|
-
|
|
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
|
-
|
|
476
|
+
⭐ **Recommended**: Use `PRNG.Instance` (currently IllusionFlow)
|
|
1116
477
|
|
|
1117
|
-
|
|
478
|
+
#### Rich API
|
|
1118
479
|
|
|
1119
|
-
|
|
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 generator
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1597
|
-
[DxReadOnly]
|
|
1598
|
-
public float currentHealth;
|
|
717
|
+
Further reading: see the full guide [Effects System](Docs/EFFECTS_SYSTEM.md).
|
|
1599
718
|
|
|
1600
|
-
|
|
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
|
-
####
|
|
733
|
+
#### JSON Profiles
|
|
1624
734
|
|
|
1625
|
-
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
874
|
+
[Full guide: Data Structures](Docs/DATA_STRUCTURES.md)
|
|
1792
875
|
|
|
1793
|
-
|
|
876
|
+
---
|
|
1794
877
|
|
|
1795
|
-
|
|
878
|
+
### Core Math & Extensions
|
|
1796
879
|
|
|
1797
|
-
|
|
880
|
+
Numeric helpers, geometry primitives, Unity extensions, colors, collections, strings, directions.
|
|
1798
881
|
|
|
1799
|
-
|
|
882
|
+
See the guide: [Core Math & Extensions](Docs/MATH_AND_EXTENSIONS.md).
|
|
1800
883
|
|
|
1801
|
-
|
|
884
|
+
#### At a Glance
|
|
1802
885
|
|
|
1803
|
-
-
|
|
1804
|
-
-
|
|
1805
|
-
-
|
|
1806
|
-
-
|
|
1807
|
-
-
|
|
1808
|
-
-
|
|
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
|
-
|
|
895
|
+
---
|
|
1811
896
|
|
|
1812
|
-
|
|
1813
|
-
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
897
|
+
<a id="singleton-utilities-odin-compatible"></a>
|
|
1814
898
|
|
|
1815
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
906
|
+
---
|
|
1857
907
|
|
|
1858
|
-
|
|
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
|
-
|
|
910
|
+
Unity Helpers includes 20+ editor tools to streamline your workflow:
|
|
1874
911
|
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
-
|
|
918
|
+
[📖 Complete Editor Tools Documentation](Docs/EDITOR_TOOLS_GUIDE.md)
|
|
1895
919
|
|
|
1896
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
937
|
+
// Example: Use pooled buffer for spatial query
|
|
938
|
+
void FindNearbyEnemies(QuadTree2D<Enemy> tree, Vector2 position)
|
|
2513
939
|
{
|
|
2514
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2539
|
-
void
|
|
953
|
+
// Array pooling example
|
|
954
|
+
void ProcessLargeDataset(int size)
|
|
2540
955
|
{
|
|
2541
|
-
|
|
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
|
-
|
|
2579
|
-
|
|
2580
|
-
void RebuildUI()
|
|
958
|
+
// Use buffer for temporary processing
|
|
959
|
+
for (int i = 0; i < size; i++)
|
|
2581
960
|
{
|
|
2582
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
968
|
+
**Do / Don'ts:**
|
|
2602
969
|
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
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
|
-
|
|
2613
|
-
Component collider = gameObject.GetOrAddComponent(typeof(CircleCollider2D));
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
```
|
|
975
|
+
<a id="pooling-utilities"></a>
|
|
2617
976
|
|
|
2618
|
-
**
|
|
977
|
+
**Pooling utilities:**
|
|
2619
978
|
|
|
2620
|
-
- `
|
|
2621
|
-
- `
|
|
2622
|
-
- `
|
|
2623
|
-
-
|
|
2624
|
-
-
|
|
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
|
-
|
|
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
|
-
-
|
|
2629
|
-
-
|
|
2630
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
999
|
+
---
|
|
2638
1000
|
|
|
2639
|
-
|
|
2640
|
-
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
2641
|
-
using System.Collections.Generic;
|
|
1001
|
+
## Dependency Injection Integrations
|
|
2642
1002
|
|
|
2643
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1023
|
+
**Quick start:**
|
|
2687
1024
|
|
|
2688
|
-
|
|
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
|
-
|
|
1030
|
+
// VContainer — LifetimeScope
|
|
1031
|
+
using VContainer;
|
|
1032
|
+
using VContainer.Unity;
|
|
1033
|
+
using WallstopStudios.UnityHelpers.Integrations.VContainer;
|
|
2692
1034
|
|
|
2693
|
-
|
|
1035
|
+
protected override void Configure(IContainerBuilder builder)
|
|
2694
1036
|
{
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
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
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
**Other timing helpers:**
|
|
1044
|
+
// Zenject — prefab instantiation with DI + relations
|
|
1045
|
+
using Zenject;
|
|
1046
|
+
using WallstopStudios.UnityHelpers.Integrations.Zenject;
|
|
2719
1047
|
|
|
2720
|
-
|
|
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
|
-
|
|
1053
|
+
### Additional Helpers
|
|
2764
1054
|
|
|
2765
|
-
-
|
|
2766
|
-
-
|
|
2767
|
-
-
|
|
2768
|
-
-
|
|
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
|
-
###
|
|
1067
|
+
### Additive Scene Loads
|
|
2773
1068
|
|
|
2774
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1085
|
+
Unity Helpers is built with performance as a top priority:
|
|
2805
1086
|
|
|
2806
|
-
|
|
1087
|
+
**Random Number Generation:**
|
|
2807
1088
|
|
|
2808
|
-
|
|
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
|
-
|
|
2811
|
-
- **IllusionFlow** (PRNG.Instance): 609M operations/sec (**7x faster**)
|
|
2812
|
-
- **RomuDuo** (fastest): 877M operations/sec (**10.5x faster**)
|
|
1093
|
+
**Spatial Queries:**
|
|
2813
1094
|
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
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
|
-
|
|
1101
|
+
**Memory Management:**
|
|
2819
1102
|
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
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
|
-
|
|
1107
|
+
**Reflection:**
|
|
2843
1108
|
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
1117
|
+
**Start Here:**
|
|
2881
1118
|
|
|
2882
|
-
- [
|
|
2883
|
-
- [
|
|
2884
|
-
- [
|
|
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
|
-
- [
|
|
2906
|
-
- [
|
|
2907
|
-
- [
|
|
2908
|
-
- [
|
|
2909
|
-
- [
|
|
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)
|
|
2914
|
-
- [3D Spatial Trees Guide](SPATIAL_TREES_3D_GUIDE.md)
|
|
2915
|
-
- [Spatial Tree Semantics](SPATIAL_TREE_SEMANTICS.md)
|
|
2916
|
-
-
|
|
2917
|
-
|
|
2918
|
-
|
|
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
|
-
|
|
2921
|
-
- [Data Structures](DATA_STRUCTURES.md) - Heaps, tries, sparse sets
|
|
2922
|
-
- [Singletons](SINGLETONS.md) - Runtime and ScriptableObject patterns
|
|
1142
|
+
**Performance & Reference:**
|
|
2923
1143
|
|
|
2924
|
-
|
|
1144
|
+
- Random Performance — [Random Performance](Docs/RANDOM_PERFORMANCE.md)
|
|
1145
|
+
- Reflection Helpers — [Reflection Helpers](Docs/REFLECTION_HELPERS.md)
|
|
2925
1146
|
|
|
2926
|
-
|
|
2927
|
-
- [Zenject Sample](Samples~/DI%20-%20Zenject/README.md) - Zenject integration
|
|
1147
|
+
**Project Info:**
|
|
2928
1148
|
|
|
2929
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3127
|
-
}
|
|
3128
|
-
```
|
|
1158
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
3129
1159
|
|
|
3130
|
-
|
|
1160
|
+
### Formatting Assistance
|
|
3131
1161
|
|
|
3132
|
-
-
|
|
3133
|
-
-
|
|
3134
|
-
-
|
|
3135
|
-
-
|
|
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
|
-
|
|
3138
|
-
Pooling utilities
|
|
1171
|
+
See more details in [CONTRIBUTING](Docs/CONTRIBUTING.md).
|
|
3139
1172
|
|
|
3140
|
-
|
|
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
|
-
|
|
1175
|
+
## License
|
|
3155
1176
|
|
|
3156
|
-
|
|
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
|
-
##
|
|
1179
|
+
## 2.0 Release Notes (Highlights)
|
|
3161
1180
|
|
|
3162
|
-
-
|
|
3163
|
-
-
|
|
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
|
-
|
|
3180
|
-
|
|
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
|
-
|
|
3190
|
-
|
|
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
|
|
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.
|