com.wallstop-studios.unity-helpers 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/format-on-demand.yml +2 -2
- package/.github/workflows/markdown-json.yml +1 -1
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +4 -4
- package/.github/workflows/yaml-format-lint.yml +1 -1
- package/Docs/EFFECTS_SYSTEM.md +1316 -0
- package/{EFFECTS_SYSTEM_TUTORIAL.md → Docs/EFFECTS_SYSTEM_TUTORIAL.md} +1 -1
- package/{GETTING_STARTED.md → Docs/GETTING_STARTED.md} +10 -8
- package/{GLOSSARY.md → Docs/GLOSSARY.md} +4 -4
- package/Docs/HELPER_UTILITIES.md +885 -0
- package/Docs/HELPER_UTILITIES.md.meta +7 -0
- package/{INDEX.md → Docs/INDEX.md} +107 -62
- package/Docs/MATH_AND_EXTENSIONS.md +1039 -0
- package/{RANDOM_PERFORMANCE.md → Docs/RANDOM_PERFORMANCE.md} +15 -15
- package/{RELATIONAL_COMPONENTS.md → Docs/RELATIONAL_COMPONENTS.md} +21 -3
- package/{SPATIAL_TREES_2D_GUIDE.md → Docs/SPATIAL_TREES_2D_GUIDE.md} +2 -2
- package/{SPATIAL_TREES_3D_GUIDE.md → Docs/SPATIAL_TREES_3D_GUIDE.md} +1 -1
- package/{SPATIAL_TREE_2D_PERFORMANCE.md → Docs/SPATIAL_TREE_2D_PERFORMANCE.md} +64 -64
- package/{SPATIAL_TREE_3D_PERFORMANCE.md → Docs/SPATIAL_TREE_3D_PERFORMANCE.md} +64 -64
- package/Docs/UTILITY_COMPONENTS.md +906 -0
- package/Docs/UTILITY_COMPONENTS.md.meta +7 -0
- package/Docs/VISUAL_COMPONENTS.md +337 -0
- package/Docs/VISUAL_COMPONENTS.md.meta +7 -0
- package/Editor/Sprites/AnimationCopier.cs +3 -3
- package/README.md +69 -62
- package/Runtime/AssemblyInfo.cs +2 -0
- package/Runtime/Core/DataStructure/KDTree3D.cs +1 -1
- package/Runtime/Core/DataStructure/OctTree3D.cs +1 -1
- package/Runtime/Core/Extension/AsyncOperationExtensions.cs +122 -0
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +76 -90
- package/Runtime/Core/Serialization/ProtobufUnitySurrogates.cs +24 -29
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs +7 -0
- package/Runtime/Integrations/Reflex/AssemblyInfo.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs +198 -0
- package/Runtime/Integrations/Reflex/ContainerRelationalExtensions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalComponentsInstaller.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs +316 -0
- package/Runtime/Integrations/Reflex/RelationalReflexSceneBootstrapper.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs +86 -0
- package/Runtime/Integrations/Reflex/RelationalSceneAssignmentOptions.cs.meta +11 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef +20 -0
- package/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Integration.Reflex.asmdef.meta +7 -0
- package/Runtime/Integrations/Reflex.meta +8 -0
- package/Runtime/Utils/ScriptableObjectSingleton.cs +1 -1
- package/Samples~/DI - Reflex/README.md +527 -0
- package/Samples~/DI - Reflex/README.md.meta +7 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs +36 -0
- package/Samples~/DI - Reflex/Scripts/ReflexPaletteService.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexRelationalConsumer.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs +30 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSampleInstaller.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs +79 -0
- package/Samples~/DI - Reflex/Scripts/ReflexSpawner.cs.meta +11 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef +26 -0
- package/Samples~/DI - Reflex/Scripts/Samples.UnityHelpers.DI.Reflex.asmdef.meta +9 -0
- package/Samples~/DI - Reflex/Scripts.meta +8 -0
- package/Samples~/DI - Reflex.meta +8 -0
- package/Samples~/DI - VContainer/README.md +6 -5
- package/Samples~/DI - Zenject/README.md +6 -5
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +29 -31
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs +41 -0
- package/Tests/Editor/Integrations/Reflex/ReflexIntegrationCompilationTests.cs.meta +11 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef +27 -0
- package/Tests/Editor/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Editor.Reflex.asmdef.meta +7 -0
- package/Tests/Editor/Integrations/Reflex.meta +8 -0
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalEntryPointTests.cs +15 -16
- package/Tests/Editor/Integrations/VContainer/VContainerRelationalHelpersTests.cs +7 -13
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalHelpersTests.cs +7 -11
- package/Tests/Editor/Integrations/Zenject/ZenjectRelationalInitializerTests.cs +19 -21
- package/Tests/Editor/PersistentDirectorySettingsTests.cs +0 -1
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +0 -1
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -2
- package/Tests/Editor/Tools/ImageBlurToolTests.cs +1 -1
- package/Tests/Editor/Utils/CommonTestBase.cs +17 -0
- package/Tests/Editor/Utils/ScriptableObjectSingletonCreatorTests.cs +1 -1
- package/Tests/Runtime/Extensions/AsyncOperationExtensionsTests.cs +179 -0
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs +55 -0
- package/Tests/Runtime/Extensions/UnityLogTagFormatterEdgeTests.cs +84 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +445 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef +28 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.UnityHelpers.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/RelationalComponentsVContainerTests.cs +24 -29
- package/Tests/Runtime/Integrations/VContainer/RelationalObjectPoolsVContainerTests.cs +8 -3
- package/Tests/Runtime/Integrations/Zenject/RelationalComponentsZenjectTests.cs +10 -20
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTree2DPerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTree3DPerformanceTests.cs +1 -1
- package/Tests/Runtime/Serialization/JsonRoundtripComprehensiveTests.cs +4 -9
- package/Tests/Runtime/Serialization/ProtoRoundtripComprehensiveTests.cs +13 -13
- package/Tests/Runtime/TestUtils/CommonTestBase.cs +11 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs +111 -0
- package/Tests/Runtime/TestUtils/ReflexTestSupport.cs.meta +12 -0
- package/Tests/Runtime/Utils/MatchColliderToSpriteTests.cs +4 -4
- package/Tests/TestUtils.meta +8 -0
- package/package.json +6 -1
- package/EFFECTS_SYSTEM.md +0 -242
- package/MATH_AND_EXTENSIONS.md +0 -316
- /package/{CHANGELOG.md → Docs/CHANGELOG.md} +0 -0
- /package/{CHANGELOG.md.meta → Docs/CHANGELOG.md.meta} +0 -0
- /package/{CONTRIBUTING.md → Docs/CONTRIBUTING.md} +0 -0
- /package/{CONTRIBUTING.md.meta → Docs/CONTRIBUTING.md.meta} +0 -0
- /package/{DATA_STRUCTURES.md → Docs/DATA_STRUCTURES.md} +0 -0
- /package/{DATA_STRUCTURES.md.meta → Docs/DATA_STRUCTURES.md.meta} +0 -0
- /package/{EDITOR_TOOLS_GUIDE.md → Docs/EDITOR_TOOLS_GUIDE.md} +0 -0
- /package/{EDITOR_TOOLS_GUIDE.md.meta → Docs/EDITOR_TOOLS_GUIDE.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM.md.meta → Docs/EFFECTS_SYSTEM.md.meta} +0 -0
- /package/{EFFECTS_SYSTEM_TUTORIAL.md.meta → Docs/EFFECTS_SYSTEM_TUTORIAL.md.meta} +0 -0
- /package/{GETTING_STARTED.md.meta → Docs/GETTING_STARTED.md.meta} +0 -0
- /package/{GLOSSARY.md.meta → Docs/GLOSSARY.md.meta} +0 -0
- /package/{HULLS.md → Docs/HULLS.md} +0 -0
- /package/{HULLS.md.meta → Docs/HULLS.md.meta} +0 -0
- /package/{INDEX.md.meta → Docs/INDEX.md.meta} +0 -0
- /package/{LICENSE.md → Docs/LICENSE.md} +0 -0
- /package/{LICENSE.md.meta → Docs/LICENSE.md.meta} +0 -0
- /package/{MATH_AND_EXTENSIONS.md.meta → Docs/MATH_AND_EXTENSIONS.md.meta} +0 -0
- /package/{RANDOM_PERFORMANCE.md.meta → Docs/RANDOM_PERFORMANCE.md.meta} +0 -0
- /package/{REFLECTION_HELPERS.md → Docs/REFLECTION_HELPERS.md} +0 -0
- /package/{REFLECTION_HELPERS.md.meta → Docs/REFLECTION_HELPERS.md.meta} +0 -0
- /package/{RELATIONAL_COMPONENTS.md.meta → Docs/RELATIONAL_COMPONENTS.md.meta} +0 -0
- /package/{SERIALIZATION.md → Docs/SERIALIZATION.md} +0 -0
- /package/{SERIALIZATION.md.meta → Docs/SERIALIZATION.md.meta} +0 -0
- /package/{SINGLETONS.md → Docs/SINGLETONS.md} +0 -0
- /package/{SINGLETONS.md.meta → Docs/SINGLETONS.md.meta} +0 -0
- /package/{SPATIAL_TREES_2D_GUIDE.md.meta → Docs/SPATIAL_TREES_2D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREES_3D_GUIDE.md.meta → Docs/SPATIAL_TREES_3D_GUIDE.md.meta} +0 -0
- /package/{SPATIAL_TREE_2D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_2D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_3D_PERFORMANCE.md.meta → Docs/SPATIAL_TREE_3D_PERFORMANCE.md.meta} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md → Docs/SPATIAL_TREE_SEMANTICS.md} +0 -0
- /package/{SPATIAL_TREE_SEMANTICS.md.meta → Docs/SPATIAL_TREE_SEMANTICS.md.meta} +0 -0
- /package/{THIRD_PARTY_NOTICES.md → Docs/THIRD_PARTY_NOTICES.md} +0 -0
- /package/{THIRD_PARTY_NOTICES.md.meta → Docs/THIRD_PARTY_NOTICES.md.meta} +0 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
# Utility Components Guide
|
|
2
|
+
|
|
3
|
+
## TL;DR — Why Use These
|
|
4
|
+
|
|
5
|
+
Drop-in MonoBehaviour components that solve common game development problems without writing custom scripts. Add them to GameObjects for instant functionality like motion animation, collision forwarding, transform following, and visual state management.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- [Oscillator](#oscillator) — Automatic circular/elliptical motion
|
|
12
|
+
- [ChildSpawner](#childspawner) — Conditional prefab instantiation
|
|
13
|
+
- [CollisionProxy](#collisionproxy) — Event-based collision detection
|
|
14
|
+
- [CircleLineRenderer](#circlelinerenderer) — Visual circle debugging
|
|
15
|
+
- [MatchTransform](#matchtransform) — Follow another transform
|
|
16
|
+
- [SpriteRendererSync](#spriterenderersyncer) — Mirror sprite renderer state
|
|
17
|
+
- [SpriteRendererMetadata](#spriterenderermetadata) — Stacked visual modifications
|
|
18
|
+
- [CenterPointOffset](#centerpointoffset) — Define logical center points
|
|
19
|
+
- [AnimatorEnumStateMachine](#animatorenumstatemachine) — Type-safe animator control
|
|
20
|
+
- [CoroutineHandler](#coroutinehandler) — Singleton coroutine host
|
|
21
|
+
- [StartTracker](#starttracker) — Lifecycle tracking
|
|
22
|
+
- [MatchColliderToSprite](#matchcollidertosprite) — Auto-sync colliders
|
|
23
|
+
- [PolygonCollider2DOptimizer](#polygoncollider2doptimizer) — Simplify collider shapes
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<a id="oscillator"></a>
|
|
28
|
+
|
|
29
|
+
## Oscillator
|
|
30
|
+
|
|
31
|
+
**What it does:** Automatically moves a GameObject in a circular or elliptical pattern. Think "floating pickup" or "idle hover animation" without animators.
|
|
32
|
+
|
|
33
|
+
**Problem it solves:** Creating simple repetitive motion (hovering, bobbing, orbiting) usually requires animation curves or custom update loops. Oscillator handles it with three parameters.
|
|
34
|
+
|
|
35
|
+
### When to Use
|
|
36
|
+
|
|
37
|
+
✅ **Use for:**
|
|
38
|
+
|
|
39
|
+
- Floating/hovering UI elements
|
|
40
|
+
- Pickup items that gently bob
|
|
41
|
+
- Decorative objects with idle motion
|
|
42
|
+
- Circular patrol paths
|
|
43
|
+
- Simple pendulum motion
|
|
44
|
+
|
|
45
|
+
❌ **Don't use for:**
|
|
46
|
+
|
|
47
|
+
- Complex animation sequences (use Animator)
|
|
48
|
+
- Physics-based motion (use Rigidbody)
|
|
49
|
+
- Player/enemy movement (too rigid)
|
|
50
|
+
|
|
51
|
+
### How to Use
|
|
52
|
+
|
|
53
|
+
1. Add `Oscillator` component to any GameObject
|
|
54
|
+
2. Configure three parameters:
|
|
55
|
+
- **speed**: Rotation speed (radians per second)
|
|
56
|
+
- **width**: Horizontal amplitude (X-axis movement range)
|
|
57
|
+
- **height**: Vertical amplitude (Y-axis movement range)
|
|
58
|
+
|
|
59
|
+
```csharp
|
|
60
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
61
|
+
|
|
62
|
+
// Via code
|
|
63
|
+
Oscillator osc = gameObject.AddComponent<Oscillator>();
|
|
64
|
+
osc.speed = 2f; // Two radians/second
|
|
65
|
+
osc.width = 1f; // ±1 unit horizontally
|
|
66
|
+
osc.height = 0.5f; // ±0.5 units vertically
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Examples
|
|
70
|
+
|
|
71
|
+
**Gentle hover (coin pickup):**
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
speed = 3
|
|
75
|
+
width = 0
|
|
76
|
+
height = 0.2
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Figure-8 motion:**
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
speed = 2
|
|
83
|
+
width = 1
|
|
84
|
+
height = 1
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Horizontal sway:**
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
speed = 1
|
|
91
|
+
width = 0.5
|
|
92
|
+
height = 0
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Important Notes
|
|
96
|
+
|
|
97
|
+
- Updates `transform.localPosition` in Update()
|
|
98
|
+
- Motion is relative to original local position
|
|
99
|
+
- Starts from current time offset (unique per instance)
|
|
100
|
+
- Zero allocation per frame
|
|
101
|
+
- Works in 2D and 3D (only affects X and Y)
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
<a id="childspawner"></a>
|
|
106
|
+
|
|
107
|
+
## ChildSpawner
|
|
108
|
+
|
|
109
|
+
**What it does:** Conditionally instantiates prefabs as children based on environment (editor/development/release) with automatic duplicate prevention.
|
|
110
|
+
|
|
111
|
+
**Problem it solves:** Managing debug overlays, analytics, or development tools that should only exist in certain builds. Handles deduplication across scene loads and DontDestroyOnLoad scenarios.
|
|
112
|
+
|
|
113
|
+
### When to Use
|
|
114
|
+
|
|
115
|
+
✅ **Use for:**
|
|
116
|
+
|
|
117
|
+
- Debug UI overlays (FPS counters, console)
|
|
118
|
+
- Analytics managers (only in release builds)
|
|
119
|
+
- Development tools (cheat menus, level select)
|
|
120
|
+
- Platform-specific managers
|
|
121
|
+
- Scene-independent singleton spawners
|
|
122
|
+
|
|
123
|
+
❌ **Don't use for:**
|
|
124
|
+
|
|
125
|
+
- Regular gameplay objects (use Instantiate)
|
|
126
|
+
- One-time spawns (just call Instantiate)
|
|
127
|
+
- Objects that need complex initialization
|
|
128
|
+
|
|
129
|
+
### How to Use
|
|
130
|
+
|
|
131
|
+
Add `ChildSpawner` to a GameObject (often on a scene manager or empty GameObject):
|
|
132
|
+
|
|
133
|
+
**Inspector configuration:**
|
|
134
|
+
|
|
135
|
+
- **Prefabs**: Always spawned
|
|
136
|
+
- **Editor Only Prefabs**: Only in Unity Editor
|
|
137
|
+
- **Development Only Prefabs**: Only in Development builds
|
|
138
|
+
- **Spawn Method**: When to spawn (Awake/OnEnable/Start)
|
|
139
|
+
- **Dont Destroy On Load**: Persist across scenes
|
|
140
|
+
|
|
141
|
+
```csharp
|
|
142
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
143
|
+
|
|
144
|
+
// Via code
|
|
145
|
+
ChildSpawner spawner = gameObject.AddComponent<ChildSpawner>();
|
|
146
|
+
spawner._prefabs = new[] { analyticsPrefab };
|
|
147
|
+
spawner._developmentOnlyPrefabs = new[] { debugMenuPrefab };
|
|
148
|
+
spawner._spawnMethod = ChildSpawner.SpawnMethod.Awake;
|
|
149
|
+
spawner._dontDestroyOnLoad = true;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Deduplication Behavior
|
|
153
|
+
|
|
154
|
+
ChildSpawner prevents duplicate instantiation:
|
|
155
|
+
|
|
156
|
+
```csharp
|
|
157
|
+
// Spawns DebugCanvas once
|
|
158
|
+
ChildSpawner spawner1 = obj1.AddComponent<ChildSpawner>();
|
|
159
|
+
spawner1._prefabs = new[] { debugCanvasPrefab };
|
|
160
|
+
|
|
161
|
+
// This will NOT spawn a second DebugCanvas (detects existing instance)
|
|
162
|
+
ChildSpawner spawner2 = obj2.AddComponent<ChildSpawner>();
|
|
163
|
+
spawner2._prefabs = new[] { debugCanvasPrefab };
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Deduplication uses prefab asset path matching.
|
|
167
|
+
|
|
168
|
+
### Spawn Methods
|
|
169
|
+
|
|
170
|
+
- **Awake**: Spawns before anything else (use for foundational systems)
|
|
171
|
+
- **OnEnable**: Spawns when component enabled (use for dynamic spawning)
|
|
172
|
+
- **Start**: Spawns after all Awake calls (use when dependencies needed)
|
|
173
|
+
|
|
174
|
+
### DontDestroyOnLoad
|
|
175
|
+
|
|
176
|
+
When enabled:
|
|
177
|
+
|
|
178
|
+
- Spawned objects persist across scene loads
|
|
179
|
+
- Deduplication works across scene transitions
|
|
180
|
+
- Objects aren't destroyed when loading new scenes
|
|
181
|
+
|
|
182
|
+
Typical use case:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
Scene 1: ChildSpawner spawns AnalyticsManager with DontDestroyOnLoad
|
|
186
|
+
Scene 2 loads: Same ChildSpawner detects existing AnalyticsManager, doesn't spawn duplicate
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
<a id="collisionproxy"></a>
|
|
192
|
+
|
|
193
|
+
## CollisionProxy
|
|
194
|
+
|
|
195
|
+
**What it does:** Exposes Unity's 2D collision callbacks as C# events, enabling composition-based collision handling without inheriting from MonoBehaviour.
|
|
196
|
+
|
|
197
|
+
**Problem it solves:** To receive collision events in Unity, you traditionally override `OnCollisionEnter2D` etc. in a MonoBehaviour subclass. CollisionProxy lets you subscribe to events instead, supporting multiple listeners and decoupled architectures.
|
|
198
|
+
|
|
199
|
+
### When to Use
|
|
200
|
+
|
|
201
|
+
✅ **Use for:**
|
|
202
|
+
|
|
203
|
+
- Composition over inheritance designs
|
|
204
|
+
- Multiple systems reacting to same collision
|
|
205
|
+
- Decoupling collision logic from GameObject code
|
|
206
|
+
- Testing collision responses
|
|
207
|
+
- Dynamic behavior attachment/detachment
|
|
208
|
+
|
|
209
|
+
❌ **Don't use for:**
|
|
210
|
+
|
|
211
|
+
- Simple single-handler cases (override is fine)
|
|
212
|
+
- 3D collisions (only supports 2D)
|
|
213
|
+
- High-frequency collisions (event overhead)
|
|
214
|
+
|
|
215
|
+
### How to Use
|
|
216
|
+
|
|
217
|
+
1. Add `CollisionProxy` to GameObject with Collider2D
|
|
218
|
+
2. Subscribe to events from other scripts
|
|
219
|
+
|
|
220
|
+
```csharp
|
|
221
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
222
|
+
|
|
223
|
+
CollisionProxy proxy = gameObject.AddComponent<CollisionProxy>();
|
|
224
|
+
|
|
225
|
+
// Subscribe to enter event
|
|
226
|
+
proxy.OnCollisionEnter += HandleCollision;
|
|
227
|
+
proxy.OnTriggerEnter += HandleTrigger;
|
|
228
|
+
|
|
229
|
+
void HandleCollision(Collision2D collision)
|
|
230
|
+
{
|
|
231
|
+
Debug.Log($"Hit {collision.gameObject.name}");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
void HandleTrigger(Collider2D other)
|
|
235
|
+
{
|
|
236
|
+
Debug.Log($"Triggered by {other.gameObject.name}");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Cleanup
|
|
240
|
+
void OnDestroy()
|
|
241
|
+
{
|
|
242
|
+
proxy.OnCollisionEnter -= HandleCollision;
|
|
243
|
+
proxy.OnTriggerEnter -= HandleTrigger;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Available Events
|
|
248
|
+
|
|
249
|
+
**Collision events** (Collision2D parameter):
|
|
250
|
+
|
|
251
|
+
- `OnCollisionEnter`
|
|
252
|
+
- `OnCollisionStay`
|
|
253
|
+
- `OnCollisionExit`
|
|
254
|
+
|
|
255
|
+
**Trigger events** (Collider2D parameter):
|
|
256
|
+
|
|
257
|
+
- `OnTriggerEnter`
|
|
258
|
+
- `OnTriggerStay`
|
|
259
|
+
- `OnTriggerExit`
|
|
260
|
+
|
|
261
|
+
### Multiple Subscribers Example
|
|
262
|
+
|
|
263
|
+
```csharp
|
|
264
|
+
// Health system subscribes
|
|
265
|
+
healthSystem.OnDamageTaken += proxy.OnCollisionEnter;
|
|
266
|
+
|
|
267
|
+
// Sound system subscribes to same event
|
|
268
|
+
soundSystem.PlayImpactSound += proxy.OnCollisionEnter;
|
|
269
|
+
|
|
270
|
+
// Analytics subscribes
|
|
271
|
+
analytics.TrackCollision += proxy.OnCollisionEnter;
|
|
272
|
+
|
|
273
|
+
// All three systems react to the same collision independently
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
<a id="circlelinerenderer"></a>
|
|
279
|
+
|
|
280
|
+
## CircleLineRenderer
|
|
281
|
+
|
|
282
|
+
**What it does:** Visualizes CircleCollider2D with a dynamically-drawn circle using LineRenderer, with randomized appearance for visual variety.
|
|
283
|
+
|
|
284
|
+
**Problem it solves:** Seeing collision bounds at runtime for debugging, or creating dynamic range indicators (ability ranges, explosion radii) without pre-made sprites.
|
|
285
|
+
|
|
286
|
+
### When to Use
|
|
287
|
+
|
|
288
|
+
✅ **Use for:**
|
|
289
|
+
|
|
290
|
+
- Debug visualization of collision bounds
|
|
291
|
+
- Dynamic range indicators (attack range, detection radius)
|
|
292
|
+
- Area-of-effect visualization
|
|
293
|
+
- Circular UI elements
|
|
294
|
+
- Animated selection rings
|
|
295
|
+
|
|
296
|
+
❌ **Don't use for:**
|
|
297
|
+
|
|
298
|
+
- Production graphics (performance overhead)
|
|
299
|
+
- Static circles (use a sprite)
|
|
300
|
+
- Thousands of circles (expensive)
|
|
301
|
+
|
|
302
|
+
### How to Use
|
|
303
|
+
|
|
304
|
+
1. Add `CircleLineRenderer` to GameObject with `CircleCollider2D`
|
|
305
|
+
2. Component automatically:
|
|
306
|
+
- Adds LineRenderer if not present
|
|
307
|
+
- Syncs circle size to collider radius
|
|
308
|
+
- Randomizes line width for visual variety
|
|
309
|
+
|
|
310
|
+
```csharp
|
|
311
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
312
|
+
|
|
313
|
+
CircleLineRenderer circleVis = gameObject.AddComponent<CircleLineRenderer>();
|
|
314
|
+
circleVis.color = Color.red;
|
|
315
|
+
circleVis.minLineWidth = 0.05f;
|
|
316
|
+
circleVis.maxLineWidth = 0.15f;
|
|
317
|
+
circleVis.updateRateSeconds = 0.5f; // Refresh twice per second
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Configuration
|
|
321
|
+
|
|
322
|
+
- **minLineWidth / maxLineWidth**: Random line thickness range
|
|
323
|
+
- **numSegments**: Circle smoothness (more segments = smoother, more expensive)
|
|
324
|
+
- **baseSegments**: Minimum segments (scaled by radius)
|
|
325
|
+
- **updateRateSeconds**: How often to randomize appearance
|
|
326
|
+
- **color**: Line color
|
|
327
|
+
|
|
328
|
+
### Update Rate
|
|
329
|
+
|
|
330
|
+
Lower values = more frequent randomization = more visual variety but higher CPU cost
|
|
331
|
+
|
|
332
|
+
```text
|
|
333
|
+
0.1f = Very active (10 updates/sec)
|
|
334
|
+
0.5f = Moderate (2 updates/sec)
|
|
335
|
+
2.0f = Subtle (0.5 updates/sec)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
<a id="matchtransform"></a>
|
|
341
|
+
|
|
342
|
+
## MatchTransform
|
|
343
|
+
|
|
344
|
+
**What it does:** Makes one transform follow another with configurable update timing and offset.
|
|
345
|
+
|
|
346
|
+
**Problem it solves:** Following transforms (UI name plates, camera targets, position constraints) usually requires custom scripts. MatchTransform handles it declaratively.
|
|
347
|
+
|
|
348
|
+
### When to Use
|
|
349
|
+
|
|
350
|
+
✅ **Use for:**
|
|
351
|
+
|
|
352
|
+
- UI name plates following 3D objects
|
|
353
|
+
- Camera targets
|
|
354
|
+
- Object attachments (weapon to hand)
|
|
355
|
+
- Position constraints
|
|
356
|
+
- Simple parent-child alternatives
|
|
357
|
+
|
|
358
|
+
❌ **Don't use for:**
|
|
359
|
+
|
|
360
|
+
- Smooth following (use Vector3.Lerp in Update)
|
|
361
|
+
- Physics-based following (use joints/springs)
|
|
362
|
+
- Complex multi-axis constraints (use Unity Constraints)
|
|
363
|
+
|
|
364
|
+
### How to Use
|
|
365
|
+
|
|
366
|
+
```csharp
|
|
367
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
368
|
+
|
|
369
|
+
MatchTransform matcher = uiPlate.AddComponent<MatchTransform>();
|
|
370
|
+
matcher.toMatch = enemyTransform;
|
|
371
|
+
matcher.localOffset = new Vector3(0, 2, 0); // 2 units above target
|
|
372
|
+
matcher.mode = MatchTransform.Mode.LateUpdate; // Update after camera
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Update Modes
|
|
376
|
+
|
|
377
|
+
- **Update**: Standard update timing (most common)
|
|
378
|
+
- **FixedUpdate**: For physics-synced following
|
|
379
|
+
- **LateUpdate**: After all Updates (best for camera followers)
|
|
380
|
+
- **Awake**: Set once at startup, then never update
|
|
381
|
+
- **Start**: Set once after Awake, then never update
|
|
382
|
+
|
|
383
|
+
### Local Offset
|
|
384
|
+
|
|
385
|
+
```csharp
|
|
386
|
+
// Offset is added to target position
|
|
387
|
+
matcher.localOffset = new Vector3(1, 0, 0); // 1 unit to the right
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Self-Matching
|
|
391
|
+
|
|
392
|
+
If `toMatch` is the same GameObject, applies offset once then disables:
|
|
393
|
+
|
|
394
|
+
```csharp
|
|
395
|
+
matcher.toMatch = transform; // Self-reference
|
|
396
|
+
matcher.localOffset = new Vector3(5, 0, 0);
|
|
397
|
+
// GameObject moves 5 units right once, then MatchTransform disables itself
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
<a id="spriterenderersyncer"></a>
|
|
403
|
+
|
|
404
|
+
## SpriteRendererSync
|
|
405
|
+
|
|
406
|
+
**What it does:** Mirrors one SpriteRenderer's properties (sprite, color, material, sorting) to another, with selective property matching.
|
|
407
|
+
|
|
408
|
+
**Problem it solves:** Creating shadow sprites, duplicate renderers for effects, or layered rendering often requires manually keeping multiple SpriteRenderers in sync.
|
|
409
|
+
|
|
410
|
+
### When to Use
|
|
411
|
+
|
|
412
|
+
✅ **Use for:**
|
|
413
|
+
|
|
414
|
+
- Shadow sprites (black silhouette following character)
|
|
415
|
+
- Duplicate renderers for effects (outlines, glows)
|
|
416
|
+
- Mirrored sprites (reflection effects)
|
|
417
|
+
- Synchronized sprite swapping
|
|
418
|
+
- VFX layers
|
|
419
|
+
|
|
420
|
+
❌ **Don't use for:**
|
|
421
|
+
|
|
422
|
+
- Single sprite rendering
|
|
423
|
+
- Particle effects (use ParticleSystem)
|
|
424
|
+
- Complex multi-layer rendering (use LayeredImage for UI)
|
|
425
|
+
|
|
426
|
+
### How to Use
|
|
427
|
+
|
|
428
|
+
```csharp
|
|
429
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
430
|
+
|
|
431
|
+
// On the "follower" sprite renderer
|
|
432
|
+
SpriteRendererSyncer syncer = shadowRenderer.AddComponent<SpriteRendererSyncer>();
|
|
433
|
+
syncer.toMatch = characterRenderer;
|
|
434
|
+
syncer.matchColor = false; // Don't copy color (shadow should be black)
|
|
435
|
+
syncer.matchMaterial = true;
|
|
436
|
+
syncer.matchSortingLayer = true;
|
|
437
|
+
syncer.matchOrderInLayer = true;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Configuration Options
|
|
441
|
+
|
|
442
|
+
**What to sync:**
|
|
443
|
+
|
|
444
|
+
- `matchColor`: Copy color tint
|
|
445
|
+
- `matchMaterial`: Copy material
|
|
446
|
+
- `matchSortingLayer`: Copy sorting layer
|
|
447
|
+
- `matchOrderInLayer`: Copy order in layer
|
|
448
|
+
- Sprite, flipX, flipY are always copied
|
|
449
|
+
|
|
450
|
+
**Dynamic source:**
|
|
451
|
+
|
|
452
|
+
```csharp
|
|
453
|
+
// Change what to match at runtime
|
|
454
|
+
syncer.DynamicToMatch = () => GetCurrentWeaponRenderer();
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**Sorting override:**
|
|
458
|
+
|
|
459
|
+
```csharp
|
|
460
|
+
// Override order in layer dynamically
|
|
461
|
+
syncer.DynamicSortingOrderOverride = () => characterRenderer.sortingOrder - 1; // Always behind
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Update Timing
|
|
465
|
+
|
|
466
|
+
Syncs in `LateUpdate()` to ensure source renderer has updated first.
|
|
467
|
+
|
|
468
|
+
### Example: Shadow Effect
|
|
469
|
+
|
|
470
|
+
```csharp
|
|
471
|
+
// Create shadow GameObject
|
|
472
|
+
GameObject shadow = new GameObject("Shadow");
|
|
473
|
+
shadow.transform.parent = character.transform;
|
|
474
|
+
shadow.transform.localPosition = new Vector3(0.2f, -0.2f, 0); // Offset
|
|
475
|
+
|
|
476
|
+
SpriteRenderer shadowRenderer = shadow.AddComponent<SpriteRenderer>();
|
|
477
|
+
SpriteRendererSyncer syncer = shadow.AddComponent<SpriteRendererSyncer>();
|
|
478
|
+
|
|
479
|
+
syncer.toMatch = character.GetComponent<SpriteRenderer>();
|
|
480
|
+
syncer.matchColor = false;
|
|
481
|
+
shadowRenderer.color = new Color(0, 0, 0, 0.5f); // Semi-transparent black
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
<a id="spriterenderermetadata"></a>
|
|
487
|
+
|
|
488
|
+
## SpriteRendererMetadata
|
|
489
|
+
|
|
490
|
+
**What it does:** Stack-based color and material management for SpriteRenderers, allowing multiple systems to modify visuals with automatic priority handling and restoration.
|
|
491
|
+
|
|
492
|
+
**Problem it solves:** When multiple systems want to modify a sprite's color (damage flash, power-up glow, status effect) simultaneously, manually coordinating who "owns" the color is error-prone. This provides push/pop semantics with component-based ownership.
|
|
493
|
+
|
|
494
|
+
### When to Use
|
|
495
|
+
|
|
496
|
+
✅ **Use for:**
|
|
497
|
+
|
|
498
|
+
- Damage flashes (red tint on hit)
|
|
499
|
+
- Status effects (poison = green, frozen = blue)
|
|
500
|
+
- Power-up visuals (glow effects)
|
|
501
|
+
- Multiple overlapping visual modifiers
|
|
502
|
+
- Temporary material swaps
|
|
503
|
+
|
|
504
|
+
❌ **Don't use for:**
|
|
505
|
+
|
|
506
|
+
- Single, exclusive color changes (just set color directly)
|
|
507
|
+
- Animations (use Animator)
|
|
508
|
+
- Permanent changes (just set the property)
|
|
509
|
+
|
|
510
|
+
### How to Use
|
|
511
|
+
|
|
512
|
+
```csharp
|
|
513
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
514
|
+
|
|
515
|
+
SpriteRenderer renderer = GetComponent<SpriteRenderer>();
|
|
516
|
+
SpriteRendererMetadata metadata = renderer.GetComponent<SpriteRendererMetadata>();
|
|
517
|
+
if (metadata == null)
|
|
518
|
+
metadata = renderer.gameObject.AddComponent<SpriteRendererMetadata>();
|
|
519
|
+
|
|
520
|
+
// Component A pushes red color
|
|
521
|
+
metadata.PushColor(this, Color.red);
|
|
522
|
+
|
|
523
|
+
// Component B pushes blue color (takes precedence)
|
|
524
|
+
metadata.PushColor(otherComponent, Color.blue);
|
|
525
|
+
// Renderer is now blue
|
|
526
|
+
|
|
527
|
+
// Component B pops its color
|
|
528
|
+
metadata.PopColor(otherComponent);
|
|
529
|
+
// Renderer reverts to red (Component A's color)
|
|
530
|
+
|
|
531
|
+
// Component A pops its color
|
|
532
|
+
metadata.PopColor(this);
|
|
533
|
+
// Renderer reverts to original color
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Stack Operations
|
|
537
|
+
|
|
538
|
+
**Push/Pop (LIFO - Last In, First Out):**
|
|
539
|
+
|
|
540
|
+
```csharp
|
|
541
|
+
metadata.PushColor(owner, Color.red); // Add to top of stack
|
|
542
|
+
metadata.PopColor(owner); // Remove from top (must match owner)
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**PushBack (add to bottom, lower priority):**
|
|
546
|
+
|
|
547
|
+
```csharp
|
|
548
|
+
metadata.PushBackColor(owner, Color.yellow); // Added to bottom, doesn't change current color unless stack is empty
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Component Ownership
|
|
552
|
+
|
|
553
|
+
Each color/material is tagged with the Component that pushed it:
|
|
554
|
+
|
|
555
|
+
```csharp
|
|
556
|
+
public class DamageFlash : MonoBehaviour
|
|
557
|
+
{
|
|
558
|
+
void OnDamage()
|
|
559
|
+
{
|
|
560
|
+
metadata.PushColor(this, Color.red);
|
|
561
|
+
Invoke(nameof(RemoveFlash), 0.1f);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
void RemoveFlash()
|
|
565
|
+
{
|
|
566
|
+
metadata.PopColor(this); // Only removes if this component owns top of stack
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
This prevents Component A from accidentally removing Component B's color.
|
|
572
|
+
|
|
573
|
+
### Material Stacking
|
|
574
|
+
|
|
575
|
+
Works identically for materials:
|
|
576
|
+
|
|
577
|
+
```csharp
|
|
578
|
+
metadata.PushMaterial(this, glowMaterial);
|
|
579
|
+
// ... later
|
|
580
|
+
metadata.PopMaterial(this);
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Original State
|
|
584
|
+
|
|
585
|
+
```csharp
|
|
586
|
+
Color original = metadata.OriginalColor; // Color before any modifications
|
|
587
|
+
Color current = metadata.CurrentColor; // Current top-of-stack color
|
|
588
|
+
|
|
589
|
+
Material originalMat = metadata.OriginalMaterial;
|
|
590
|
+
Material currentMat = metadata.CurrentMaterial;
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Important Notes
|
|
594
|
+
|
|
595
|
+
- Automatically detects and stores original color/material in `Awake()`
|
|
596
|
+
- Survives enable/disable cycles
|
|
597
|
+
- Priority is determined by push order (last push wins)
|
|
598
|
+
- Cleanup happens automatically when component destroyed
|
|
599
|
+
- If non-owner tries to pop, operation is ignored (defensive)
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
<a id="centerpointoffset"></a>
|
|
604
|
+
|
|
605
|
+
## CenterPointOffset
|
|
606
|
+
|
|
607
|
+
**What it does:** Defines a logical center point for a GameObject that's separate from the transform pivot, scaled by the object's local scale.
|
|
608
|
+
|
|
609
|
+
**Problem it solves:** Sprites with off-center pivots (for animation reasons) need a separate "logical center" for gameplay (rotation point, targeting reticle, etc.). This provides that without changing the transform pivot.
|
|
610
|
+
|
|
611
|
+
### When to Use
|
|
612
|
+
|
|
613
|
+
✅ **Use for:**
|
|
614
|
+
|
|
615
|
+
- Sprites with off-center pivots that need gameplay center
|
|
616
|
+
- Rotation pivots different from visual pivot
|
|
617
|
+
- Targeting reticles
|
|
618
|
+
- AI targeting points
|
|
619
|
+
- Center-of-mass definitions
|
|
620
|
+
|
|
621
|
+
❌ **Don't use for:**
|
|
622
|
+
|
|
623
|
+
- Centered sprites (just use transform.position)
|
|
624
|
+
- Complex multi-point definitions
|
|
625
|
+
- Physics center of mass (use Rigidbody2D.centerOfMass)
|
|
626
|
+
|
|
627
|
+
### How to Use
|
|
628
|
+
|
|
629
|
+
```csharp
|
|
630
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
631
|
+
|
|
632
|
+
CenterPointOffset centerDef = gameObject.AddComponent<CenterPointOffset>();
|
|
633
|
+
centerDef.offset = new Vector2(0, 0.5f); // Center is 0.5 units above transform
|
|
634
|
+
centerDef.spriteUsesOffset = true; // Flag for sprite-specific logic
|
|
635
|
+
|
|
636
|
+
// Get world-space center point
|
|
637
|
+
Vector2 centerInWorld = centerDef.CenterPoint;
|
|
638
|
+
|
|
639
|
+
// Use for targeting
|
|
640
|
+
targetingSystem.AimAt(centerDef.CenterPoint);
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Offset Scaling
|
|
644
|
+
|
|
645
|
+
Offset is multiplied by `transform.localScale`:
|
|
646
|
+
|
|
647
|
+
```text
|
|
648
|
+
transform.position = (0, 0)
|
|
649
|
+
offset = (1, 0)
|
|
650
|
+
transform.localScale = (2, 2, 2)
|
|
651
|
+
|
|
652
|
+
CenterPoint = (0, 0) + (1, 0) * (2, 2) = (2, 0)
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
This ensures the center point scales with the object.
|
|
656
|
+
|
|
657
|
+
### Sprite Flag
|
|
658
|
+
|
|
659
|
+
`spriteUsesOffset` is a boolean flag you can check in other systems:
|
|
660
|
+
|
|
661
|
+
```csharp
|
|
662
|
+
if (center.spriteUsesOffset)
|
|
663
|
+
{
|
|
664
|
+
// Apply sprite-specific logic
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
<a id="animatorenumstatemachine"></a>
|
|
671
|
+
|
|
672
|
+
## AnimatorEnumStateMachine
|
|
673
|
+
|
|
674
|
+
**What it does:** Type-safe, enum-based Animator state control. Maps enum values to Animator boolean parameters for exclusive state control.
|
|
675
|
+
|
|
676
|
+
**Problem it solves:** Setting Animator bools with magic strings (`animator.SetBool("IsJumping", true)`) is error-prone and hard to refactor. This provides compile-time safety and automatic cleanup of previous states.
|
|
677
|
+
|
|
678
|
+
### When to Use
|
|
679
|
+
|
|
680
|
+
✅ **Use for:**
|
|
681
|
+
|
|
682
|
+
- Complex state machines (player states, enemy AI)
|
|
683
|
+
- Type-safe animation control
|
|
684
|
+
- State pattern implementations
|
|
685
|
+
- Refactor-friendly animation code
|
|
686
|
+
|
|
687
|
+
❌ **Don't use for:**
|
|
688
|
+
|
|
689
|
+
- Simple trigger-based animations (use animator.SetTrigger)
|
|
690
|
+
- Float/int parameters (only supports bools)
|
|
691
|
+
- Blend trees (use animator.SetFloat)
|
|
692
|
+
|
|
693
|
+
### How to Use
|
|
694
|
+
|
|
695
|
+
**1. Define enum matching your Animator parameters:**
|
|
696
|
+
|
|
697
|
+
```csharp
|
|
698
|
+
public enum PlayerState
|
|
699
|
+
{
|
|
700
|
+
Idle, // Maps to Animator bool "Idle"
|
|
701
|
+
Running, // Maps to Animator bool "Running"
|
|
702
|
+
Jumping, // Maps to Animator bool "Jumping"
|
|
703
|
+
Falling // Maps to Animator bool "Falling"
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
**2. Create state machine:**
|
|
708
|
+
|
|
709
|
+
```csharp
|
|
710
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
711
|
+
|
|
712
|
+
Animator animator = GetComponent<Animator>();
|
|
713
|
+
AnimatorEnumStateMachine<PlayerState> stateMachine;
|
|
714
|
+
|
|
715
|
+
void Awake()
|
|
716
|
+
{
|
|
717
|
+
stateMachine = new AnimatorEnumStateMachine<PlayerState>(animator, PlayerState.Idle);
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**3. Set state:**
|
|
722
|
+
|
|
723
|
+
```csharp
|
|
724
|
+
void Jump()
|
|
725
|
+
{
|
|
726
|
+
stateMachine.Value = PlayerState.Jumping;
|
|
727
|
+
// Automatically sets Animator bools:
|
|
728
|
+
// Idle = false
|
|
729
|
+
// Running = false
|
|
730
|
+
// Jumping = true
|
|
731
|
+
// Falling = false
|
|
732
|
+
}
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Automatic State Management
|
|
736
|
+
|
|
737
|
+
Setting `stateMachine.Value` automatically:
|
|
738
|
+
|
|
739
|
+
1. Sets ALL enum-named bools to `false`
|
|
740
|
+
2. Sets ONLY the matching bool to `true`
|
|
741
|
+
|
|
742
|
+
This ensures exclusive state control (only one state active).
|
|
743
|
+
|
|
744
|
+
### Animator Setup
|
|
745
|
+
|
|
746
|
+
Your Animator needs bool parameters matching enum names:
|
|
747
|
+
|
|
748
|
+
```text
|
|
749
|
+
Animator parameters:
|
|
750
|
+
- Idle (bool)
|
|
751
|
+
- Running (bool)
|
|
752
|
+
- Jumping (bool)
|
|
753
|
+
- Falling (bool)
|
|
754
|
+
|
|
755
|
+
Transitions:
|
|
756
|
+
- Any State → Idle: Idle == true
|
|
757
|
+
- Any State → Running: Running == true
|
|
758
|
+
- Any State → Jumping: Jumping == true
|
|
759
|
+
- Any State → Falling: Falling == true
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Serialization
|
|
763
|
+
|
|
764
|
+
`AnimatorEnumStateMachine<T>` is serializable for debugging in Inspector.
|
|
765
|
+
|
|
766
|
+
---
|
|
767
|
+
|
|
768
|
+
<a id="coroutinehandler"></a>
|
|
769
|
+
|
|
770
|
+
## CoroutineHandler
|
|
771
|
+
|
|
772
|
+
**What it does:** Singleton MonoBehaviour that provides a global coroutine host for non-MonoBehaviour classes.
|
|
773
|
+
|
|
774
|
+
**Problem it solves:** Coroutines require a MonoBehaviour to start. Static classes, plain C# objects, and ScriptableObjects can't start coroutines directly.
|
|
775
|
+
|
|
776
|
+
### When to Use
|
|
777
|
+
|
|
778
|
+
✅ **Use for:**
|
|
779
|
+
|
|
780
|
+
- Starting coroutines from static utility classes
|
|
781
|
+
- Coroutines in plain C# objects
|
|
782
|
+
- ScriptableObjects that need coroutines
|
|
783
|
+
- Global/scene-independent coroutines
|
|
784
|
+
|
|
785
|
+
❌ **Don't use for:**
|
|
786
|
+
|
|
787
|
+
- MonoBehaviours (just use StartCoroutine)
|
|
788
|
+
- Short-lived coroutines (might outlive object)
|
|
789
|
+
- Frame-perfect timing (singleton has overhead)
|
|
790
|
+
|
|
791
|
+
### How to Use
|
|
792
|
+
|
|
793
|
+
```csharp
|
|
794
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
795
|
+
|
|
796
|
+
// From anywhere
|
|
797
|
+
CoroutineHandler.Instance.StartCoroutine(MyCoroutine());
|
|
798
|
+
|
|
799
|
+
IEnumerator MyCoroutine()
|
|
800
|
+
{
|
|
801
|
+
yield return new WaitForSeconds(1f);
|
|
802
|
+
Debug.Log("Done!");
|
|
803
|
+
}
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Lifetime
|
|
807
|
+
|
|
808
|
+
CoroutineHandler persists across scene loads (`DontDestroyOnLoad`), so coroutines survive scene transitions.
|
|
809
|
+
|
|
810
|
+
### Stopping Coroutines
|
|
811
|
+
|
|
812
|
+
```csharp
|
|
813
|
+
Coroutine routine = CoroutineHandler.Instance.StartCoroutine(MyCoroutine());
|
|
814
|
+
// ... later
|
|
815
|
+
CoroutineHandler.Instance.StopCoroutine(routine);
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
<a id="starttracker"></a>
|
|
821
|
+
|
|
822
|
+
## StartTracker
|
|
823
|
+
|
|
824
|
+
**What it does:** Simple component that tracks whether `MonoBehaviour.Start()` has been called.
|
|
825
|
+
|
|
826
|
+
**Problem it solves:** Sometimes you need to know if initialization (Start) has completed, especially in editor or during complex initialization orders.
|
|
827
|
+
|
|
828
|
+
### When to Use
|
|
829
|
+
|
|
830
|
+
✅ **Use for:**
|
|
831
|
+
|
|
832
|
+
- Initialization order checking
|
|
833
|
+
- Conditional setup logic
|
|
834
|
+
- Editor tools validating scene state
|
|
835
|
+
- Testing initialization
|
|
836
|
+
|
|
837
|
+
❌ **Don't use for:**
|
|
838
|
+
|
|
839
|
+
- Production gameplay logic (architectural smell)
|
|
840
|
+
- Most scenarios (rethink if you need this)
|
|
841
|
+
|
|
842
|
+
### How to Use
|
|
843
|
+
|
|
844
|
+
```csharp
|
|
845
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
846
|
+
|
|
847
|
+
// Add to GameObject
|
|
848
|
+
StartTracker tracker = gameObject.AddComponent<StartTracker>();
|
|
849
|
+
|
|
850
|
+
// Later, check if Start has been called
|
|
851
|
+
if (tracker.Started)
|
|
852
|
+
{
|
|
853
|
+
// Initialization complete
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
<a id="matchcollidertosprite"></a>
|
|
860
|
+
|
|
861
|
+
## MatchColliderToSprite
|
|
862
|
+
|
|
863
|
+
Automatically syncs `PolygonCollider2D` shape to sprite's physics shape.
|
|
864
|
+
|
|
865
|
+
**See:** [Editor Tools Guide - MatchColliderToSprite](EDITOR_TOOLS_GUIDE.md#matchcollidertosprite-editor)
|
|
866
|
+
|
|
867
|
+
---
|
|
868
|
+
|
|
869
|
+
<a id="polygoncollider2doptimizer"></a>
|
|
870
|
+
|
|
871
|
+
## PolygonCollider2DOptimizer
|
|
872
|
+
|
|
873
|
+
Reduces PolygonCollider2D point count using Douglas-Peucker simplification.
|
|
874
|
+
|
|
875
|
+
**See:** [Editor Tools Guide - PolygonCollider2DOptimizer](EDITOR_TOOLS_GUIDE.md#polygoncollider2doptimizer-editor)
|
|
876
|
+
|
|
877
|
+
---
|
|
878
|
+
|
|
879
|
+
## Best Practices
|
|
880
|
+
|
|
881
|
+
### General
|
|
882
|
+
|
|
883
|
+
- **One utility per GameObject**: Don't stack unrelated utilities on the same GameObject
|
|
884
|
+
- **Configure in Awake/Start**: Set properties before first Update
|
|
885
|
+
- **Remove when done**: Disable/destroy utilities that are no longer needed
|
|
886
|
+
- **Test in builds**: Some utilities behave differently in editor vs builds (ChildSpawner)
|
|
887
|
+
|
|
888
|
+
### Performance
|
|
889
|
+
|
|
890
|
+
- **CircleLineRenderer**: Use sparingly, each instance updates line vertices
|
|
891
|
+
- **SpriteRendererSync**: Updates every LateUpdate, don't use for hundreds of sprites
|
|
892
|
+
- **MatchTransform**: Choose appropriate update mode (FixedUpdate for physics, LateUpdate for camera)
|
|
893
|
+
|
|
894
|
+
### Architecture
|
|
895
|
+
|
|
896
|
+
- **CollisionProxy**: Great for composition, but don't overuse events everywhere
|
|
897
|
+
- **SpriteRendererMetadata**: Document ownership in team code (who can push/pop)
|
|
898
|
+
- **AnimatorEnumStateMachine**: Keep enum names matching Animator parameters
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
## Related Documentation
|
|
903
|
+
|
|
904
|
+
- [Math & Extensions](MATH_AND_EXTENSIONS.md) - Extension methods used by utilities
|
|
905
|
+
- [Editor Tools Guide](EDITOR_TOOLS_GUIDE.md) - Editor components
|
|
906
|
+
- [Helpers Guide](HELPER_UTILITIES.md) - Non-component helper classes
|