Advertisement
Guest User

Unity C# GenericQuadTree

a guest
Jan 1st, 2020
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.92 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3.  
  4. /** Quadtree for quick nearest neighbour search of agents.
  5.  */
  6. public class GenericQuadtree<T> where T : UnityEngine.Component {
  7.     const int LeafSize = 8;
  8.  
  9.     struct Node {
  10.         public int child00;
  11.         public int child01;
  12.         public int child10;
  13.         public int child11;
  14.         public byte count;
  15.         public List<T> linkedList;
  16.  
  17.         public void Add(T agent) {
  18.             if (linkedList == null) linkedList = new List<T>();
  19.             linkedList.Add(agent);
  20.             count++;
  21.         }
  22.  
  23.         public void Distribute(Node[] nodes, Rect r) {
  24.             if (linkedList == null) return;
  25.  
  26.             Vector2 c = r.center;
  27.             for (int i = 0; i < linkedList.Count; i++) {
  28.                 T obj = linkedList[i];
  29.  
  30.                 if (obj.transform.position.x > c.x) {
  31.                     if (obj.transform.position.z > c.y) {
  32.                         nodes[child11].Add(obj);
  33.                     }
  34.                     else {
  35.                         nodes[child10].Add(obj);
  36.                     }
  37.                 }
  38.                 else {
  39.                     if (obj.transform.position.z > c.y) {
  40.                         nodes[child01].Add(obj);
  41.                     }
  42.                     else {
  43.                         nodes[child00].Add(obj);
  44.                     }
  45.                 }
  46.             }
  47.             linkedList.Clear();
  48.             count = 0;
  49.         }
  50.     }
  51.  
  52.     Node[] nodes = new Node[32];
  53.     int filledNodes = 1;
  54.     bool dirty = false;
  55.     Rect bounds;
  56.  
  57.     public void Clear() {
  58.         nodes[0].count = 0;
  59.         nodes[0].child00 = 0;
  60.         if (nodes[0].linkedList != null) nodes[0].linkedList.Clear();
  61.         filledNodes = 1;
  62.     }
  63.  
  64.     System.Collections.Generic.LinkedList<T> allNodes = new LinkedList<T>();
  65.  
  66.     void ReserveFour() {
  67.         if (filledNodes + 4 >= nodes.Length) {
  68.             var nds = new Node[nodes.Length * 2];
  69.             for (int i = 0; i < nodes.Length; i++) nds[i] = nodes[i];
  70.             nodes = nds;
  71.         }
  72.     }
  73.  
  74.     int GetNodeIndex() {
  75.         if (nodes[filledNodes].linkedList != null) nodes[filledNodes].linkedList.Clear();
  76.         nodes[filledNodes].count = 0;
  77.         nodes[filledNodes].child00 = filledNodes;
  78.         filledNodes++;
  79.         return filledNodes - 1;
  80.     }
  81.  
  82.     void SetBounds(Rect r) {
  83.         bounds = r;
  84.     }
  85.  
  86.     public void SetDirty() {
  87.         dirty = true;
  88.     }
  89.  
  90.     void Rebuild() {
  91. #if UNITY_EDITOR
  92.         System.Console.WriteLine("Rebuilding Quadtree for " + typeof(T).Name + "... ");
  93. #endif
  94.         dirty = false;
  95.  
  96.         // Remove all previous data
  97.         Clear();
  98.  
  99.         // Calculate the total bounds
  100.         var c = allNodes.First;
  101.  
  102.         if (c == null) {
  103.             // Nothing in the tree!
  104.             return;
  105.         }
  106.  
  107.         bounds = new Rect(c.Value.transform.position.x, c.Value.transform.position.z, 0, 0);
  108.         while (c != null) {
  109.             Vector2 p = new Vector2(c.Value.transform.position.x, c.Value.transform.position.z);
  110.             bounds = Rect.MinMaxRect(Mathf.Min(bounds.xMin, p.x), Mathf.Min(bounds.yMin, p.y), Mathf.Max(bounds.xMax, p.x), Mathf.Max(bounds.yMax, p.y));
  111.             c = c.Next;
  112.         }
  113.         SetBounds(bounds);
  114.  
  115.         // Insert all nodes
  116.         c = allNodes.First;
  117.         while (c != null) {
  118.             Update(c.Value);
  119.             c = c.Next;
  120.         }
  121.     }
  122.  
  123.     public LinkedListNode<T> Insert(T agent) {
  124.         Vector2 p = new Vector2(agent.transform.position.x, agent.transform.position.z);
  125.  
  126.         if (!bounds.Contains(p)) {
  127.             SetDirty();
  128.         }
  129.         else {
  130.             if (dirty) {
  131.                 Rebuild();
  132.             }
  133.  
  134.             Update(agent);
  135.         }
  136.  
  137.         return allNodes.AddLast(agent);
  138.     }
  139.  
  140.     void Update(T agent) {
  141.         int i = 0;
  142.         Rect r = bounds;
  143.  
  144.         Vector2 p = new Vector2(agent.transform.position.x, agent.transform.position.z);
  145.  
  146.         int depth = 0;
  147.  
  148.         while (true) {
  149.             depth++;
  150.  
  151.             if (nodes[i].child00 == i) {
  152.                 // Leaf node. Break at depth 10 in case lots of agents ( > LeafSize ) are in the same spot
  153.                 if (nodes[i].count < LeafSize || depth > 10) {
  154.                     nodes[i].Add(agent);
  155.                     break;
  156.                 }
  157.                 else {
  158.                     // Split
  159.                     Node node = nodes[i];
  160.                     ReserveFour();
  161.                     node.child00 = GetNodeIndex();
  162.                     node.child01 = GetNodeIndex();
  163.                     node.child10 = GetNodeIndex();
  164.                     node.child11 = GetNodeIndex();
  165.                     nodes[i] = node;
  166.  
  167.                     nodes[i].Distribute(nodes, r);
  168.                 }
  169.             }
  170.             // Note, no else
  171.             if (nodes[i].child00 != i) {
  172.                 // Not a leaf node
  173.                 Vector2 c = r.center;
  174.                 if (p.x > c.x) {
  175.                     if (p.y > c.y) {
  176.                         i = nodes[i].child11;
  177.                         r = Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax);
  178.                     }
  179.                     else {
  180.                         i = nodes[i].child10;
  181.                         r = Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y);
  182.                     }
  183.                 }
  184.                 else {
  185.                     if (p.y > c.y) {
  186.                         i = nodes[i].child01;
  187.                         r = Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax);
  188.                     }
  189.                     else {
  190.                         i = nodes[i].child00;
  191.                         r = Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y);
  192.                     }
  193.                 }
  194.             }
  195.         }
  196.     }
  197.  
  198.     public void Remove(LinkedListNode<T> node) {
  199.         allNodes.Remove(node);
  200.         SetDirty();
  201.     }
  202.  
  203.     public void Query(Vector2 p, float radius, List<T> results) {
  204.         if (dirty) {
  205.             Rebuild();
  206.         }
  207.  
  208.         QueryRec(0, p, radius, bounds, results);
  209.     }
  210.  
  211.     void QueryRec(int i, Vector2 p, float radius, Rect r, List<T> results) {
  212.         if (nodes[i].child00 == i) {
  213.             // Leaf node
  214.             if (nodes[i].linkedList != null) {
  215.                 for (int j = 0; j < nodes[i].linkedList.Count; j++) {
  216.                     var a = nodes[i].linkedList[j];
  217.  
  218.                     //Debug.DrawLine (a.transform.position, new Vector3(p.x,0,p.y),Color.black);
  219.                     float dist = (new Vector2(a.transform.position.x, a.transform.position.z) - p).sqrMagnitude;
  220.                     if (dist < radius * radius) {
  221.                         results.Add(a);
  222.                     }
  223.                 }
  224.             }
  225.         }
  226.         else {
  227.             // Not a leaf node
  228.             Vector2 c = r.center;
  229.             if (p.x - radius < c.x) {
  230.                 if (p.y - radius < c.y) {
  231.                     QueryRec(nodes[i].child00, p, radius, Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y), results);
  232.                 }
  233.                 if (p.y + radius > c.y) {
  234.                     QueryRec(nodes[i].child01, p, radius, Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax), results);
  235.                 }
  236.             }
  237.  
  238.             if (p.x + radius > c.x) {
  239.                 if (p.y - radius < c.y) {
  240.                     QueryRec(nodes[i].child10, p, radius, Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y), results);
  241.                 }
  242.                 if (p.y + radius > c.y) {
  243.                     QueryRec(nodes[i].child11, p, radius, Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax), results);
  244.                 }
  245.             }
  246.         }
  247.     }
  248.  
  249.     public void DebugDraw() {
  250.         DebugDrawRec(0, bounds);
  251.     }
  252.  
  253.     void DebugDrawRec(int i, Rect r) {
  254.         Debug.DrawLine(new Vector3(r.xMin, 0, r.yMin), new Vector3(r.xMax, 0, r.yMin), Color.white);
  255.         Debug.DrawLine(new Vector3(r.xMax, 0, r.yMin), new Vector3(r.xMax, 0, r.yMax), Color.white);
  256.         Debug.DrawLine(new Vector3(r.xMax, 0, r.yMax), new Vector3(r.xMin, 0, r.yMax), Color.white);
  257.         Debug.DrawLine(new Vector3(r.xMin, 0, r.yMax), new Vector3(r.xMin, 0, r.yMin), Color.white);
  258.  
  259.         if (nodes[i].child00 != i) {
  260.             // Not a leaf node
  261.             Vector2 c = r.center;
  262.             DebugDrawRec(nodes[i].child11, Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax));
  263.             DebugDrawRec(nodes[i].child10, Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y));
  264.             DebugDrawRec(nodes[i].child01, Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax));
  265.             DebugDrawRec(nodes[i].child00, Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y));
  266.         }
  267.  
  268.         if (nodes[i].linkedList != null) {
  269.             for (int j = 0; j < nodes[i].linkedList.Count; j++) {
  270.                 var a = nodes[i].linkedList[j];
  271.                 Debug.DrawLine(nodes[i].linkedList[0].transform.position + Vector3.up, a.transform.position + Vector3.up, new Color(1, 1, 0, 0.5f));
  272.             }
  273.         }
  274.     }
  275. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement