声明:参考https://blog.csdn.net/mobilebbki399/article/details/79491544和《游戏编程模式》
当场景元素过多时,需要实时的显示及隐藏物体使得性能提示,但是物体那么多,怎么知道哪些物体需要显示,哪些物体不需要显示的。当然,遍历物体判断该物体是否可以显示是最容易想到的方法,但是每次更新要遍历所有物体的代价很高,有没有其他可以替代的方法呢,当然有,四叉树就是其中一个方法。
假设场景是一维的,所有物体从左到右排成一条线,那么用二分法就可以快速找出距离自己一定范围内的物体。
同样四叉树的原理像二分一样,只是二分法处理的是一维世界, 四叉树处理的是二维世界,再往上三维世界用八叉树处理,这里用四叉树管理,八叉树暂时不讨论,原理类似。
这里先展示效果:
四叉树结构:
根节点是整个场景区域,然后分成四块:左上右上左下右下,分别作为根节点的儿子,然后每个儿子又分成四块重复之前步骤,这就是一棵四叉树。
每个节点保存四个儿子节点的引用,并且有存放在自己节点的物体列表,为什么物体不全部存放在叶子节点呢?因为有可能某个物体比较大,刚好在两个块的边界上。
这时候有两种做法:
1、这个物体同时插入两个节点的物体列表中
2、这个物体放在两个几点的父亲节点的物体列表中
第一种方法管理起来比较麻烦,所以在此采用第二种方法。
首先定义场景物体的数据类:
1 [System.Serializable] 2 public class ObjData 3 { 4 [SerializeField] 5 public string sUid;//独一无二的id,通过guid创建 6 [SerializeField] 7 public string resPath;//prefab路径 8 [SerializeField] 9 public Vector3 pos;//位置 10 [SerializeField] 11 public Quaternion rotation;//旋转 12 public ObjData(string resPath, Vector3 pos, Quaternion rotation) 13 { 14 this.sUid = System.Guid.NewGuid().ToString(); 15 this.resPath = resPath; 16 this.pos = pos; 17 this.rotation = rotation; 18 } 19 }
定义节点的接口:
1 public interface INode 2 { 3 Bounds bound { get; set; } 4 /// <summary> 5 /// 初始化插入一个场景物体 6 /// </summary> 7 /// <param name="obj"></param> 8 void InsertObj(ObjData obj); 9 /// <summary> 10 /// 当触发者(主角)移动时显示/隐藏物体 11 /// </summary> 12 /// <param name="camera"></param> 13 void TriggerMove(Camera camera); 14 void DrawBound(); 15 }
定义节点:
1 public class Node : INode 2 { 3 public Bounds bound { get; set; } 4 5 private int depth; 6 private Tree belongTree; 7 private Node[] childList; 8 private List<ObjData> objList; 9 10 public Node(Bounds bound, int depth, Tree belongTree) 11