Treasure chest of Unity developer tools
npm install com.wallstop-studios.unity-helpers
---
> ๐ค AI Assistance Disclosure:
>
> Recent versions of this project have utilized AI assistance for feature development, bug detection, performance optimization, and documentation.
>
> The original codebase was developed entirely by humans over several years.
---
Reduce time spent on boilerplate and focus more on features.
Unity Helpers provides production-ready utilities designed to improve development speed. Key performance highlights: 10-15x faster random generation than Unity.Random (see benchmarks), O(log n) spatial queries, and declarative inspector attributes to reduce custom editor code.
---
| Source | Install Method |
| ------------------------- | ---------------------------------------------------------------------------------------------- |
| OpenUPM (Recommended) | openupm add com.wallstop-studios.unity-helpers |
| Git URL | Package Manager โ Add from git URL โ https://github.com/wallstop/unity-helpers.git |
| NPM | Add scoped registry https://registry.npmjs.org โ search com.wallstop-studios.unity-helpers |
| Source | Import .unitypackage or clone repo |
๐ Full installation instructions with step-by-step guides for each method.
---
What makes this different:
- ๐จ Inspector tooling โ Grouping, buttons, conditional display, toggle grids. Covers common use cases similar to Odin Inspector; for advanced scenarios, see the feature comparison
- โก 10-15x faster random generation than Unity.Random in benchmarks (see performance docs for details)
- ๐ Reduced boilerplate component wiring with attributes
- ๐ฎ Designer-friendly effects system (buffs/debuffs as ScriptableObjects)
- ๐ณ O(log n) spatial queries instead of O(n) loops
- ๐ ๏ธ 20+ editor tools that automate sprite/animation workflows
- โ
8,000+ tests ensuring production quality
---
๐บ๏ธ Roadmap Snapshot โ See the Roadmap for prioritized details.
- Inspector tooling: inline nested editors, tabbed navigation, live instrumentation, disable-if/layer attributes
- Editor automation: Animation Creator and Sprite Sheet Animation Creator enhancements, timeline-ready Event Editor upgrades, and new automation dashboards
- Random/statistics: CI statistical harness, automated quality reports, scenario samplers, job-safe stream schedulers
- Spatial trees: graduate the 3D variants, add incremental updates, physics-shape parity, and streaming builders
- UI Toolkit: control pack (dockable panes, data grids), theming samples, and performance patterns
- Utilities: cross-system bridges plus new math/combinatorics and service-pattern helpers
- Performance: automated benchmarks, Burst/Jobs rewrites of hot paths, and allocation analyzers
- Attributes & tags: effect visualization tools, attribute graphs, and migration/versioning helpers
- Relational components: cached reflection, source generators, editor-time validation, and interface-based resolution
---
๐ New to Unity Helpers? Start here: Getting Started Guide
๐ Looking for something specific? Check the Feature Index
โ Need a definition? See the Glossary
---
Pick your starting point based on your biggest pain point:
| Your Problem | Your Solution | Time to Value |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------- | ------------- |
| ๐จ Writing custom editors | Inspector Tooling - Inspector tooling for common use cases | ~2 minutes |
| ๐ Writing GetComponent everywhere | Relational Components - Auto-wire with attributes | ~2 minutes |
| ๐ฎ Need buffs/debuffs system | Effects System - Designer-friendly ScriptableObjects | ~5 minutes |
| ๐ Slow spatial searches | Spatial Trees - O(log n) queries | ~5 minutes |
| ๐ฒ Random is too slow/limited | PRNG.Instance - 10-15x faster, extensive API | ~1 minute |
| ๐พ Need save/load system | Serialization - Unity types just work | ~10 minutes |
| ๐ ๏ธ Manual sprite workflows | Editor Tools - 20+ automation tools | ~3 minutes |
Not sure where to start? โ Getting Started Guide walks through the top 3 features in ~5 minutes.
---
These features address common categories of repetitive work. Pick one that solves your immediate pain:
Reduce custom PropertyDrawer and EditorGUI code with declarative attributes:
``csharp
// โ OLD WAY: 100+ lines of custom editor code
[CustomEditor(typeof(CharacterStats))]
public class CharacterStatsEditor : Editor {
// ... SerializedProperty declarations ...
// ... OnEnable setup ...
// ... OnInspectorGUI with EditorGUI.BeginFoldoutHeaderGroup ...
// ... Custom button rendering ...
// ... Conditional field display logic ...
}
// โ
NEW WAY: Declarative attributes, zero custom editors
public class CharacterStats : MonoBehaviour
{
[WGroup("combat", "Combat Stats", collapsible: true)]
public float maxHealth = 100f;
[WGroupEnd("combat")] // defense IS included, then group closes
public float defense = 10f;
[WGroup("abilities", "Abilities", collapsible: true, startCollapsed: true)]
[System.Flags] public enum Powers { None = 0, Fly = 1, Strength = 2, Speed = 4 }
[WEnumToggleButtons(showSelectAll: true, buttonsPerRow: 3)]
[WGroupEnd("abilities")] // currentPowers IS included, then group closes
public Powers currentPowers;
public enum WeaponType { Melee, Ranged, Magic }
public WeaponType weaponType;
[WShowIf(nameof(weaponType), WShowIfComparison.Equal, WeaponType.Ranged)]
public int ammoCapacity = 30;
[WButton("Heal to Full", groupName: "Debug")]
private void HealToFull() { maxHealth = 100f; }
}
`
Features:
- WGroup - Boxed sections with auto-inclusion, collapsible headers, and animations when enabled
- WButton - Method buttons with history, async support, cancellation
- WShowIf - Conditional visibility (9 comparison operators)
- WEnumToggleButtons - Flag enums as visual toggle grids
- SerializableDictionary, SerializableSet, WGuid, SerializableType - Collections Unity can't serialize
๐ Complete Inspector Guide | ๐ Odin Migration Guide
---
Reduce GetComponent boilerplate with declarative attributes. Replace 20+ lines with 3 attributes:
`csharp
// โ OLD WAY: 20+ lines per script
void Awake() {
sprite = GetComponent
if (sprite == null) Debug.LogError("Missing SpriteRenderer!");
rigidbody = GetComponentInParent
if (rigidbody == null) Debug.LogError("Missing Rigidbody2D!");
colliders = GetComponentsInChildren
// 15 more lines...
}
// โ
NEW WAY: 4 lines total
[SiblingComponent] private SpriteRenderer sprite;
[ParentComponent] private Rigidbody2D rigidbody;
[ChildComponent] private Collider2D[] colliders;
void Awake() => this.AssignRelationalComponents();
`
Bonus: Works with VContainer/Zenject/Reflex for automatic DI + relational wiring!
๐ Learn More | ๐ฏ DI โ VContainer | ๐ฏ DI โ Zenject | ๐ฏ DI โ Reflex
---
Designers create buffs/debuffs as ScriptableObjects. Minimal programmer involvement after initial setup:
`csharp
// Create once (ScriptableObject in editor):
// - HasteEffect: Speed ร 1.5, duration 5s, tag "Haste", particle effect
// Use everywhere (zero boilerplate):
player.ApplyEffect(hasteEffect); // Apply buff
if (player.HasTag("Stunned")) return; // Query state
player.RemoveEffects(player.GetHandlesWithTag("Haste")); // Batch removal
`
What you get:
- Automatic stacking & duration management
- Reference-counted tags for gameplay queries
- Cosmetic VFX/SFX that spawn/despawn automatically
- Designer-friendly iteration without code changes
Beyond buffs: Tags become a powerful capability system for AI decisions, permission gates, state management, and complex gameplay interactions (invulnerability, stealth, elemental systems).
๐ Full Guide | ๐ 5-Minute Tutorial
---
#### Handles Unity type serialization and helps prevent player data loss
JSON/Protobuf that understands Vector3, GameObject, Color - no custom converters needed:
`csharp
// Vector3, Color, GameObject references just work:
var saveData = new SaveData {
playerPosition = new Vector3(1, 2, 3),
playerColor = Color.cyan,
inventory = new List
};
// One line to save:
byte[] data = Serializer.JsonSerialize(saveData);
// Schema evolution = never break old saves:
[ProtoMember(1)] public int gold;
[ProtoMember(2)] public Vector3 position;
// Adding new field? Old saves still load!
[ProtoMember(3)] public int level; // Safe to add
`
Real-world impact: Ship updates without worrying about corrupting player saves.
---
Reduces GC pressure in allocation-heavy scenarios. Zero-allocation queries with automatic cleanup. Thread-safe pooling in one line:
`csharp
// Get pooled buffer - automatically returned on scope exit
void ProcessEnemies(QuadTree2D
using var lease = Buffers
// Use it for spatial query - zero allocations!
enemyTree.GetElementsInRange(playerPos, 10f, buffer);
foreach (Enemy enemy in buffer) {
enemy.TakeDamage(5f);
}
// Buffer automatically returned to pool here - no cleanup needed
}
`
Why this matters:
- Stable 60 FPS under load (no GC spikes)
- AI systems querying hundreds of neighbors per frame
- Particle systems with thousands of particles
- Works for List, HashSet, Stack, Queue, and Arrays
---
20+ tools that automate sprite cropping, animation creation, atlas generation, prefab validation:
Common workflows:
- Sprite Cropper: Add or remove transparent pixels from 500 sprites โ 1 click (was: 30 minutes in Photoshop)
- Animation Creator: Bulk-create clips from naming patterns (walk_0001.png) โ ~1 minute (was: ~20 minutes)
- Prefab Checker: Validate 200 prefabs for missing references โ 1 click (was: manual QA)
- Atlas Generator: Create sprite atlases from regex/labels โ automated (was: manual setup)
---
Common operations available as extension methods.
Unity Helpers includes 200+ extension methods that handle common repetitive patterns:
`csharp
// Color averaging (4 methods: LAB, HSV, Weighted, Dominant)
Color teamColor = sprite.GetAverageColor(ColorAveragingMethod.LAB); // Perceptually accurate
// Collider auto-fitting
polygonCollider.UpdateShapeToSprite(); // Instant sprite โ collider sync
// Smooth direction rotation (returns rotated direction vector)
Vector2 facing = Helpers.GetAngleWithSpeed(targetDirection, currentFacing, rotationSpeed);
// Safe destruction (works in editor AND runtime)
gameObject.SmartDestroy(); // No more #if UNITY_EDITOR everywhere
// Camera world bounds
Bounds visibleArea = Camera.main.OrthographicBounds(); // Perfect for culling/spawning
// Predictive targeting (intercept moving targets)
Vector2 aimPoint = target.PredictCurrentTarget(shooter.position, projectileSpeed, predictiveFiring: true, targetVelocity);
turret.transform.up = (aimPoint - (Vector2)shooter.position).normalized;
`
`csharp
// Positive modulo (no more negative results!)
int index = (-1).PositiveMod(array.Length); // 4, not -1
// Wrapped add for ring buffers
index = index.WrappedAdd(2, capacity); // Handles overflow correctly
// Approximate equality with tolerance
if (transform.position.x.Approximately(target.x, 0.01f)) { / close enough / }
// Polyline simplification (DouglasโPeucker)
List
`
`csharp
// Infinite iterator (no extra allocation)
foreach (var item in itemList.Infinite()) { / cycles forever / }
// Aggregate bounds from multiple renderers
Bounds? combined = renderers.Select(r => r.bounds).GetBounds();
// String similarity for fuzzy search
int distance = playerName.LevenshteinDistance("jon"); // "john" = 1, close match!
// Case conversions (6 styles: Pascal, Camel, Snake, Kebab, Title, Constant)
string apiKey = "user_name".ToPascalCase(); // "UserName"
`
Full list: Math & Extensions Guide | Reflection Helpers
---
These powerful utilities solve specific problems that waste hours if you implement them yourself:
| Feature | What It Does | Benefit |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
| Predictive Targeting | Perfect ballistics for turrets/missiles in one call | Simplifies implementation |
| Coroutine Jitter | Prevents 100 enemies polling on same frame | Reduces frame spikes |
| IL-Emitted Reflection | 10-100x faster than System.Reflection (varies by operation), IL2CPP safe | Improves serialization performance |
| SmartDestroy() | Editor/runtime safe destruction (no scene corruption) | Works across editor/runtime |
| Convex/Concave Hulls | Generate territory borders from point clouds | Avoids manual hull calculation |
| Logging Extensions | Rich tags, thread-aware logs, per-object toggles | Improves debugging |
---
Common Unity development patterns like GetComponent calls, spatial queries, and serialization often involve repetitive code. Unity Helpers provides utilities for these patterns.
Built for Real Projects:
- โ
Production-tested in shipped commercial games
- โ
8,000+ automated tests catch edge cases before you hit them
- โ
Zero external dependencies โ protobuf-net is bundled for binary serialization
- โ
IL2CPP/WebGL ready with optimized SINGLE_THREADED paths
- โ
MIT Licensed - use freely in commercial projects
Who This Is For:
- Indie devs who need professional tools without enterprise overhead
- Teams who value performance and want their junior devs to use battle-tested code
- Senior engineers who are tired of re-implementing the same utilities every project
---
Unity Helpers is available from multiple sources. Choose the one that best fits your workflow:
| Source | Best For | Auto-Updates |
| ------------------------------------ | ----------------------------------- | ------------ |
| OpenUPM | Most users, easy version management | โ
Yes |
| Git URL | Latest commits, CI/CD pipelines | โ
Yes |
| NPM Registry | Teams already using NPM | โ
Yes |
| Source | Offline, modifications needed | โ Manual |
OpenUPM is the recommended installation method for easy version management and updates.
#### Option A: Via Package Manager UI
1. Open Edit โ Project Settings โ Package Manager
2. Under Scoped Registries, click + to add a new registry:
- Name: OpenUPMhttps://package.openupm.com
- URL: com.wallstop-studios
- Scope(s): Unity Helpers
3. Click Save
4. Open Window โ Package Manager
5. Change the dropdown to My Registries
6. Find and install
#### Option B: Via OpenUPM CLI
If you have the OpenUPM CLI installed:
`bash`
openupm add com.wallstop-studios.unity-helpers
#### Option C: Manual manifest.json
Add to your Packages/manifest.json:
`json`
{
"scopedRegistries": [
{
"name": "OpenUPM",
"url": "https://package.openupm.com",
"scopes": ["com.wallstop-studios"]
}
],
"dependencies": {
"com.wallstop-studios.unity-helpers": "3.1.0"
}
}
Install directly from GitHub for the latest version:
1. Open Window โ Package Manager
2. Click + โ Add package from git URL...
3. Enter: https://github.com/wallstop/unity-helpers.git
OR add to your Packages/manifest.json:
`json`
{
"dependencies": {
"com.wallstop-studios.unity-helpers": "https://github.com/wallstop/unity-helpers.git"
}
}
> Tip: To lock to a specific version, append #3.1.0 to the URL.
1. Open Edit โ Project Settings โ Package Manager
2. Under Scoped Registries, click + to add a new registry:
- Name: NPMhttps://registry.npmjs.org
- URL: com.wallstop-studios
- Scope(s): com.wallstop-studios.unity-helpers
3. Click Save
4. Open Window โ Package Manager
5. Change the dropdown to My Registries
6. Find and install
#### Option A: Import Unity Package
1. Download the latest .unitypackage from GitHub Releases
2. In Unity, go to Assets โ Import Package โ Custom Package...
3. Select the downloaded .unitypackage file and import
#### Option B: Clone or Download Repository
1. Clone or download the repository
2. Copy the contents to your project's Assets/ or Packages/ folder
3. Unity will automatically import the package
---
| Unity Version | Built-In | URP | HDRP |
| ------------- | -------------------- | -------------------- | -------------------- |
| 2021 | Likely, but untested | Likely, but untested | Likely, but untested |
| 2022 | โ
Compatible | โ
Compatible | โ
Compatible |
| 2023 | โ
Compatible | โ
Compatible | โ
Compatible |
| Unity 6 | โ
Compatible | โ
Compatible | โ
Compatible |
Unity Helpers is fully multiplatform compatible including:
- โ
WebGL - Full support with optimized SINGLE_THREADED hot paths
- โ
IL2CPP - Tested and compatible with ahead-of-time compilation
- โ
Mobile (iOS, Android) - Production-ready with IL2CPP
- โ
Desktop (Windows, macOS, Linux) - Full threading support
- โ
Consoles - IL2CPP compatible
Requirements:
- .NET Standard 2.1 - Required for core library features
Unity Helpers includes a SINGLE_THREADED scripting define symbol for WebGL and other single-threaded environments. When enabled, the library automatically uses optimized code paths that eliminate threading overhead:
Optimized systems with SINGLE_THREADED:
- Buffers & Pooling - Uses Stack and Dictionary instead of ConcurrentBag and ConcurrentDictionaryThreadLocal
- Random Number Generation - Static instances instead of
- Reflection Caches - Non-concurrent dictionaries for faster lookups
- Thread Pools - SingleThreadedThreadPool disabled (not needed on WebGL)
How to enable:
Unity automatically defines UNITY_WEBGL for WebGL builds. To enable SINGLE_THREADED optimization:
1. Go to Project Settings > Player > Other Settings > Scripting Define Symbols
2. Add SINGLE_THREADED for WebGL platformcsc.rsp
3. Or use in your file: -define:SINGLE_THREADED
Performance impact: 10-20% faster hot path operations on single-threaded platforms by avoiding unnecessary synchronization overhead.
โ ๏ธ Important for IL2CPP builds (WebGL, Mobile, Consoles):
Some features in Unity Helpers use reflection internally (particularly Protobuf serialization and ReflectionHelpers). IL2CPP's managed code stripping may remove types/members that are only accessed via reflection, causing runtime errors.
Symptoms of stripping issues:
- NullReferenceException or TypeLoadException during deserialization
- Missing fields after Protobuf deserialization
- Reflection helpers failing to find types at runtime
#### Solution: Use link.xml to preserve required types
Create a link.xml file in your Assets folder to prevent stripping:
`xml
`
Best practices:
- โ
Always test IL2CPP builds - Development builds don't use stripping, so bugs only appear in release builds
- โ
Test on target platform - WebGL stripping behaves differently than iOS/Android
- โ
Use link.xml for all Protobuf types - Any type with [ProtoContract] should be preserved
- โ
Verify after every schema change - Adding new serialized types requires updating link.xml
- โ
Check logs for stripping warnings - Unity logs which types are stripped during build
When you don't need link.xml:
- JSON serialization (uses source-generated converters, not reflection)
- Spatial trees and data structures (no reflection used)
- Most helper methods (compiled ahead-of-time)
Related documentation:
- Unity Manual: Managed Code Stripping
- Protobuf-net and IL2CPP
- Serialization Guide: IL2CPP Warning
- Reflection Helpers: IL2CPP Warning
---
> ๐ก First time? Skip to section #2 (Relational Components) - it has the biggest immediate impact.
Already read the Top 5 Time-Savers? Jump directly to the Core Features reference below, or check out the comprehensive Getting Started Guide.
---
Unity Helpers includes 15 high-quality random number generators, all implementing a rich IRandom interface:
#### Available Generators
> The tables below are auto-generated by the performance benchmark. Run
> RandomPerformanceTests.Benchmark in the Unity Test Runner to refresh them.
_No benchmark data available yet. Run RandomPerformanceTests.Benchmark to populate these tables._
#### Rich API
All generators implement IRandom with extensive functionality:
`csharp
IRandom random = PRNG.Instance;
// Basic types
int i = random.Next(); // int in [0, int.MaxValue]
int range = random.Next(10, 20); // int in [10, 20)
uint ui = random.NextUint(); // uint in [0, uint.MaxValue]
float f = random.NextFloat(); // float in [0.0f, 1.0f]
double d = random.NextDouble(); // double in [0.0d, 1.0d]
bool b = random.NextBool(); // true or false
// Unity types
Vector2 v2 = random.NextVector2(); // Random 2D vector
Vector3 v3 = random.NextVector3(); // Random 3D vector
Color color = random.NextColor(); // Random color
Quaternion rot = random.NextRotation(); // Random rotation
// Distributions
float gaussian = random.NextGaussian(mean: 0f, stdDev: 1f);
// Collections
T item = random.NextOf(collection); // Random element
T[] shuffled = random.Shuffle(array); // Fisher-Yates shuffle
int weightedIndex = random.NextWeightedIndex(weights);
// Special
Guid uuid = random.NextGuid(); // UUIDv4
T enumValue = random.NextEnum
float[,] noise = random.NextNoiseMap(width, height); // Perlin noise
`
#### Deterministic Gameplay
All generators are seedable for replay systems:
`csharp
// Create seeded generator for deterministic behavior
IRandom seededRandom = new IllusionFlow(seed: 12345);
// Same seed = same sequence
IRandom replay = new IllusionFlow(seed: 12345);
// Both will generate identical values
`
Threading:
- Do not share a single RNG instance across threads.
- Use PRNG.Instance for a thread-local default, or use each generator's TypeName.Instance (e.g., IllusionFlow.Instance, PcgRandom.Instance).
- Alternatively, create one separate instance per thread.
---
Efficient spatial data structures for 2D and 3D games.
#### 2D Spatial Trees
- QuadTree2D - Best general-purpose choice
- KDTree2D - Fast nearest-neighbor queries
- RTree2D - Optimized for bounding boxes
`csharp
using WallstopStudios.UnityHelpers.Core.DataStructure;
// Create from collection
GameObject[] objects = FindObjectsOfType
QuadTree2D
// Query by radius
List
tree.GetElementsInRange(playerPos, radius: 10f, nearby);
// Query by bounds
Bounds searchArea = new(center, size);
tree.GetElementsInBounds(searchArea, nearby);
// Find nearest neighbors (approximate, but fast)
tree.GetApproximateNearestNeighbors(playerPos, count: 5, nearby);
`
#### 3D Spatial Trees
Note: KdTree3D, OctTree3D, and RTree3D are under active development. SpatialHash3D is stable and productionโready.
- OctTree3D - Best general-purpose choice for 3D
- KDTree3D - Fast 3D nearest-neighbor queries
- RTree3D - Optimized for 3D bounding volumes
- SpatialHash3D - Efficient for uniformly distributed moving objects (stable)
`csharp
// Same API as 2D, but with Vector3
Vector3[] positions = GetAllPositions();
OctTree3D
List
tree.GetElementsInRange(center, radius: 50f, results);
`
#### When to Use Spatial Trees
โ Good for:
- Many objects (100+)
- Frequent spatial queries
- Static or slowly changing data
- AI awareness systems
- Visibility culling
- Collision detection optimization
โ Not ideal for:
- Few objects (<50)
- Constantly moving objects
- Single queries
- Already using Unity's physics system
๐ 2D Benchmarks | ๐ 3D Benchmarks
For behavior details and edge cases, see: Spatial Tree Semantics
---
Stop writing GetComponent boilerplate. Auto-wire components using attributes.
Key attributes:
- [SiblingComponent] - Find components on same GameObject[ParentComponent]
- - Find components in parent hierarchy[ChildComponent]
- - Find components in children[ValidateAssignment]
- - Validate at edit time, show errors in inspector[WNotNull]
- - Must be assigned in inspector[WReadOnly]
- - Read-only display in inspector[WInLineEditor]
- - Inline inspector editing for object references[WShowIf]
- - Conditional display based on field values
Quick example:
`csharp
using WallstopStudios.UnityHelpers.Core.Attributes;
public class Enemy : MonoBehaviour
{
// Find on same GameObject
[SiblingComponent]
private Animator animator;
// Find in parent
[ParentComponent]
private EnemySpawner spawner;
// Find in children
[ChildComponent]
private List
// Optional component (no error if missing)
[SiblingComponent(Optional = true)]
private AudioSource audioSource;
// Only search direct children/parents
[ParentComponent(OnlyAncestors = true)]
private Transform[] parentHierarchy;
// Include inactive components
[ChildComponent(IncludeInactive = true)]
private ParticleSystem[] effects;
private void Awake()
{
this.AssignRelationalComponents();
}
}
`
See the in-depth guide: Relational Components.
Performance snapshots: Relational Component Performance Benchmarks.
---
Create data-driven gameplay effects that modify stats, apply tags, and drive cosmetics.
Key pieces:
- AttributeEffect โ ScriptableObject that bundles stat changes, tags, cosmetics, and duration.EffectHandle
- โ Unique ID for one application instance; remove/refresh specific stacks.AttributesComponent
- โ Base class for components that expose modifiable Attribute fields.TagHandler
- โ Counts and queries string tags for gating gameplay (e.g., "Stunned").CosmeticEffectData
- โ Prefab-like container of behaviors shown while an effect is active.
Quick example:
`csharp
using WallstopStudios.UnityHelpers.Tags;
// 1) Define stats on a component
public class CharacterStats : AttributesComponent
{
public Attribute Health = 100f;
public Attribute Speed = 5f;
}
// 2) Author an AttributeEffect (ScriptableObject) in the editor
// - modifications: [ { attribute: "Speed", action: Multiplication, value: 1.5f } ]
// - durationType: Duration, duration: 5
// - effectTags: [ "Haste" ]
// - cosmeticEffects: [ a prefab with CosmeticEffectData + Particle/Audio components ]
// 3) Apply and later remove
GameObject player = ...;
AttributeEffect haste = ...; // ScriptableObject reference
EffectHandle? handle = player.ApplyEffect(haste);
if (handle.HasValue)
{
// Remove early if needed
player.RemoveEffect(handle.Value);
}
// Query tags anywhere
if (player.HasTag("Stunned")) { / disable input / }
`
Details at a glance:
- ModifierDurationType.Instant โ applies permanently; returns null handle.ModifierDurationType.Duration
- โ temporary; expires automatically; reapply can reset if enabled.ModifierDurationType.Infinite
- โ persists until RemoveEffect(handle) is called.AttributeModification
- order: Addition โ Multiplication โ Override.CosmeticEffectData.RequiresInstancing
- โ instance per application or reuse shared presenters.
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 for patterns.
Further reading: see the full guide Effects System.
---
Fast, compact serialization for save systems, config, and networking.
This package provides three serialization technologies:
- Json โ Uses System.Text.Json with builtโin converters for Unity types.Protobuf
- โ Uses protobuf-net for compact, fast, schemaโevolvable binary.SystemBinary
- โ Uses .NET BinaryFormatter for legacy/ephemeral data only.
All are exposed via WallstopStudios.UnityHelpers.Core.Serialization.Serializer.
#### JSON Profiles
- Normal โ robust defaults (case-insensitive, includes fields, comments/trailing commas allowed)
- Pretty โ human-friendly, indented
- Fast โ strict, minimal with Unity converters (case-sensitive, strict numbers, no comments/trailing commas, IncludeFields=false)
- FastPOCO โ strict, minimal, no Unity converters; best for pure POCO graphs
#### When To Use What
- Use Json for:
- Player or tool settings, humanโreadable saves, serverless workflows.
- Interop with tooling, debugging, or versioning in Git.
- Use Protobuf for:
- Network payloads, large save files, bandwidth/storageโsensitive data.
- Situations where you expect schema evolution across versions.
- Use SystemBinary only for:
- Transient caches in trusted environments where data and code version match.
- Never for untrusted data or longโterm persistence.
#### JSON Example
`csharp
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Serialization;
public class SaveData
{
public Vector3 position;
public Color playerColor;
public List
}
var data = new SaveData
{
position = new Vector3(1, 2, 3),
playerColor = Color.cyan,
inventory = new List
};
// Serialize to UTFโ8 JSON bytes (Unity types supported via builtโin converters)
byte[] jsonBytes = Serializer.JsonSerialize(data);
// Deserialize from string
string jsonText = Serializer.JsonStringify(data, pretty: true);
SaveData fromText = Serializer.JsonDeserialize
// File helpers
Serializer.WriteToJsonFile(data, path: "save.json", pretty: true);
SaveData fromFile = Serializer.ReadFromJsonFile
// Generic entry points (choose format at runtime)
byte[] bytes = Serializer.Serialize(data, SerializationType.Json);
SaveData loaded = Serializer.Deserialize
`
#### Protobuf Example
`csharp
using ProtoBuf; // protobuf-net
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Serialization;
[ProtoContract]
public class NetworkMessage
{
[ProtoMember(1)] public int playerId;
[ProtoMember(2)] public Vector3 position; // Vector3 works in Protobuf via built-in surrogates
}
var message = new NetworkMessage { playerId = 7, position = new Vector3(5, 0, -2) };
// Protobuf bytes (small + fast)
byte[] bytes = Serializer.ProtoSerialize(message);
NetworkMessage decoded = Serializer.ProtoDeserialize
// Generic entry points
byte[] bytes2 = Serializer.Serialize(message, SerializationType.Protobuf);
NetworkMessage decoded2 = Serializer.Deserialize
// Buffer reuse (reduce GC for hot paths)
byte[] buffer = null;
int len = Serializer.Serialize(message, SerializationType.Protobuf, ref buffer);
NetworkMessage again = Serializer.Deserialize
`
Notes:
- Protobufโnet requires stable field numbers. Annotate with [ProtoMember(n)] and never reuse or renumber.
- Unity types supported via surrogates: Vector2/3, Vector2Int/3Int, Quaternion, Color/Color32, Rect/RectInt, Bounds/BoundsInt, Resolution.
Features:
- Custom converters for Unity types (Vector2/3/4, Color, GameObject, Matrix4x4, Type)
- Protobuf (protobufโnet) support for compact binary
- LZMA compression utilities (see Runtime/Utils/LZMA.cs)
- Typeโsafe serialization and pooled buffers/writers to reduce GC
---
Additional high-performance data structures:
| Structure | Use Case |
| -------------------- | ------------------------------ |
| CyclicBuffer
| BitSet | Compact boolean storage |
| ImmutableBitSet | Read-only bit flags |
| Heap
| PriorityQueue
| Deque
| DisjointSet | Union-find operations |
| Trie | String prefix trees |
| SparseSet | Fast add/remove with iteration |
| TimedCache
`csharp
// Cyclic buffer for damage history
CyclicBuffer
damageHistory.Add(25f);
damageHistory.Add(30f);
float avgDamage = damageHistory.Average();
// Priority queue for event scheduling (priority determined by comparer)
PriorityQueue
Comparer
);
eventQueue.Enqueue(spawnEvent);
eventQueue.Enqueue(bossEvent);
if (eventQueue.TryDequeue(out GameEvent next)) { / process event / }
// Trie for autocomplete
Trie commandTrie = new();
commandTrie.Insert("teleport");
commandTrie.Insert("tell");
commandTrie.Insert("terrain");
List
// Returns: ["teleport", "tell"]
`
---
Numeric helpers, geometry primitives, Unity extensions, colors, collections, strings, directions.
See the guide: Core Math & Extensions.
#### At a Glance
- PositiveMod, WrappedAdd โ Safe cyclic arithmetic for indices/angles.LineHelper.Simplify
- โ Reduce polyline vertices with DouglasโPeucker.Line2D.Intersects
- โ Robust 2D segment intersection and closest-point helpers.RectTransform.GetWorldRect
- โ Axis-aligned world bounds for rotated UI.Camera.OrthographicBounds
- โ Compute visible world bounds for ortho cameras.Color.GetAverageColor
- โ LAB/HSV/Weighted/Dominant color averaging.IEnumerable.Infinite
- โ Cycle sequences without extra allocations.StringExtensions.LevenshteinDistance
- โ Edit distance for fuzzy matching.
---
- RuntimeSingleton โ Global component singleton with optional crossโscene persistence.ScriptableObjectSingleton
- โ Global settings/data singleton loaded from Resources/, autoโcreated by the editor tool.
See the guide: Singleton Utilities and the tool: ScriptableObject Singleton Creator.
---
Unity Helpers includes 20+ editor tools to streamline your workflow:
- Sprite Tools: Cropper, Atlas Generator, Animation Editor, Pivot Adjuster
- Texture Tools: Blur, Resize, Settings Applier, Fit Texture Size
- Animation Tools: Event Editor, Creator, Copier, Sheet Animation Creator
- Validation: Prefab Checker with comprehensive validation rules
- Automation: ScriptableObject Singleton Creator, Attribute Cache Generator
- Compilation: Request a manual script compilation via Tools > Wallstop Studios > Unity Helpers > Request Script Compilation or use the default shortcut (Ctrl/Cmd + Alt + R) registered with Unityโs Shortcut Manager (listed under _Wallstop / Request Script Compilation_). The shortcut now forces an AssetDatabase.Refresh before requesting compilation and logs whenever Unity is already compiling, so scripts added outside the editor are imported even while Unity is unfocused.
๐ Complete Editor Tools Documentation
Quick Access:
- Menu: Tools > Wallstop Studios > Unity HelpersAssets > Create > Wallstop Studios > Unity Helpers
- Create Assets:
---
Zero-allocation queries with automatic cleanup and thread-safe pooling.
`csharp
using WallstopStudios.UnityHelpers.Utils;
using WallstopStudios.UnityHelpers.Core.DataStructure;
// Example: Use pooled buffer for spatial query
void FindNearbyEnemies(QuadTree2D
{
// Get pooled list - automatically returned when scope exits
using var lease = Buffers
// Use it with spatial query - combines zero-alloc query + pooled buffer!
tree.GetElementsInRange(position, 10f, buffer);
foreach (Enemy enemy in buffer)
{
enemy.TakeDamage(5f);
}
// buffer automatically returned to pool here
}
// Array pooling example
void ProcessLargeDataset(int size)
{
using var lease = WallstopArrayPool
// Use buffer for temporary processing
for (int i = 0; i < size; i++)
{
buffer[i] = ComputeValue(i);
}
// buffer automatically returned to pool here
}
`
Do / Don'ts:
- Do reuse buffers per system or component.
- Do treat buffers as temporary scratch space (APIs clear them first).
- Don't keep references to pooled lists beyond their lease lifetime.
- Don't share the same buffer across overlapping async/coroutine work.
- Buffers โ pooled collections (List/Stack/Queue/HashSet) with PooledResource leases.using var lease = Buffers
- Lists: using var lease = Buffers
- Stacks: using var lease = Buffers
- HashSets:
- Pattern: acquire โ use โ Dispose (returns to pool, clears collection).
- WallstopArrayPool โ rent arrays by length with automatic return on dispose.using var lease = WallstopArrayPool
- Example:
- Use for temporary processing buffers, sorting, or interop with APIs that require arrays.
- WallstopFastArrayPool โ fast array pool specialized for frequent shortโlived arrays (requires T : unmanaged), does not clear arrays. Returned arrays may have previous content in them.using var lease = WallstopFastArrayPool
- Example:
- Used throughout Helpers for highโfrequency editor/runtime operations (e.g., asset searches).
How pooling + buffering help APIs:
- Spatial queries: pass a reusable List to GetElementsInRange/GetElementsInBounds and iterate results without allocations.GetComponents(buffer)
- Component queries: clears and fills your buffer instead of allocating arrays.
- Editor utilities: temporary arrays/lists from pools keep import/scan tools snappy, especially inside loops.
---
Auto-detected packages:
- Zenject/Extenject: com.extenject.zenject, com.modesttree.zenject, com.svermeulen.extenjectjp.cysharp.vcontainer
- VContainer: , jp.hadashikick.vcontainercom.gustavopsantos.reflex
- Reflex:
Manual or source imports (no UPM):
- Add scripting defines in Project Settings > Player > Other Settings > Scripting Define Symbols:ZENJECT_PRESENT
- when Zenject/Extenject is presentVCONTAINER_PRESENT
- when VContainer is presentREFLEX_PRESENT
- when Reflex is present
- Add the define per target platform (e.g., Standalone, Android, iOS).
Notes:
- When the define is present, optional assemblies under Runtime/Integrations/* compile automatically and expose helpers like RelationalComponentsInstaller (Zenject/Reflex) and RegisterRelationalComponents() (VContainer).versionDefines
- If you use UPM, no manual defines are required โ the package IDs above trigger symbols via in the asmdefs.
- For test scenarios without LifetimeScope (VContainer), SceneContext (Zenject), or SceneScope (Reflex), see DI Integrations: Testing and Edge Cases for stepโbyโstep patterns.
Quick start:
- VContainer: in your LifetimeScope.Configure, call builder.RegisterRelationalComponents().RelationalComponentsInstaller
- Zenject/Extenject: add to your SceneContext (toggle scene scan if desired).RelationalComponentsInstaller
- Reflex: place 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.
`csharp
// VContainer โ LifetimeScope
using VContainer;
using VContainer.Unity;
using WallstopStudios.UnityHelpers.Integrations.VContainer;
protected override void Configure(IContainerBuilder builder)
{
// Register assigner + one-time scene scan + additive listener (default)
builder.RegisterRelationalComponents(
RelationalSceneAssignmentOptions.Default,
enableAdditiveSceneListener: true
);
}
// Zenject โ prefab instantiation with DI + relations
using Zenject;
using WallstopStudios.UnityHelpers.Integrations.Zenject;
var enemy = Container.InstantiateComponentWithRelations(enemyPrefab, parent);
// Reflex โ prefab instantiation with DI + relations
using Reflex.Core;
using WallstopStudios.UnityHelpers.Integrations.Reflex;
var enemy = container.InstantiateComponentWithRelations(enemyPrefab, parent);
`
See the full guide with scenarios, troubleshooting, and testing patterns: Relational Components Guide
- VContainer:
- resolver.InjectWithRelations(component) โ inject + assign a single instanceresolver.InstantiateComponentWithRelations(prefab, parent)
- โ instantiate + inject + assignresolver.InjectGameObjectWithRelations(root, includeInactiveChildren)
- โ inject hierarchy + assignresolver.InstantiateGameObjectWithRelations(prefab, parent)
- โ instantiate GO + inject + assign
- Zenject:
- container.InjectWithRelations(component) โ inject + assign a single instancecontainer.InstantiateComponentWithRelations(prefab, parent)
- โ instantiate + assigncontainer.InjectGameObjectWithRelations(root, includeInactiveChildren)
- โ inject hierarchy + assigncontainer.InstantiateGameObjectWithRelations(prefab, parent)
- โ instantiate GO + inject + assign
- Reflex:
- container.InjectWithRelations(component) โ inject + assign a single instancecontainer.InstantiateComponentWithRelations(prefab, parent)
- โ instantiate + inject + assigncontainer.InjectGameObjectWithRelations(root, includeInactiveChildren)
- โ inject hierarchy + assigncontainer.InstantiateGameObjectWithRelations(prefab, parent)
- โ instantiate GO + inject + assign
- VContainer: RegisterRelationalComponents(..., enableAdditiveSceneListener: true) registers a listener that hydrates components in newly loaded scenes.RelationalComponentsInstaller
- Zenject: exposes a toggle "Listen For Additive Scenes" to register the same behavior.RelationalComponentsInstaller
- Reflex: exposes a toggle "Listen For Additive Scenes" to register the same behavior.
- Only the newly loaded scene is processed; other loaded scenes are not reโscanned.
- One-time scene scan runs after container build; additive scenes are handled incrementally.
- Single-pass scan (default) reduces FindObjectsOfType calls by scanning once and checking type ancestry.new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)
- VContainer: new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)
- Zenject: new RelationalSceneAssignmentOptions(includeInactive: true, useSinglePassScan: true)
- Reflex:
- Per-object paths (instantiate/inject helpers, pools) avoid global scans entirely for objects created via DI.
---
Unity Helpers is built with performance as a top priority:
Random Number Generation:
- Benchmarks show 10-15x faster generation than Unity.Random. Results vary by generator and operation; see performance documentation for details.
- Zero GC pressure with thread-local instances
- ๐ Full Random Performance Benchmarks
Spatial Queries:
- O(log n) tree queries vs O(n) linear search
- Significant speedup for large datasets due to O(log n) complexity
- QuadTree2D: 10,000 objects = ~13 checks vs 10,000 checks
- ๐ 2D Performance Benchmarks
- ๐ 3D Performance Benchmarks
Memory Management:
- Zero-allocation buffering pattern eliminates GC spikes
- Professional-grade pooling for List, HashSet, Stack, Queue, Arrays
- Reduces GC pressure in allocation-heavy scenarios
Reflection:
- Cached delegates are 10-100x faster than raw System.Reflection (method invocations ~12x; boxed scenarios up to 100x)ReflectionHelpers.OverrideReflectionCapabilities
- Safe for IL2CPP and AOT platforms; capability overrides () let tests force expression/IL fallbacks
- Run the benchmarks via ReflectionPerformanceTests.Benchmark (EditMode Test Runner) and commit the updated markdown section
- ๐ Reflection Helpers Guide and ๐ Benchmarks
List Sorting:
- Multiple adaptive algorithms (Ghost, Meteor, Power, Grail, Pattern-Defeating QuickSort, Insertion, Tim, Jesse, Green, Ska, Ipn, Smooth, Block, IPS4o, Power+, Glide, Flux) tuned for IList
- Deterministic datasets (sorted, nearly sorted, shuffled) across sizes from 100 to 1,000,000
- ๐ IList Sorting Performance Benchmarks
---
Start Here:
- ๐ Getting Started โ Getting Started Guide
- ๐ Feature Index โ Complete A-Z Index
- ๐ Glossary โ Term Definitions
Core Guides:
- Odin Migration Guide โ Migrate from Odin Inspector
- Serialization Guide โ Serialization
- Editor Tools Guide โ Editor Tools
- Math & Extensions โ Core Math & Extensions
- Singletons โ Singleton Utilities
- Relational Components โ Relational Components
- Effects System โ Effects System
- Data Structures โ Data Structures
Spatial Trees:
- 2D Spatial Trees Guide โ 2D Spatial Trees Guide
- 3D Spatial Trees Guide โ 3D Spatial Trees Guide
- Spatial Tree Semantics โ Spatial Tree Semantics
- Spatial Tree 2D Performance โ Spatial Tree 2D Performance
- Spatial Tree 3D Performance โ Spatial Tree 3D Performance
- Hulls (Convex vs Concave) โ Hulls
Performance & Reference:
- Reflection Performance Guide โ Reflection Benchmarks
- Reflection AOT/Burst Validation โ IL2CPP & Burst Validation
- Reflection Benchmark Workflow โ Benchmarking & Verification
- Random Performance โ Random Performance
- Reflection Helpers โ Reflection Helpers
- IList Sorting Performance โ IList Sorting Performance
Project Info:
- Changelog โ Changelog
- License โ License
- ThirdโParty Notices โ ThirdโParty Notices
- Contributing โ Contributing
- llms.txt โ LLM-Friendly Documentation | llms.txt
---
Contributions are welcome! Please feel free to submit a Pull Request.
- Dependabot PRs: Formatting fixes (CSharpier + Prettier + markdownlint) are applied automatically by CI.
- Contributor PRs: Opt-in formatting is available.
- Comment on the PR with /format (aliases: /autofix, /lint-fix).
- If the PR branch is in this repo, the bot pushes a commit with fixes.
- If the PR is from a fork, the bot opens a formatting PR targeting the base branch.
- The commenter must be the PR author or a maintainer/collaborator.
- Or run the Actions workflow manually: Actions โ "Opt-in Formatting" โ Run workflow โ enter the PR number.
- Not everything is auto-fixable: link checks and YAML linting may still require manual changes.
See more details in CONTRIBUTING.
---
This project is licensed under the MIT License - see the LICENSE file for details.
- BinaryFormatter deprecated, still functional for trusted/legacy data:
- 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.
- GameObject JSON converter outputs structured JSON:
- GameObjectConverter now writes a JSON object with name, type (assembly-qualified), and instanceId rather than a stringified placeholder.
- Minor robustness improvements:
- Guarded stray UnityEditor` imports in runtime files to ensure clean player builds.
See Serialization guide for AOT/IL2CPP guidance and Unity JSON options, and Editor tools guide for Editor tool usage details.