Treasure chest of Unity developer tools. Professional inspector tooling, high-performance utilities, spatial queries, and 20+ editor tools.
This practical guide complements performance and semantics pages with diagrams and actionable selection advice.
| Object Count | Naive Approach (checks) | Spatial Tree (checks) | Speedup |
|---|---|---|---|
| 100 | 100 | ~7 | 14x |
| 1,000 | 1,000 | ~10 | 100x |
| 10,000 | 10,000 | ~13 | 769x |
| 100,000 | 💀 Unplayable | ~17 | ∞ |
Quick picks
Points (QuadTree2D / KdTree2D)
using WallstopStudios.UnityHelpers.Core.DataStructure;
using UnityEngine;
using System.Collections.Generic;
// Example element with a position
struct Enemy { public Vector2 pos; public int id; }
var enemies = new List<Enemy>(/* fill with positions */);
// Build a tree from points
var quad = new QuadTree2D<Enemy>(enemies, e => e.pos);
var kd = new KdTree2D<Enemy>(enemies, e => e.pos); // balanced by default
// Range query (circle)
var inRange = new List<Enemy>();
quad.GetElementsInRange(playerPos, 10f, inRange);
// Bounds (box) query
var inBox = new List<Enemy>();
kd.GetElementsInBounds(new Bounds(center, size), inBox);
// Approximate nearest neighbors
var neighbors = new List<Enemy>();
kd.GetApproximateNearestNeighbors(playerPos, count: 10, neighbors);
Sized objects (RTree2D)
using WallstopStudios.UnityHelpers.Core.DataStructure;
using UnityEngine;
using System.Collections.Generic;
struct Tile { public Bounds bounds; public int kind; }
var tiles = new List<Tile>(/* fill with bounds */);
// Build from bounds (AABBs)
var rtree = new RTree2D<Tile>(tiles, t => t.bounds);
// Bounds query (fast for large areas)
var hits = new List<Tile>();
rtree.GetElementsInBounds(worldBounds, hits);
// Range query (treats items by their bounds)
var near = new List<Tile>();
rtree.GetElementsInRange(center, radius, near);
Notes
SpatialHash2D for broad‑phase.The Problem - GC Spikes Every Frame:
void Update()
{
// 🔴 BAD: Allocates new List every frame
List<Enemy> nearby = tree.GetElementsInRange(playerPos, 10f);
foreach (Enemy e in nearby)
{
e.ReactToPlayer();
}
// Result: GC runs frequently = frame drops
}
The Solution - Buffering Pattern:
// Reusable buffer (declare once)
private List<Enemy> nearbyBuffer = new(64);
void Update()
{
nearbyBuffer.Clear();
// 🟢 GOOD: Reuses same List = zero allocations
tree.GetElementsInRange(playerPos, 10f, nearbyBuffer);
foreach (Enemy e in nearbyBuffer)
{
e.ReactToPlayer();
}
// Result: No GC, stable 60fps
}
Impact:
All spatial trees support this pattern:
QuadTree2D.GetElementsInRange(pos, radius, buffer)KdTree2D.GetElementsInBounds(bounds, buffer)RTree2D.GetElementsInRange(pos, radius, buffer)💡 Pro Tip: Pre-size your buffers based on expected max results.
new List<Enemy>(64)avoids internal resizing for results up to 64 items.
Maximum Ergonomics:
These APIs return the buffer you pass in, so you can use them directly in foreach loops:
private List<Enemy> nearbyBuffer = new(64);
void Update()
{
// Returns the same buffer - use it directly in foreach!
foreach (Enemy e in tree.GetElementsInRange(playerPos, 10f, nearbyBuffer))
{
e.ReactToPlayer();
}
}
Using Pooled Buffers:
Don’t want to manage buffers yourself? Use the built-in pooling utilities:
using WallstopStudios.UnityHelpers.Utils;
void Update()
{
// Get pooled buffer - automatically returned when scope exits
using var lease = Buffers<Enemy>.List.Get(out List<Enemy> buffer);
// Use it directly in foreach - combines zero-alloc query + pooled buffer!
foreach (Enemy e in tree.GetElementsInRange(playerPos, 10f, buffer))
{
e.ReactToPlayer();
}
// buffer automatically returned to pool here
}
See Buffering Pattern for the complete guide and Pooling Utilities for more pooling options.
Diagram:
Diagram:
Diagram:
Use this decision flowchart to pick the right spatial tree:
START: Do your objects move frequently?
│
├─ YES → Consider SpatialHash2D instead (see README)
│ (Spatial trees require rebuild on movement)
│
└─ NO → Continue to next question
│
└─ What type of queries do you need?
│
├─ Primarily nearest neighbor (k-NN)
│ │
│ ├─ Static data, want consistent performance
│ │ → KDTree2D (Balanced) ✓
│ │
│ └─ Data changes occasionally, need fast rebuilds
│ → KDTree2D (Unbalanced) ✓
│
├─ Do objects have size/bounds (not just points)?
│ │
│ ├─ YES → Need bounds intersection queries
│ │ → RTree2D ✓
│ │
│ └─ NO → Continue
│
└─ General range/circular queries, broad-phase
→ QuadTree2D ✓ (best all-around choice)
For deeper details, performance data, and diagrams, see: