最近在做一个塔防类小游戏,游戏世界设置为40*80的方格,每个单位占多个格子,单位所占区域相互不能重叠(Rect与Rect的重叠计算),考虑到世界是以2d为基础,所以最初想到的是“四叉树”,深度为3,则可以把世界分为4^3=64个叶子节点,将每个单位递归保存至与之重叠的叶子节点,以此实现了区域划分。在查找某个Rect是否有与之重叠的单位时,从Root节点依次向下深度查找。下图列举出某次查找的过程。
该图省略了Root节点的比较,直接从第二深度开始计算。第一次比较,取全图的1/4的左上角块与红色框进行重叠判定,发现有重叠,则进行第二步,第二步发现与红色无重叠,所以进行第三步…第五步时,与红色框发生了重叠,所以要进行下一个深度的查询,由于第三深度为叶子节点,且经过比较,叶子节点上所有的数据都不符合重叠判定,所以进行到第十步,开始右上1/4的节点树比较。按前面描述的过程发现第十五步时与红框且与蓝色区域发生了重叠,所以至此找到了重叠结果。
如上图所示情况,地图中只有一个数据,但是发生了15次查找,才最终查找到结果,如果使用顺序存储则只需要一次判定就可以拿到结果,当然四叉树本身就是用于大量单位的查找才能发挥其有效的作用。
以下是四叉树的代码:
using System.Collections.Generic;
using Pathfinding.Util;
using UnityEngine;
namespace Model
{
public class QuadTree
{
private Node root;
private int maxDepth = 3;
private Dictionary<IQuadTreeUnit, Node[]> _unitPos = new Dictionary<IQuadTreeUnit,Node[]>();
public QuadTree(Rect rect)
{
root = new Node(1, rect, null, this);
}
public void Update(IQuadTreeUnit unit)
{
var list = ListPool<Node>.Claim();
var r = unit.GetRect();
if (_unitPos.ContainsKey(unit))
{
var oldNodes = _unitPos[unit];
foreach (var oldNode in oldNodes)
{
oldNode.Remove(unit);
}
root.Add(unit, r, list);
}
else
{
root.Add(unit, r, list);
}
_unitPos[unit] = list.ToArray();
ListPool<Node>.Release(list);
}
public bool IsOverlaps(Rect rect)
{
return root.IsOverlaps(rect);
}
public IQuadTreeUnit[] Overlaps(Rect rect)
{
var list = ListPool<IQuadTreeUnit>.Claim();
root.Find(rect, list);
var res = list.ToArray();
ListPool<IQuadTreeUnit>.Release(list);
return res;
}
public IQuadTreeUnit Contain(Vector2 point)
{
return root.Contain(point);
}
public void Remove(IQuadTreeUnit unit)
{
if (_unitPos.ContainsKey(unit))
{
var oldNodes = _unitPos[unit];
foreach (var oldNode in oldNodes)
{
oldNode.Remove(unit);
}
_unitPos.Remove(unit);
}
}
public void OnDrawGizmos()
{
root.OnDrawGizmos();
}
public class Node
{
public int Id;
private int _depth;
private Rect _rect;
private QuadTree _tree;
private Node _parent;
private Node[] _children;
private UnOrderList<IQuadTreeUnit> _dataList = new UnOrderList<IQuadTreeUnit>();
public Node(int depth, Rect r, Node p, QuadTree belongTree)
{
this._rect = r;
this._tree = belongTree;
this._parent = p;
this._depth = depth;
if (depth <= belongTree.maxDepth)
{
_children = new Node[4];
for (int i = 0; i < 4; i++)
{
var xSymbol = (i == 1 || i == 2) ? 0.5f : 0;
var ySymbol = (i == 0 || i == 1) ? 0f : 0.5f;
var childRect = new Rect(_rect.position +
new Vector2(_rect.width * xSymbol, _rect.height * ySymbol)