介绍
当场景元素过多时,需要实时的显示及隐藏物体使得性能提示,但是物体那么多,怎么知道哪些物体需要显示,哪些物体不需要显示的。当然,遍历物体判断该物体是否可以显示是最容易想到的方法,但是每次更新要遍历所有物体的代价很高。
假设场景是一维的,所有物体从左到右排成一条线,那么用二分法就可以快速找出距离自己一定范围内的物体。
同样四叉树的原理像二分一样,只是二分法处理的是一维世界, 四叉树处理的是二维世界,再往上三维世界用八叉树处理。
根节点是整个场景区域,然后分成四块:左上右上左下右下,分别作为根节点的儿子,然后每个儿子又分成四块重复之前步骤,这就是一棵四叉树。对于某些物体比较大,刚好在多个块的边界上,将这些物体放在所属块的父亲节点上去管理。
实战

例子运行图
QuadTree.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| using System.Collections.Generic; using UnityEngine;
public interface INode { Bounds Bound { get; set; } void InsertObj(SceneGoSaveInfo obj); void TriggerMove(Camera camera); void DrawBound(); }
public class Node : INode { public Bounds Bound { get; set; } private int depth; private QuadTree belongTree; private Node[] arrChildNode; private List<SceneGoSaveInfo> listSaveInfo; public Node(Bounds bound, int depth, QuadTree belongTree) { this.belongTree = belongTree; this.Bound = bound; this.depth = depth; listSaveInfo = new List<SceneGoSaveInfo>(); } public void InsertObj(SceneGoSaveInfo obj) { Node node = null; bool bChild = false; if(depth < belongTree.MaxDepth && arrChildNode == null) { CerateChild(); } if(arrChildNode != null) { for (int i = 0; i < arrChildNode.Length; ++i) { Node item = arrChildNode[i]; if (item == null) { break; } if (item.Bound.Contains(obj.Pos)) { if (node != null) { bChild = false; break; } node = item; bChild = true; } } } if (bChild) { node.InsertObj(obj); } else { listSaveInfo.Add(obj); } }
public void TriggerMove(Camera camera) { for(int i = 0; i < listSaveInfo.Count; ++i) { ResourcesManager.Instance.LoadAsync(listSaveInfo[i]); }
if(depth == 0) { ResourcesManager.Instance.RefreshStatus(); }
if (arrChildNode != null) { for(int i = 0; i < arrChildNode.Length; ++i) { if (Help.IsBoundInCameraView(arrChildNode[i].Bound, camera)) { arrChildNode[i].TriggerMove(camera); } } } } private void CerateChild() { arrChildNode = new Node[belongTree.MaxChildCount]; int index = 0; for(int i = -1; i <= 1; i+=2) { for(int j = -1; j <= 1; j+=2) { Vector3 centerOffset = new Vector3(Bound.size.x / 4 * i, 0, Bound.size.z / 4 * j); Vector3 cSize = new Vector3(Bound.size.x / 2, Bound.size.y, Bound.size.z / 2); Bounds cBound = new Bounds(Bound.center + centerOffset, cSize); arrChildNode[index++] = new Node(cBound, depth + 1, belongTree); } } } public void DrawBound() { if(listSaveInfo.Count != 0) { Gizmos.color = Color.blue; Gizmos.DrawWireCube(Bound.center, Bound.size - Vector3.one*0.1f); } else { Gizmos.color = Color.green; Gizmos.DrawWireCube(Bound.center, Bound.size - Vector3.one * 0.1f); } if(arrChildNode != null) { for(int i = 0; i < arrChildNode.Length; ++i) { arrChildNode[i].DrawBound(); } } } }
public class QuadTree : INode { public Bounds Bound { get; set; } public int MaxDepth { get; } public int MaxChildCount { get; } private Node root;
public QuadTree(Bounds bound) { this.Bound = bound; this.MaxDepth = 5; this.MaxChildCount = 4; root = new Node(bound, 0, this); }
public void InsertObj(SceneGoSaveInfo obj) { root.InsertObj(obj); }
public void TriggerMove(Camera camera) { root.TriggerMove(camera); }
public void DrawBound() { root.DrawBound(); } }
|
ResourcesManager.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public enum SceneObjStatus { OLD, LOADING, NEW, }
public class SceneObj { public SceneGoSaveInfo SaveInfo; public SceneObjStatus Status; public GameObject GoInst;
public SceneObj(SceneGoSaveInfo data) { this.SaveInfo = data; this.GoInst = null; } }
public class ResourcesManager : MonoBehaviour { public static ResourcesManager Instance;
public float delTime = 2; private List<string> listTmpDeleteUid = new List<string>();
private Dictionary<string, SceneObj> dicActiveObj = new Dictionary<string, SceneObj>();
private Dictionary<string, SceneObj> dicInactiveObj = new Dictionary<string, SceneObj>();
private void Awake() { Instance = this; }
private void OnEnable() { StartCoroutine(IEDelete()); }
private IEnumerator IEDelete() { while (true) { foreach(var pair in dicInactiveObj) { Destroy(pair.Value.GoInst); } dicInactiveObj.Clear(); yield return new WaitForSeconds(delTime); } } private IEnumerator IELoad(SceneGoSaveInfo obj) { SceneObj sceneObj = new SceneObj(obj); sceneObj.Status = SceneObjStatus.LOADING; dicActiveObj.Add(obj.Uid, sceneObj); GameObject resObj = null; ResourceRequest request = Resources.LoadAsync<GameObject>(obj.Path); yield return request; resObj = request.asset as GameObject; CreateObj(resObj, sceneObj); sceneObj.Status = SceneObjStatus.NEW; }
public SceneObj CheckIsActive(string sUid) { SceneObj obj; if(dicActiveObj.TryGetValue(sUid, out obj)) { return obj; } return null; }
private bool MoveToActive(SceneGoSaveInfo obj) { SceneObj sceneObj; if (dicInactiveObj.TryGetValue(obj.Uid, out sceneObj)) { sceneObj.GoInst.SetActive(true); sceneObj.Status = SceneObjStatus.NEW; dicActiveObj.Add(obj.Uid, sceneObj); dicInactiveObj.Remove(obj.Uid); return true; } return false; }
public bool MoveToInActive(SceneGoSaveInfo obj) { SceneObj sceneObj; if (dicActiveObj.TryGetValue(obj.Uid, out sceneObj)) { sceneObj.GoInst.SetActive(false); dicInactiveObj.Add(obj.Uid, sceneObj); dicActiveObj.Remove(obj.Uid); return true; } return false; }
private void CreateObj(GameObject prefab, SceneObj sceneObj) { sceneObj.GoInst = Instantiate(prefab); sceneObj.GoInst.transform.position = sceneObj.SaveInfo.Pos; sceneObj.GoInst.transform.rotation = sceneObj.SaveInfo.Rotation; } public void LoadAsync(SceneGoSaveInfo obj) { if (CheckIsActive(obj.Uid) != null) { return; } if (!MoveToActive(obj)) { StartCoroutine(IELoad(obj)); } }
public void RefreshStatus() { listTmpDeleteUid.Clear(); foreach (var pair in dicActiveObj) { SceneObj sceneObj = pair.Value; if(sceneObj.Status == SceneObjStatus.OLD) { listTmpDeleteUid.Add(pair.Key); } else if(sceneObj.Status == SceneObjStatus.NEW) { sceneObj.Status = SceneObjStatus.OLD; } } for(int i = 0; i < listTmpDeleteUid.Count; ++i) { MoveToInActive(dicActiveObj[listTmpDeleteUid[i]].SaveInfo); } } }
|
Help.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| using UnityEngine;
public static class Help { public static bool IsBoundInCameraView(Bounds bound, Camera camera) { System.Func<Vector4, int> ComputeOutCode = (projectionPos) => { int _code = 0; if (projectionPos.x < -projectionPos.w) _code |= 1; if (projectionPos.x > projectionPos.w) _code |= 2; if (projectionPos.y < -projectionPos.w) _code |= 4; if (projectionPos.y > projectionPos.w) _code |= 8; if (projectionPos.z < -projectionPos.w) _code |= 16; if (projectionPos.z > projectionPos.w) _code |= 32; return _code; };
Vector4 worldPos = Vector4.one; int code = 63; for (int i = -1; i <= 1; i += 2) { for (int j = -1; j <= 1; j += 2) { for (int k = -1; k <= 1; k += 2) { worldPos.x = bound.center.x + i * bound.extents.x; worldPos.y = bound.center.y + j * bound.extents.y; worldPos.z = bound.center.z + k * bound.extents.z;
code &= ComputeOutCode(camera.projectionMatrix * camera.worldToCameraMatrix * worldPos); } } } return code == 0 ? true : false; } }
|
代码地址 https://gitee.com/hahafox_0/unity-example-scene-quad-tree