Skip to content

Instantly share code, notes, and snippets.

@Denchyaknow
Last active February 23, 2023 16:15
Show Gist options
  • Save Denchyaknow/246fa0459fa72d62d8af61944ea55b2b to your computer and use it in GitHub Desktop.
Save Denchyaknow/246fa0459fa72d62d8af61944ea55b2b to your computer and use it in GitHub Desktop.
Template_QuadTree (Spawns) a spatial partitioning data structure to store your spawn points, so you can make queries to find the closest spawn point to a given point in a efficient way.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace HunyLand.Utils
{
/// <summary>
/// QuadTree (Spawns) a spatial partitioning data structure to store your spawn points,
/// so you can make queries to find the closest spawn point to a given point in a efficient way.
/// Mage: Dencho
/// </summary>
public class QuadTree<T> where T : BaseSpawn
{
private const int MAX_OBJECTS = 10;//The MAX_OBJECTS and MAX_LEVELS constants can be adjusted to suit the specific needs of your game.
private const int MAX_LEVELS = 5;
private int level;
private List<T> objects;
private Bounds bounds;
private QuadTree<T>[] children;
public QuadTree(int level, Bounds bounds)
{
this.level = level;
this.objects = new List<T>();
this.bounds = bounds;
this.children = new QuadTree<T>[4];
}
public void Clear()
{
objects.Clear();
for (int i = 0; i < children.Length; i++)
{
if (children[i] != null)
{
children[i].Clear();
children[i] = null;
}
}
}
public void Insert(T spawn)
{
if (children[0] != null)
{
int index = GetIndex(spawn.transform.position);
if (index != -1)
{
children[index].Insert(spawn);
return;
}
}
objects.Add(spawn);
if (objects.Count > MAX_OBJECTS && level < MAX_LEVELS)
{
if (children[0] == null)
{
Split();
}
int i = 0;
while (i < objects.Count)
{
int index = GetIndex(objects[i].transform.position);
if (index != -1)
{
children[index].Insert(objects[i]);
objects.RemoveAt(i);
}
else
{
i++;
}
}
}
}
public bool Remove(T spawn)
{
if (children[0] != null)
{
int index = GetIndex(spawn.transform.position);
if (index != -1 && children[index].Remove(spawn))
{
return true;
}
}
if (objects.Remove(spawn))
{
return true;
}
return false;
}
public List<T> Retrieve(Vector3 position)
{
List<T> spawnInRange = new List<T>();
int index = GetIndex(position);
if (index != -1 && children[0] != null)
{
spawnInRange = children[index].Retrieve(position);
}
spawnInRange.AddRange(objects.Where(spawn => spawn.IsAvailable() && (spawn.transform.position - position).sqrMagnitude <= spawn.range * spawn.range));
return spawnInRange;
}
private void Split()
{
float subWidth = (bounds.extents.x / 2);
float subHeight = (bounds.extents.z / 2);
float x = bounds.center.x;
float y = bounds.center.z;
children[0] = new QuadTree<T>(level + 1, new Bounds(new Vector3(x + subWidth, 0, y + subHeight), new Vector3(subWidth, subHeight, 0)));
children[1] = new QuadTree<T>(level + 1, new Bounds(new Vector3(x - subWidth, 0, y + subHeight), new Vector3(subWidth, subHeight, 0)));
children[2] = new QuadTree<T>(level + 1, new Bounds(new Vector3(x - subWidth, 0, y - subHeight), new Vector3(subWidth, subHeight, 0)));
children[3] = new QuadTree<T>(level + 1, new Bounds(new Vector3(x + subWidth, 0, y - subHeight), new Vector3(subWidth, subHeight, 0)));
}
private int GetIndex(Vector3 position)
{
int index = -1;
double verticalMidpoint = bounds.center.x;
double horizontalMidpoint = bounds.center.z;
bool topQuadrant = (position.z > horizontalMidpoint);
bool bottomQuadrant = (position.z < horizontalMidpoint);
bool leftQuadrant = (position.x < verticalMidpoint);
bool rightQuadrant = (position.x > verticalMidpoint);
if (topQuadrant)
{
if (leftQuadrant)
{
index = 0;
}
else if (rightQuadrant)
{
index = 1;
}
}
else if (bottomQuadrant)
{
if (leftQuadrant)
{
index = 2;
}
else if (rightQuadrant)
{
index = 3;
}
}
return index;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment