
A live QuadTree visualiser built in UE5 C++. Hundreds of particles move each frame; the tree is rebuilt only when a particle has moved more than a 10-unit threshold since the last insert. Per-particle neighbour queries drive debug connection lines — demonstrating O(log n) proximity lookups versus O(n²) brute force.
UpdateQuadtree() clears the tree then reinserts all particles in 1 linear pass, keeping root bounds stable across frames.QueryPoints() called per particle with 1 configurable radius; O(log n) results drive per-particle debug line drawing.Only rebuilds when a particle exceeds the movement threshold. The query result feeds the debug draw call each tick.
void AParticleSystemActor::UpdateQuadtree()
{
Quadtree->Clear();
for (AParticleActor* P : Particles)
Quadtree->Insert(P->GetActorLocation(), P);
// Cache last-known positions for dirty checking
LastPositions.Empty();
for (AParticleActor* P : Particles)
LastPositions.Add(P->GetActorLocation());
}
void AParticleSystemActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Rebuild only when any particle moved > 10 units
bool bDirty = false;
for (int32 i = 0; i < Particles.Num(); ++i)
{
if (FVector::Dist(Particles[i]->GetActorLocation(), LastPositions[i]) > 10.f)
{ bDirty = true; break; }
}
if (bDirty) UpdateQuadtree();
// Per-particle neighbour query → debug lines
for (AParticleActor* P : Particles)
{
TArray<AActor*> Neighbours;
Quadtree->QueryPoints(P->GetActorLocation(), QueryRadius, Neighbours);
for (AActor* N : Neighbours)
DrawDebugLine(GetWorld(), P->GetActorLocation(),
N->GetActorLocation(), FColor::Cyan, false, -1.f, 0, 1.f);
}
}