Helper Utilities Guide¶
TL;DR — Why Use These¶
Static helper classes and utilities that solve common programming problems without needing components on GameObjects. Use these for predictive aiming, path utilities, threading, hashing, formatting, and more.
Contents¶
- Gameplay Helpers — Predictive aiming, spatial sampling, rotation
- GameObject & Component Helpers — Component discovery, hierarchy manipulation
- Transform Helpers — Hierarchy traversal
- Coroutine Wait Pools — Configure
Buffers.GetWaitForSeconds*caching - Threading — Main thread dispatcher
- Path & File Helpers — Path resolution, file operations
- Scene Helpers — Scene queries and loading
- Advanced Utilities — Null checks, hashing, formatting
- Environment Detection — CI, batch mode, and runtime environment
Coroutine Wait Pools¶
Unity allocates a new WaitForSeconds/WaitForSecondsRealtime every time you yield with a literal. Buffers.GetWaitForSeconds(...) and Buffers.GetWaitForSecondsRealTime(...) pool those instructions to reduce coroutine allocations, but each distinct duration used to stick around forever. Large ranges (randomized cooldowns, tweens, etc.) could leak thousands of instances.
New pooling policy knobs (Runtime 2.2.1+):
| Setting | Default | Purpose |
|---|---|---|
Buffers.WaitInstructionMaxDistinctEntries | 512 | Upper bound on distinct cached durations. Set to 0 to disable the cap, or tighten it for editor/dev builds. When the limit is reached the cache stops growing (or evicts, if LRU is enabled). |
Buffers.WaitInstructionQuantizationStepSeconds | 0 (off) | Rounds requested durations to the nearest step before caching. Useful when you can tolerate millisecond snapping (e.g., .005f → .01f). |
Buffers.WaitInstructionUseLruEviction | false | When true, the cache becomes an LRU: it evicts the least recently used duration whenever it hits the max entry count instead of rejecting new ones. Diagnostics expose the eviction count. |
Buffers.TryGetWaitForSecondsPooled(float seconds) / TryGetWaitForSecondsRealtimePooled | n/a | Returns the cached instruction or null if the request would exceed the cap. Use this when you want to detect “unsafe” usages and allocate manually instead. |
Buffers.WaitForSecondsCacheDiagnostics / .WaitForSecondsRealtimeCacheDiagnostics | snapshot | Exposes DistinctEntries, MaxDistinctEntries, LimitRefusals, and whether quantization is active so you can surface metrics in your own tooling. |
⚙️ Project-wide defaults: Open the Coroutine Wait Instruction Buffers foldout under Project Settings ▸ Wallstop Studios ▸ Unity Helpers to edit these knobs. The settings asset lives at
Resources/Wallstop Studios/Unity Helpers/UnityHelpersBufferSettings.asset, ships with your build, and automatically applies on script/domain reload or when a player starts (unless your code overrides the values at runtime). Use Apply Defaults Now to push the current sliders into the active domain or Capture Current Values to snapshot whateverBuffersis using in play mode.🔒 Persistence Behavior: When you click Apply Defaults Now, the settings are immediately:
- Saved to disk — The asset is marked dirty and saved via
AssetDatabase.SaveAssets()- Applied to the runtime —
Buffers.WaitInstruction*properties are updated immediatelyThis ensures settings persist across:
- Domain reloads (script recompilation, entering/exiting play mode) — Via
[InitializeOnLoadMethod]- Editor restarts — The asset is saved to disk and reloads automatically
- Standalone builds — The asset ships under
Resources/and auto-applies via[RuntimeInitializeOnLoadMethod]Toggle Apply On Load to control whether the saved defaults auto-apply when the domain loads. If disabled, the asset serves as a reference and you must call
asset.ApplyToBuffers()manually.
⚠️ Limit warnings: In Editor and Development builds the first limit hit (and every 25th after) emits a warning so you can spot misuses quickly. Production builds skip the log to avoid noise.
✅ Deterministic fallback: When the cache refuses a duration,
Buffers.GetWaitForSeconds*still returns a valid instruction—it just isn’t cached, so highly variable waits no longer lead to unbounded memory growth.
Gameplay Helpers¶
Predictive Aiming¶
What it does: Calculates where to aim when shooting at a moving target, accounting for projectile travel time.
Problem it solves: Shooting a bullet at where an enemy is misses if they're moving. You need to aim at where they will be.
When to use:
- Turrets shooting at moving enemies
- AI aiming at moving players
- Predictive targeting systems
- Guided missiles
When NOT to use:
- Homing projectiles (use steering behaviors)
- Instant-hit weapons (use raycasts)
- Slow-moving or stationary targets (just aim directly)
Spatial Sampling¶
Get random points in circles/spheres:
| C# | |
|---|---|
Use for:
- Spawn points (enemies, pickups, particles)
- Explosion damage distribution
- Random movement destinations
- Scatter patterns
Smooth Rotation Helpers¶
Get rotation speed for smooth turning:
Handles:
- Frame-rate independence
- Shortest rotation path (doesn't spin 270° when 90° is shorter)
- Angle wrapping (0-360°)
Delayed Execution¶
Execute code after delay or next frame:
| C# | |
|---|---|
Uses coroutines under the hood.
Repeating Execution with Jitter¶
Run function repeatedly with random timing variance:
| C# | |
|---|---|
Use for:
- Enemy spawning with variability
- Random event triggers
- Staggered updates to spread CPU load
- Natural-feeling timing
Layer & Label Queries¶
| C# | |
|---|---|
Use for:
- Populating dropdowns in editor tools
- Runtime layer/label validation
- Configuration systems
Collider Syncing¶
Update PolygonCollider2D to match sprite:
| C# | |
|---|---|
GameObject & Component Helpers¶
Cached Component Lookup¶
Tag-based component finding with caching:
Performance: First call searches the scene using GameObject.FindWithTag; subsequent calls use a cached O(1) dictionary lookup. The cache persists until manually cleared.
Component Existence Checks¶
| C# | |
|---|---|
Get-or-Add Pattern¶
| C# | |
|---|---|
Hierarchical Enable/Disable¶
Recursively enable/disable components:
| C# | |
|---|---|
Use for:
- Toggling collision for entire character rigs
- Hiding/showing complex prefabs
- Debug visualization toggles
Bulk Child Destruction¶
| C# | |
|---|---|
Use for:
- Clearing inventory UI
- Resetting spawn containers
- Cleanup before repopulating
Smart Destruction¶
Editor/runtime aware destruction:
| C# | |
|---|---|
Use in editor tools to avoid "Destroying assets is not permitted" errors.
Prefab Utilities¶
Transform Helpers¶
Hierarchy Traversal (Depth-First)¶
Visit all children recursively:
Hierarchy Traversal (Breadth-First)¶
Visit by depth level:
| C# | |
|---|---|
Use for:
- Finding immediate area (not entire tree)
- Level-based operations
- Performance-sensitive searches
Parent Traversal¶
Walk up the hierarchy:
Use for:
- Finding UI Canvas parents
- Inheritance checking (is this under X?)
- Walking to root of hierarchy
Direct Children/Parents¶
| C# | |
|---|---|
Threading¶
UnityMainThreadDispatcher¶
Execute code on Unity's main thread from background threads:
Problem it solves: Unity APIs can only be called from the main thread. Background Tasks/threads can't directly manipulate GameObjects. This marshals callbacks back to the main thread.
See the dedicated Unity Main Thread Dispatcher guide for details about auto-creation, queue limits, the AutoCreationScope helper, and the CreateTestScope(...) convenience method that packages can use in their own test fixtures.
Async version with result:
| C# | |
|---|---|
Logging¶
Use the Logging Extensions guide for:
- Rich text tags applied directly inside interpolated strings (
$"{value:b,color=red}") - Thread-aware logging helpers (
this.Log,this.LogWarn,this.LogError,this.LogDebug) - Tips for registering custom decorations and gating logs per-object or globally
These helpers rely on the same dispatcher utilities above, so logging from jobs/background threads stays safe.
Fire-and-forget on main thread:
| C# | |
|---|---|
When to use:
- Async file loading callbacks
- Network request callbacks
- Database query results
- Background computation results that update UI
Important:
- Works in both edit mode and play mode
- Actions queued during edit mode execute in next editor update
- Don't block the main thread with long operations
Path & File Helpers¶
Path Sanitization¶
Normalize path separators:
| C# | |
|---|---|
Unity prefers forward slashes. Use this for cross-platform paths.
Directory Utilities¶
Create directories safely:
| C# | |
|---|---|
Find package root:
| C# | |
|---|---|
Use for:
- Editor tools generating assets
- Finding package-relative paths
- Build scripts creating folders
Path Conversion¶
Convert between absolute and Unity-relative paths:
| C# | |
|---|---|
Get calling script's directory:
| C# | |
|---|---|
File Operations¶
Initialize file if missing:
| C# | |
|---|---|
Async file copy:
| C# | |
|---|---|
Use for:
- Large file operations without blocking
- Cancellable copy operations
- Streaming file operations
Scene Helpers¶
Scene Queries¶
Check if scene is loaded:
| C# | |
|---|---|
Get all scene paths (editor):
| C# | |
|---|---|
Temporary Scene Loading¶
Load scene, extract data, auto-unload:
Use for:
- Extracting data from data-only scenes
- Editor tools reading scene contents
- Validation scripts
- Testing scene contents
Advanced Utilities¶
Unity-Aware Null Checks¶
The problem: Unity's == operator overload can be slow, and destroyed UnityEngine.Objects return true for == null but false for is null.
| C# | |
|---|---|
Handles:
- Destroyed UnityEngine.Objects
- Actual null references
- Optimized checks for non-Unity types
Deterministic Hashing¶
Combine hash codes correctly:
| C# | |
|---|---|
Supports up to 11 parameters. Uses FNV-1a algorithm for good distribution.
Hash entire collections:
| C# | |
|---|---|
Use for:
- Custom GetHashCode implementations
- Dictionary keys with multiple fields
- Networking determinism
- Save file hashing
Formatting¶
Human-readable byte counts:
| C# | |
|---|---|
Auto-scales to B, KB, MB, GB, TB.
Use for:
- File size displays
- Memory usage UI
- Profiling output
- Download progress
Multi-Dimensional Array Iteration¶
Enumerate 2D/3D array indices:
Also supports 3D arrays with (int, int, int) tuples.
Binary Array Conversion¶
Marshalling between int[] and byte[]:
| C# | |
|---|---|
Use for:
- Network serialization
- Binary file formats
- Save game data
- High-performance data conversion
Performance: Uses native memory copy (Buffer.BlockCopy) which is faster than element-by-element loops due to optimized native implementation, though both are O(n).
Custom Comparers¶
Create IComparer from lambda:
| C# | |
|---|---|
Reverse any comparer:
| C# | |
|---|---|
Environment Detection¶
CI/CD Detection¶
Detect if running in a CI environment:
| C# | |
|---|---|
Supported CI systems (checked via environment variables):
| CI System | Environment Variable |
|---|---|
| Generic CI | CI |
| GitHub Actions | GITHUB_ACTIONS |
| GitLab CI | GITLAB_CI |
| Jenkins | JENKINS_URL |
| Travis CI | TRAVIS |
| CircleCI | CIRCLECI |
| Azure Pipelines | TF_BUILD |
| TeamCity | TEAMCITY_VERSION |
| Buildkite | BUILDKITE |
| AWS CodeBuild | CODEBUILD_BUILD_ID |
| Bitbucket Pipelines | BITBUCKET_BUILD_NUMBER |
| AppVeyor | APPVEYOR |
| Drone CI | DRONE |
| Unity CI | UNITY_CI |
| Unity Tests | UNITY_TESTS |
Check specific environment variables:
Use for:
- Skipping interactive dialogs in CI
- Disabling expensive editor visualizations
- Conditional test behavior
- Build automation scripts
- Asset processors that shouldn't run headless
Best Practices¶
Performance¶
- Cache lookups:
Helpers.Find<T>()caches, but don't call every frame anyway - Use buffered variants:
IterateOverAllChildrenRecursivelywith buffers for hot paths - Main thread dispatch: Don't send hundreds of tiny tasks, batch work
- Hierarchy traversal: Use breadth-first with depth limits for large hierarchies
Threading¶
- Main thread rule: Only Unity APIs need main thread, pure C# can stay on background threads
- Avoid blocking: Don't wait for main thread results in tight loops
- CancellationToken: Support cancellation for long operations
Architecture¶
- Component vs Helper: Components (MonoBehaviours) for per-object state, Helpers for stateless operations
- Static method smell: If you need instance state, use a component instead
- Editor/Runtime split: Use
#if UNITY_EDITORguards for editor-only helpers
Code Organization¶
- Namespace imports: Use
using WallstopStudios.UnityHelpers.Core.Helper;at top of file - Don't extend helpers: These are sealed utility classes, not inheritance hierarchies
- Prefer composition: Use helpers from components, don't try to combine them
Related Documentation¶
- Intelligent Pooling System - Advanced object pooling with auto-purging
- Math & Extensions - Extension methods on built-in types
- Utility Components - MonoBehaviour-based utilities
- Reflection Helpers - High-performance reflection utilities
- Singletons - RuntimeSingleton and ScriptableObjectSingleton
- Data Structures - Cache, spatial trees, and other collections