com.wallstop-studios.unity-helpers 2.0.4 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/Docs/DATA_STRUCTURES.md +7 -7
  2. package/Docs/EFFECTS_SYSTEM.md +836 -8
  3. package/Docs/EFFECTS_SYSTEM_TUTORIAL.md +77 -18
  4. package/Docs/HULLS.md +2 -2
  5. package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
  6. package/Docs/ILIST_SORTING_PERFORMANCE.md.meta +7 -0
  7. package/Docs/INDEX.md +10 -1
  8. package/Docs/Images/random_generators.svg +7 -7
  9. package/Docs/RANDOM_PERFORMANCE.md +18 -15
  10. package/Docs/REFLECTION_HELPERS.md +1 -1
  11. package/Docs/RELATIONAL_COMPONENTS.md +51 -6
  12. package/Docs/SERIALIZATION.md +1 -1
  13. package/Docs/SINGLETONS.md +2 -2
  14. package/Docs/SPATIAL_TREES_2D_GUIDE.md +3 -3
  15. package/Docs/SPATIAL_TREES_3D_GUIDE.md +3 -3
  16. package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
  17. package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
  18. package/Docs/SPATIAL_TREE_SEMANTICS.md +7 -7
  19. package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
  20. package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +131 -41
  21. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +175 -18
  22. package/README.md +42 -18
  23. package/Runtime/Core/Extension/IListExtensions.cs +720 -12
  24. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
  25. package/Runtime/Core/Random/AbstractRandom.cs +52 -5
  26. package/Runtime/Core/Random/DotNetRandom.cs +3 -3
  27. package/Runtime/Core/Random/FlurryBurstRandom.cs +285 -0
  28. package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
  29. package/Runtime/Core/Random/IllusionFlow.cs +3 -3
  30. package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
  31. package/Runtime/Core/Random/PcgRandom.cs +6 -6
  32. package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
  33. package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
  34. package/Runtime/Core/Random/RomuDuo.cs +3 -3
  35. package/Runtime/Core/Random/SplitMix64.cs +3 -3
  36. package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
  37. package/Runtime/Core/Random/StormDropRandom.cs +271 -0
  38. package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
  39. package/Runtime/Core/Random/UnityRandom.cs +3 -3
  40. package/Runtime/Core/Random/WyRandom.cs +6 -4
  41. package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
  42. package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
  43. package/Runtime/Tags/Attribute.cs +144 -24
  44. package/Runtime/Tags/AttributeEffect.cs +119 -16
  45. package/Runtime/Tags/AttributeMetadataCache.cs +312 -3
  46. package/Runtime/Tags/AttributeModification.cs +59 -29
  47. package/Runtime/Tags/AttributesComponent.cs +20 -0
  48. package/Runtime/Tags/EffectBehavior.cs +171 -0
  49. package/Runtime/Tags/EffectBehavior.cs.meta +4 -0
  50. package/Runtime/Tags/EffectHandle.cs +5 -0
  51. package/Runtime/Tags/EffectHandler.cs +385 -39
  52. package/Runtime/Tags/EffectStackKey.cs +79 -0
  53. package/Runtime/Tags/EffectStackKey.cs.meta +4 -0
  54. package/Runtime/Tags/PeriodicEffectDefinition.cs +102 -0
  55. package/Runtime/Tags/PeriodicEffectDefinition.cs.meta +4 -0
  56. package/Runtime/Tags/PeriodicEffectRuntimeState.cs +40 -0
  57. package/Runtime/Tags/PeriodicEffectRuntimeState.cs.meta +4 -0
  58. package/Samples~/DI - Zenject/README.md +0 -2
  59. package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs +285 -0
  60. package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs.meta +11 -0
  61. package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +2 -2
  62. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
  63. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
  64. package/{node_modules.meta → Tests/Editor/Tags.meta} +1 -1
  65. package/Tests/Editor/Utils/ScriptableObjectSingletonTests.cs +41 -0
  66. package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
  67. package/Tests/Runtime/Helper/ObjectsTests.cs +3 -3
  68. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
  69. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
  70. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
  71. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
  72. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
  73. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
  74. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
  75. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
  76. package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
  77. package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
  78. package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
  79. package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
  80. package/Tests/Runtime/Serialization/JsonSerializationTest.cs +4 -3
  81. package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
  82. package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
  83. package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
  84. package/Tests/Runtime/Tags/AttributeEffectTests.cs +135 -0
  85. package/Tests/Runtime/Tags/AttributeEffectTests.cs.meta +3 -0
  86. package/Tests/Runtime/Tags/AttributeModificationTests.cs +137 -0
  87. package/Tests/Runtime/Tags/AttributeTests.cs +192 -0
  88. package/Tests/Runtime/Tags/AttributeTests.cs.meta +3 -0
  89. package/Tests/Runtime/Tags/EffectBehaviorTests.cs +184 -0
  90. package/Tests/Runtime/Tags/EffectBehaviorTests.cs.meta +3 -0
  91. package/Tests/Runtime/Tags/EffectHandlerTests.cs +618 -0
  92. package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs +89 -0
  93. package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs.meta +4 -0
  94. package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +92 -0
  95. package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs.meta +3 -0
  96. package/package.json +1 -1
  97. package/scripts/lint-doc-links.ps1 +156 -11
  98. package/Tests/Runtime/Tags/AttributeDataTests.cs +0 -312
  99. /package/Tests/Runtime/Tags/{AttributeDataTests.cs.meta → AttributeModificationTests.cs.meta} +0 -0
@@ -61,8 +61,9 @@ public class PlayerStats : AttributesComponent
61
61
  {
62
62
  // Define attributes that effects can modify
63
63
  public Attribute Speed = 5f;
64
- public Attribute Health = 100f;
65
- public Attribute Damage = 10f;
64
+ public Attribute MaxHealth = 100f;
65
+ public Attribute AttackDamage = 10f;
66
+ public Attribute Defense = 5f;
66
67
 
67
68
  void Start()
68
69
  {
@@ -80,6 +81,15 @@ public class PlayerStats : AttributesComponent
80
81
  - Calculates final value automatically (Add → Multiply → Override)
81
82
  - Raises events when value changes
82
83
 
84
+ **⚠️ Important: Use Attributes for "max" or "rate" values, NOT "current" depleting values!**
85
+
86
+ - ✅ **MaxHealth** - modified by buffs (good)
87
+ - ❌ **CurrentHealth** - modified by damage/healing from many systems (bad - causes state conflicts)
88
+ - ✅ **AttackDamage** - modified by strength buffs (good)
89
+ - ✅ **Speed** - modified by haste/slow effects (good)
90
+
91
+ If a value is frequently modified by systems outside the effects system (like health being reduced by damage), use a regular field instead. See the main documentation for details.
92
+
83
93
  ---
84
94
 
85
95
  ## Step 2: Add Stats to Your Player (30 seconds)
@@ -88,8 +98,9 @@ public class PlayerStats : AttributesComponent
88
98
  2. Add Component → `PlayerStats`
89
99
  3. Set values in Inspector:
90
100
  - Speed: `5`
91
- - Health: `100`
92
- - Damage: `10`
101
+ - MaxHealth: `100`
102
+ - AttackDamage: `10`
103
+ - Defense: `5`
93
104
 
94
105
  That's it! Your player now has modifiable attributes.
95
106
 
@@ -282,8 +293,8 @@ One effect can modify multiple attributes:
282
293
  **Create "Berserker Rage" effect:**
283
294
 
284
295
  - Modification 1: Speed × 1.3
285
- - Modification 2: Damage × 2.0
286
- - Modification 3: Health × 0.8 (trade-off!)
296
+ - Modification 2: AttackDamage × 2.0
297
+ - Modification 3: Defense × 0.5 (trade-off - more damage but less defense!)
287
298
  - Duration: 10 seconds
288
299
  - Tags: `"Berserker"`, `"Buff"`
289
300
 
@@ -312,23 +323,63 @@ if (handle.HasValue)
312
323
 
313
324
  ```csharp
314
325
  // Create "Poison" effect:
315
- // - Modification: Health + (-2) per second
326
+ // - periodicEffects: interval = 1s, maxTicks = 10, modifications = []
327
+ // - behaviors: PoisonDamageBehavior (below)
316
328
  // - Duration: 10 seconds
317
- // - Tags: "Poison", "DoT", "Debuff"
329
+ // - Tags: "Poisoned", "DoT", "Debuff"
330
+
331
+ void ApplyPoison(GameObject target)
332
+ {
333
+ target.ApplyEffect(poisonEffect);
334
+ }
335
+
336
+ [CreateAssetMenu(menuName = "Combat/Effects/Poison Damage")]
337
+ public sealed class PoisonDamageBehavior : EffectBehavior
338
+ {
339
+ [SerializeField]
340
+ private float damagePerTick = 2f;
341
+
342
+ public override void OnPeriodicTick(
343
+ EffectBehaviorContext context,
344
+ PeriodicEffectTickContext tickContext
345
+ )
346
+ {
347
+ if (!context.Target.TryGetComponent(out PlayerHealth health))
348
+ {
349
+ return;
350
+ }
318
351
 
319
- // Apply to enemy
320
- enemy.ApplyEffect(poisonEffect);
352
+ health.ApplyDamage(damagePerTick);
353
+ }
354
+ }
321
355
 
322
- // In PlayerStats, handle negative health:
323
- void Update()
356
+ public sealed class PlayerHealth : MonoBehaviour
324
357
  {
325
- if (Health.Value <= 0)
358
+ [SerializeField]
359
+ private float currentHealth = 100f;
360
+
361
+ public float CurrentHealth => currentHealth;
362
+
363
+ public void ApplyDamage(float amount)
326
364
  {
327
- Die();
365
+ currentHealth -= amount;
366
+
367
+ if (currentHealth <= 0f)
368
+ {
369
+ currentHealth = 0f;
370
+ Die();
371
+ }
372
+ }
373
+
374
+ private void Die()
375
+ {
376
+ // Handle player death
328
377
  }
329
378
  }
330
379
  ```
331
380
 
381
+ This keeps `CurrentHealth` as a regular gameplay field while the effect system triggers damage through behaviours.
382
+
332
383
  ### Cooldown Reduction
333
384
 
334
385
  ```csharp
@@ -376,6 +427,13 @@ void TryApplyBuff(AttributeEffect effect)
376
427
 
377
428
  ## Troubleshooting
378
429
 
430
+ ### "Should I use CurrentHealth as an Attribute?"
431
+
432
+ - **No!** Use `MaxHealth` as an Attribute (modified by buffs), but keep `CurrentHealth` as a regular field (modified by damage/healing)
433
+ - **Why:** CurrentHealth is modified by many systems (combat, regeneration, etc.). Using it as an Attribute causes state conflicts when effects and other systems both try to modify it
434
+ - **Pattern:** Attribute for max/cap, regular field for current/depleting value
435
+ - **See:** "Understanding Attributes: What to Model and What to Avoid" in main documentation
436
+
379
437
  ### "Attribute 'Speed' not found"
380
438
 
381
439
  - **Cause:** Attribute name in effect doesn't match field name in AttributesComponent
@@ -409,11 +467,12 @@ You now have a complete buff/debuff system! Here are some ideas to expand:
409
467
 
410
468
  ### Create More Effects
411
469
 
412
- - **Shield:** Health × 2.0, visual shield sprite
470
+ - **Shield:** MaxHealth × 1.5, visual shield sprite
413
471
  - **Slow:** Speed × 0.5, "Slowed" tag
414
- - **Critical:** Damage × 1.5, "Critical" tag
415
- - **Invisibility:** Just tags ("Invisible"), no stat changes
416
- - **Burn:** Health + (-5) per second, fire particles
472
+ - **Critical Strike:** AttackDamage × 2.0, "CriticalHit" tag, brief flash effect
473
+ - **Invisibility:** Just tags ("Invisible"), no stat changes, transparency effect
474
+ - **Armor Buff:** Defense + 10, metallic sheen cosmetic
475
+ - **Strength Potion:** AttackDamage × 1.5, red particle aura
417
476
 
418
477
  ### Build Systems Around Tags
419
478
 
package/Docs/HULLS.md CHANGED
@@ -18,7 +18,7 @@ This guide explains convex and concave hulls, when to use each, and how they dif
18
18
 
19
19
  Illustration:
20
20
 
21
- ![Convex Hull](Docs/Images/convex_hull.svg)
21
+ ![Convex Hull](Images/convex_hull.svg)
22
22
 
23
23
  ## Concave Hull
24
24
 
@@ -31,7 +31,7 @@ Illustration:
31
31
 
32
32
  Illustration:
33
33
 
34
- ![Concave Hull](Docs/Images/concave_hull.svg)
34
+ ![Concave Hull](Images/concave_hull.svg)
35
35
 
36
36
  ## Choosing Between Them
37
37
 
@@ -0,0 +1,92 @@
1
+ # IList Sorting Performance Benchmarks
2
+
3
+ Unity Helpers ships several custom sorting algorithms for `IList<T>` that cover different trade-offs between adaptability, allocation patterns, and stability. This page gathers context and benchmark snapshots so you can choose the right algorithm for your workload and compare results across operating systems.
4
+
5
+ ## Algorithm Cheatsheet
6
+
7
+ | Algorithm | Stable? | Best For | Reference |
8
+ | --------------------------- | ------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
9
+ | Ghost Sort | No | Mixed workloads that benefit from adaptive gap sorting and few allocations | Upstream project by Will Stafford Parsons (public repository currently offline) |
10
+ | Meteor Sort | No | Almost-sorted data where gap shrinking beats plain insertion sort | [meteorsort by Wiley Looper](https://github.com/wileylooper/meteorsort) |
11
+ | Pattern-Defeating QuickSort | No | General-purpose quicksort with protections against worst-case inputs | [pdqsort by Orson Peters](https://github.com/orlp/pdqsort) |
12
+ | Grail Sort | Yes | Large datasets where stability + low allocations matter | [GrailSort](https://github.com/Mrrl/GrailSort) |
13
+ | Power Sort | Yes | Partially ordered data that benefits from adaptive run detection | [PowerSort (Munro & Wild)](https://arxiv.org/abs/1805.04154) |
14
+ | Insertion Sort | Yes | Tiny or nearly sorted collections where O(n²) is acceptable | [Wikipedia - Insertion sort](https://en.wikipedia.org/wiki/Insertion_sort) |
15
+
16
+ > **What does “stable” mean?** Stable sorting algorithms preserve the relative order of elements that compare as equal. This matters when items carry secondary keys (e.g., sorting people by last name but keeping first-name order deterministic). Unstable algorithms can reshuffle equal entries, which is usually fine for numeric keys but can break deterministic pipelines.
17
+ >
18
+ > **Heads up:** The original Ghost Sort repository was formerly hosted on GitHub under `wstaffordp/ghostsort`, but it currently returns 404. The Unity Helpers implementation remains based on that source; we will relink if/when an official mirror returns.
19
+
20
+ ## Dataset Scenarios
21
+
22
+ - **Sorted** – ascending integers, verifying best-case behavior.
23
+ - **Nearly Sorted (2% swaps)** – deterministic neighbor swaps introduce light disorder to expose adaptive optimizations.
24
+ - **Shuffled (deterministic)** – Fisher–Yates shuffle using a fixed seed for reproducibility across runs and machines.
25
+
26
+ Each benchmark sorts a fresh copy of the dataset once and reports wall-clock duration. Insertion sort is skipped for lists larger than 10,000 elements because O(n²) quickly becomes impractical; the table shows `n/a` for those entries.
27
+
28
+ Run the `IListSortingPerformanceTests.Benchmark` test inside Unity’s Test Runner to refresh the tables below. Results automatically land in the section that matches the current operating system.
29
+
30
+ ## Windows (Editor/Player)
31
+
32
+ <!-- ILIST_SORT_WINDOWS_START -->
33
+
34
+ _Last updated 2025-10-26 18:00 UTC on Windows 11 (10.0.26200) 64bit_
35
+
36
+ Times are single-pass measurements in milliseconds (lower is better). `n/a` indicates the algorithm was skipped for the dataset size.
37
+
38
+ ### Sorted
39
+
40
+ | List Size | Ghost | Meteor | Pattern-Defeating QuickSort | Grail | Power | Insertion |
41
+ | --------- | -------- | -------- | --------------------------- | -------- | -------- | --------- |
42
+ | 100 | 0.373 ms | 0.072 ms | 0.422 ms | 2.04 ms | 0.907 ms | 0.071 ms |
43
+ | 1,000 | 0.021 ms | 0.025 ms | 0.007 ms | 0.040 ms | 0.005 ms | 0.005 ms |
44
+ | 10,000 | 0.288 ms | 0.368 ms | 0.067 ms | 0.555 ms | 0.041 ms | 0.054 ms |
45
+ | 100,000 | 3.35 ms | 4.81 ms | 0.703 ms | 10.7 ms | 0.424 ms | n/a |
46
+ | 1,000,000 | 37.0 ms | 57.1 ms | 6.70 ms | 11.4 ms | 4.08 ms | n/a |
47
+
48
+ ### Nearly Sorted (2% swaps)
49
+
50
+ | List Size | Ghost | Meteor | Pattern-Defeating QuickSort | Grail | Power | Insertion |
51
+ | --------- | -------- | -------- | --------------------------- | -------- | -------- | --------- |
52
+ | 100 | 0.002 ms | 0.002 ms | 0.007 ms | 0.145 ms | 0.053 ms | 0.001 ms |
53
+ | 1,000 | 0.021 ms | 0.025 ms | 0.032 ms | 0.009 ms | 0.020 ms | 0.006 ms |
54
+ | 10,000 | 0.279 ms | 0.355 ms | 0.383 ms | 0.072 ms | 0.223 ms | 0.057 ms |
55
+ | 100,000 | 3.17 ms | 4.64 ms | 4.61 ms | 0.689 ms | 3.47 ms | n/a |
56
+ | 1,000,000 | 37.3 ms | 57.3 ms | 53.9 ms | 7.45 ms | 44.8 ms | n/a |
57
+
58
+ ### Shuffled (deterministic)
59
+
60
+ | List Size | Ghost | Meteor | Pattern-Defeating QuickSort | Grail | Power | Insertion |
61
+ | --------- | -------- | -------- | --------------------------- | -------- | -------- | --------- |
62
+ | 100 | 0.009 ms | 0.007 ms | 0.007 ms | 0.011 ms | 0.010 ms | 0.015 ms |
63
+ | 1,000 | 0.143 ms | 0.123 ms | 0.089 ms | 0.106 ms | 0.111 ms | 1.34 ms |
64
+ | 10,000 | 1.97 ms | 1.76 ms | 1.16 ms | 1.38 ms | 1.41 ms | 131 ms |
65
+ | 100,000 | 28.3 ms | 23.5 ms | 14.4 ms | 17.3 ms | 17.5 ms | n/a |
66
+ | 1,000,000 | 395 ms | 296 ms | 171 ms | 207 ms | 212 ms | n/a |
67
+
68
+ <!-- ILIST_SORT_WINDOWS_END -->
69
+
70
+ ## macOS
71
+
72
+ <!-- ILIST_SORT_MACOS_START -->
73
+
74
+ Pending — run the IList sorting benchmark suite on macOS to capture results.
75
+
76
+ <!-- ILIST_SORT_MACOS_END -->
77
+
78
+ ## Linux
79
+
80
+ <!-- ILIST_SORT_LINUX_START -->
81
+
82
+ Pending — run the IList sorting benchmark suite on Linux to capture results.
83
+
84
+ <!-- ILIST_SORT_LINUX_END -->
85
+
86
+ ## Other Platforms
87
+
88
+ <!-- ILIST_SORT_OTHER_START -->
89
+
90
+ Pending — run the IList sorting benchmark suite on the target platform to capture results.
91
+
92
+ <!-- ILIST_SORT_OTHER_END -->
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 472ebf17c174498daf3ef01bda2a8c6b
3
+ TextScriptImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
package/Docs/INDEX.md CHANGED
@@ -139,6 +139,9 @@ Alphabetical index of all Unity Helpers features with quick links to documentati
139
139
  **Fit Texture Size** - Auto-adjust texture max size to source dimensions
140
140
  → [Editor Tools Guide](EDITOR_TOOLS_GUIDE.md#fit-texture-size)
141
141
 
142
+ **FlurryBurstRandom** - Six-word ARX generator (FlurryBurst32 port)
143
+ → [README - Random Generators](../README.md#random-number-generators) | [Random Performance](RANDOM_PERFORMANCE.md)
144
+
142
145
  ---
143
146
 
144
147
  ## G
@@ -262,6 +265,9 @@ Alphabetical index of all Unity Helpers features with quick links to documentati
262
265
  **PcgRandom** - High-quality PCG random generator
263
266
  → [README - Random Generators](../README.md#random-number-generators) | [Random Performance](RANDOM_PERFORMANCE.md)
264
267
 
268
+ **PhotonSpinRandom** - SHISHUA-inspired bulk generator
269
+ → [README - Random Generators](../README.md#random-number-generators) | [Random Performance](RANDOM_PERFORMANCE.md)
270
+
265
271
  **Point-in-Polygon** - 2D/3D containment tests
266
272
  → [Math & Extensions](MATH_AND_EXTENSIONS.md#geometry)
267
273
 
@@ -306,7 +312,7 @@ Alphabetical index of all Unity Helpers features with quick links to documentati
306
312
  **Random Extensions** - Random vectors, colors, weighted selection, subset sampling
307
313
  → [Math & Extensions](MATH_AND_EXTENSIONS.md#random-generators)
308
314
 
309
- **Random Generators** - 12 high-performance PRNG implementations
315
+ **Random Generators** - 15 high-performance PRNG implementations
310
316
  → [README - Random Generators](../README.md#random-number-generators) | [Random Performance](RANDOM_PERFORMANCE.md)
311
317
 
312
318
  **Range<T>** - Inclusive/exclusive range helper
@@ -352,6 +358,9 @@ Alphabetical index of all Unity Helpers features with quick links to documentati
352
358
  **Singletons** - Runtime and ScriptableObject singleton patterns
353
359
  → [Singletons Guide](SINGLETONS.md) | [README](../README.md#singleton-utilities-odin-compatible)
354
360
 
361
+ **StormDropRandom** - Large-buffer ARX generator
362
+ → [README - Random Generators](../README.md#random-number-generators) | [Random Performance](RANDOM_PERFORMANCE.md)
363
+
355
364
  **Sparse Set** - O(1) membership with dense iteration
356
365
  → [Data Structures](DATA_STRUCTURES.md#sparse-set) | [README](../README.md#data-structures)
357
366
 
@@ -82,14 +82,14 @@
82
82
  <!-- pip patterns 1,2,5,6 -->
83
83
  <circle cx="392" cy="267" r="2"/>
84
84
  <circle cx="420" cy="262" r="2"/>
85
- <circle cx="424" cy="276" r="2"/>
85
+ <circle cx="424" cy="272" r="2"/>
86
86
  <circle cx="450" cy="260" r="2"/>
87
- <circle cx="452" cy="268" r="2"/>
88
- <circle cx="458" cy="276" r="2"/>
89
- <circle cx="480" cy="260" r="2"/>
90
- <circle cx="490" cy="260" r="2"/>
91
- <circle cx="480" cy="276" r="2"/>
92
- <circle cx="490" cy="276" r="2"/>
87
+ <circle cx="453" cy="266" r="2"/>
88
+ <circle cx="456" cy="272" r="2"/>
89
+ <circle cx="478" cy="262" r="2"/>
90
+ <circle cx="488" cy="262" r="2"/>
91
+ <circle cx="478" cy="272" r="2"/>
92
+ <circle cx="488" cy="272" r="2"/>
93
93
  </g>
94
94
  <g font-family="Verdana,Arial,sans-serif" font-size="10" fill="#9fb3c8">
95
95
  <text x="390" y="315">discrete outcomes</text>
@@ -71,7 +71,7 @@ Threading
71
71
 
72
72
  Visual
73
73
 
74
- ![Random Generators](Docs/Images/random_generators.svg)
74
+ ![Random Generators](Images/random_generators.svg)
75
75
 
76
76
  This document contains performance benchmarks for the various random number generators included in Unity Helpers.
77
77
 
@@ -81,18 +81,21 @@ This document contains performance benchmarks for the various random number gene
81
81
 
82
82
  | Random | NextBool | Next | NextUInt | NextFloat | NextDouble | NextUint - Range | NextInt - Range |
83
83
  | --------------------------- | ----------- | ----------- | ------------- | ----------- | ----------- | ---------------- | --------------- |
84
- | DotNetRandom | 550,600,000 | 53,100,000 | 57,400,000 | 45,600,000 | 26,900,000 | 53,700,000 | 53,900,000 |
85
- | LinearCongruentialGenerator | 814,800,000 | 538,900,000 | 1,335,100,000 | 184,700,000 | 296,500,000 | 591,500,000 | 508,400,000 |
86
- | IllusionFlow | 800,200,000 | 489,500,000 | 892,600,000 | 167,600,000 | 268,200,000 | 444,600,000 | 396,100,000 |
87
- | PcgRandom | 796,400,000 | 537,900,000 | 889,500,000 | 184,300,000 | 291,400,000 | 456,500,000 | 412,000,000 |
88
- | RomuDuo | 794,300,000 | 359,300,000 | 766,200,000 | 167,200,000 | 191,600,000 | 446,000,000 | 397,600,000 |
89
- | SplitMix64 | 801,100,000 | 537,400,000 | 972,300,000 | 183,800,000 | 296,600,000 | 487,500,000 | 446,600,000 |
90
- | SquirrelRandom | 747,700,000 | 383,300,000 | 413,800,000 | 172,300,000 | 204,800,000 | 330,200,000 | 314,200,000 |
91
- | SystemRandom | 146,800,000 | 148,300,000 | 65,700,000 | 132,500,000 | 139,500,000 | 59,800,000 | 61,300,000 |
92
- | UnityRandom | 647,700,000 | 77,800,000 | 87,800,000 | 62,100,000 | 39,500,000 | 81,500,000 | 82,400,000 |
93
- | WyRandom | 750,700,000 | 382,900,000 | 447,500,000 | 166,800,000 | 191,700,000 | 296,800,000 | 281,100,000 |
94
- | XorShiftRandom | 792,900,000 | 536,400,000 | 606,000,000 | 184,100,000 | 287,300,000 | 442,800,000 | 391,200,000 |
95
- | XoroShiroRandom | 789,200,000 | 359,300,000 | 715,100,000 | 167,300,000 | 192,500,000 | 428,900,000 | 383,500,000 |
84
+ | DotNetRandom | 535,000,000 | 54,400,000 | 56,700,000 | 45,200,000 | 28,200,000 | 52,200,000 | 51,800,000 |
85
+ | LinearCongruentialGenerator | 798,300,000 | 823,200,000 | 1,329,100,000 | 179,900,000 | 402,000,000 | 577,800,000 | 493,300,000 |
86
+ | IllusionFlow | 778,000,000 | 662,100,000 | 895,100,000 | 178,000,000 | 331,100,000 | 444,000,000 | 384,900,000 |
87
+ | PcgRandom | 762,500,000 | 668,400,000 | 892,700,000 | 179,700,000 | 345,200,000 | 450,000,000 | 400,200,000 |
88
+ | RomuDuo | 758,600,000 | 579,300,000 | 767,300,000 | 167,200,000 | 255,900,000 | 446,500,000 | 397,400,000 |
89
+ | SplitMix64 | 800,900,000 | 670,400,000 | 943,700,000 | 179,000,000 | 346,600,000 | 473,300,000 | 432,800,000 |
90
+ | FlurryBurstRandom | 762,800,000 | 603,800,000 | 863,700,000 | 183,000,000 | 305,200,000 | 456,400,000 | 412,400,000 |
91
+ | SquirrelRandom | 759,700,000 | 393,600,000 | 413,500,000 | 172,300,000 | 187,800,000 | 329,600,000 | 307,100,000 |
92
+ | SystemRandom | 138,400,000 | 144,300,000 | 63,200,000 | 127,600,000 | 135,800,000 | 59,600,000 | 60,400,000 |
93
+ | UnityRandom | 655,300,000 | 85,000,000 | 87,800,000 | 62,200,000 | 41,500,000 | 81,500,000 | 82,400,000 |
94
+ | WyRandom | 758,600,000 | 390,600,000 | 457,100,000 | 166,800,000 | 191,100,000 | 293,600,000 | 274,700,000 |
95
+ | XorShiftRandom | 766,300,000 | 554,600,000 | 586,100,000 | 181,100,000 | 259,100,000 | 443,300,000 | 393,600,000 |
96
+ | XoroShiroRandom | 766,200,000 | 522,900,000 | 714,100,000 | 167,200,000 | 243,300,000 | 428,400,000 | 381,000,000 |
97
+ | PhotonSpinRandom | 677,900,000 | 232,100,000 | 258,000,000 | 116,900,000 | 114,800,000 | 209,700,000 | 201,100,000 |
98
+ | StormDropRandom | 758,100,000 | 538,100,000 | 698,600,000 | 184,100,000 | 271,800,000 | 406,300,000 | 365,900,000 |
96
99
 
97
100
  <!-- RANDOM_BENCHMARKS_END -->
98
101
 
@@ -110,8 +113,8 @@ This document contains performance benchmarks for the various random number gene
110
113
 
111
114
  Based on the benchmarks:
112
115
 
113
- - **For general use**: `IllusionFlow` (via `PRNG.Instance`) - Great balance of speed and quality
114
- - **For maximum speed**: `RomuDuo` or `LinearCongruentialGenerator` - Fastest overall
116
+ - **For general use**: `IllusionFlow` (via `PRNG.Instance`) or `PCG` - Great balance of speed and quality
117
+ - **For maximum speed**: `LinearCongruentialGenerator` - Fastest overall (but not recommended for statistical quality)
115
118
  - **For compatibility**: `DotNetRandom` - Uses .NET's built-in Random
116
119
  - **Avoid for performance**: `UnityRandom` - Significantly slower than alternatives
117
120
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  Visual
9
9
 
10
- ![Reflection Scan](Docs/Images/reflection_scan.svg)
10
+ ![Reflection Scan](Images/reflection_scan.svg)
11
11
 
12
12
  ReflectionHelpers is a set of utilities for high‑performance reflection in Unity projects. It generates and caches delegates to access fields and properties, call methods and constructors, and quickly create common collections — with safe fallbacks when dynamic IL isn’t available.
13
13
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Visual
4
4
 
5
- ![Relational Wiring](Docs/Images/relational_wiring.svg)
5
+ ![Relational Wiring](Images/relational_wiring.svg)
6
6
 
7
7
  Auto-wire components in your hierarchy without `GetComponent` boilerplate. These attributes make common relationships explicit, robust, and easy to maintain.
8
8
 
@@ -10,7 +10,14 @@ Auto-wire components in your hierarchy without `GetComponent` boilerplate. These
10
10
  - `ParentComponent` — up the transform hierarchy
11
11
  - `ChildComponent` — down the transform hierarchy (breadth-first)
12
12
 
13
- Each works with single fields, arrays, `List<T>`, and `HashSet<T>`, supports optional assignment, filters (tag/name), depth limits, max results, and interface/base-type resolution.
13
+ **Collection Type Support:** Each attribute works with:
14
+
15
+ - Single fields (e.g., `Transform`)
16
+ - Arrays (e.g., `Collider2D[]`)
17
+ - **Lists** (e.g., `List<Rigidbody2D>`)
18
+ - **HashSets** (e.g., `HashSet<Renderer>`)
19
+
20
+ All attributes support optional assignment, filters (tag/name), depth limits, max results, and interface/base-type resolution.
14
21
 
15
22
  Having issues? Jump to Troubleshooting: see [Troubleshooting](#troubleshooting).
16
23
 
@@ -197,7 +204,8 @@ Examples:
197
204
  [SiblingComponent] private Animator animator; // required by default
198
205
  [SiblingComponent(Optional = true)] private Rigidbody2D rb; // optional
199
206
  [SiblingComponent(TagFilter = "Visual", NameFilter = "Sprite")] private Component[] visuals;
200
- [SiblingComponent(MaxCount = 2)] private List<Collider2D> firstTwo;
207
+ [SiblingComponent(MaxCount = 2)] private List<Collider2D> firstTwo; // List<T> supported
208
+ [SiblingComponent] private HashSet<Renderer> allRenderers; // HashSet<T> supported
201
209
  ```
202
210
 
203
211
  ### ParentComponent
@@ -232,7 +240,10 @@ Examples:
232
240
  // First matching descendant with a tag
233
241
  [ChildComponent(OnlyDescendants = true, TagFilter = "Weapon")] private Collider2D weaponCollider;
234
242
 
235
- // Gather into a hash set (unique results) and limit count
243
+ // Gather into a List (preserves insertion order)
244
+ [ChildComponent(OnlyDescendants = true)] private List<MeshRenderer> childRenderers;
245
+
246
+ // Gather into a HashSet (unique results, no duplicates) and limit count
236
247
  [ChildComponent(OnlyDescendants = true, MaxCount = 10)] private HashSet<Rigidbody2D> firstTenRigidbodies;
237
248
  ```
238
249
 
@@ -261,6 +272,39 @@ Examples:
261
272
  - `AllowInterfaces` (default: true)
262
273
  - If `true`, can assign by interface or base type; set `false` to restrict to concrete types
263
274
 
275
+ ### Choosing the Right Collection Type
276
+
277
+ **Use Arrays (`T[]`)** when:
278
+
279
+ - Collection size is fixed or rarely changes
280
+ - Need the smallest memory footprint
281
+ - Interoperating with APIs that require arrays
282
+
283
+ **Use Lists (`List<T>`)** when:
284
+
285
+ - Need insertion order preserved
286
+ - Plan to add/remove elements after assignment
287
+ - Want indexed access with `[]` operator
288
+ - Need compatibility with most LINQ operations
289
+
290
+ **Use HashSets (`HashSet<T>`)** when:
291
+
292
+ - Need guaranteed uniqueness (no duplicates)
293
+ - Performing frequent membership tests (`Contains()`)
294
+ - Order doesn't matter
295
+ - Want O(1) lookup performance
296
+
297
+ ```csharp
298
+ // Arrays: Fixed size, minimal overhead
299
+ [ChildComponent] private Collider2D[] colliders;
300
+
301
+ // Lists: Dynamic, ordered, index-based access
302
+ [ChildComponent] private List<Renderer> renderers;
303
+
304
+ // HashSets: Unique, fast lookups, unordered
305
+ [ChildComponent] private HashSet<AudioSource> audioSources;
306
+ ```
307
+
264
308
  ## Recipes
265
309
 
266
310
  - UI hierarchy references
@@ -564,7 +608,8 @@ Common pitfalls and how to avoid them
564
608
 
565
609
  **DI Integration Samples:**
566
610
 
567
- - [VContainer Integration](Samples~/DI%20-%20VContainer/README.md) - Complete VContainer setup guide
568
- - [Zenject Integration](Samples~/DI%20-%20Zenject/README.md) - Complete Zenject setup guide
611
+ - [VContainer Integration](../Samples~/DI%20-%20VContainer/README.md) - Complete VContainer setup guide
612
+ - [Zenject Integration](../Samples~/DI%20-%20Zenject/README.md) - Complete Zenject setup guide
613
+ - [Reflex Integration](../Samples~/DI%20-%20Reflex/README.md) - Complete Reflex setup guide
569
614
 
570
615
  **Need help?** [Open an issue](https://github.com/wallstop/unity-helpers/issues) | [Troubleshooting](#troubleshooting)
@@ -8,7 +8,7 @@
8
8
 
9
9
  Visuals
10
10
 
11
- ![Serialization Flow](Docs/Images/serialization_flow.svg)
11
+ ![Serialization Flow](Images/serialization_flow.svg)
12
12
 
13
13
  This package provides fast, compact serialization for save systems, configuration, and networking with a unified API.
14
14
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Visual
4
4
 
5
- ![Singletons Lifecycle](Docs/Images/singletons_lifecycle.svg)
5
+ ![Singletons Lifecycle](Images/singletons_lifecycle.svg)
6
6
 
7
7
  This package includes two lightweight, production‑ready singleton helpers that make global access patterns safe, consistent, and testable:
8
8
 
@@ -403,7 +403,7 @@ Use alternatives when one or more of these apply:
403
403
 
404
404
  Use this chart to pick an approach based on constraints:
405
405
 
406
- ![Data Distribution Strategy](Docs/Images/data_distribution_decision.svg)
406
+ ![Data Distribution Strategy](Images/data_distribution_decision.svg)
407
407
 
408
408
  <a id="troubleshooting"></a>
409
409
 
@@ -188,7 +188,7 @@ See [Buffering Pattern](../README.md#buffering-pattern) for the complete guide a
188
188
  - Pros: Simple structure; predictable performance; incremental updates straightforward.
189
189
  - Cons: Data hotspots deepen local trees; nearest neighbors slower than KDTree.
190
190
 
191
- Diagram: ![QuadTree2D](Docs/Images/quadtree_2d.svg)
191
+ Diagram: ![QuadTree2D](Images/quadtree_2d.svg)
192
192
 
193
193
  ### KDTree2D
194
194
 
@@ -197,7 +197,7 @@ Diagram: ![QuadTree2D](Docs/Images/quadtree_2d.svg)
197
197
  - Pros: Strong NN performance; balanced variant gives consistent query time.
198
198
  - Cons: Costly to maintain under heavy churn; unbalanced variant can degrade.
199
199
 
200
- Diagram: ![KDTree2D](Docs/Images/kdtree_2d.svg)
200
+ Diagram: ![KDTree2D](Images/kdtree_2d.svg)
201
201
 
202
202
  ### RTree2D
203
203
 
@@ -206,7 +206,7 @@ Diagram: ![KDTree2D](Docs/Images/kdtree_2d.svg)
206
206
  - Pros: Great for large bounds queries; matches bounds semantics.
207
207
  - Cons: Overlapping MBRs can increase node visits; not optimal for point NN.
208
208
 
209
- Diagram: ![RTree2D](Docs/Images/rtree_2d.svg)
209
+ Diagram: ![RTree2D](Images/rtree_2d.svg)
210
210
 
211
211
  ## Choosing a Structure
212
212
 
@@ -178,7 +178,7 @@ See [Buffering Pattern](../README.md#buffering-pattern) for the complete guide a
178
178
  - Pros: Good spatial locality; intuitive partitioning; balanced performance.
179
179
  - Cons: Nearest neighbors slower than KDTree on pure point data.
180
180
 
181
- ![Octree3D](Docs/Images/octree_3d.svg)
181
+ ![Octree3D](Images/octree_3d.svg)
182
182
 
183
183
  ### KDTree3D
184
184
 
@@ -187,7 +187,7 @@ See [Buffering Pattern](../README.md#buffering-pattern) for the complete guide a
187
187
  - Pros: Strong NN performance; balanced variant gives consistent query time.
188
188
  - Cons: Costly to maintain under heavy churn; unbalanced variant can degrade.
189
189
 
190
- ![KDTree3D](Docs/Images/kdtree_3d.svg)
190
+ ![KDTree3D](Images/kdtree_3d.svg)
191
191
 
192
192
  ### RTree3D
193
193
 
@@ -196,7 +196,7 @@ See [Buffering Pattern](../README.md#buffering-pattern) for the complete guide a
196
196
  - Pros: Great for large bounds queries; matches volumetric semantics.
197
197
  - Cons: Overlapping boxes can increase node visits; not optimal for point NN.
198
198
 
199
- ![RTree3D](Docs/Images/rtree_3d.svg)
199
+ ![RTree3D](Images/rtree_3d.svg)
200
200
 
201
201
  ## Choosing a Structure
202
202