基于四叉树的场景管理

编程入门 行业动态 更新时间:2024-10-25 12:16:34

基于四叉树的<a href=https://www.elefans.com/category/jswz/34/1770727.html style=场景管理"/>

基于四叉树的场景管理

一、解决的问题

在游戏地图中有很多对象,根据视野范围内的区域,并把这些区域的对象显示出来,其它不在视野范围的不显示。

效果如下:

二、四叉树原理

在数据结构中,树常常用于层级管理,就像我们国家行政单位一样,从国家-->省-->市-->县(区)-->街道(村)

这样每个人属于哪个地方就很清楚了。

同样我们在游戏场景中也可以对游戏地图做类似的分类,基于地图的形状样式,使用四分法会比较方便,且层次又不会太多,所以我们选用四叉树来对场景进行管理。

三、使用四叉树创建地图分区

1.先定义一个完整的树

maxChildCount 即为使用四叉树,maxDepth 为我们树划分层数,同时使用Bounds把划分的区域绘制出来。

public class Tree : INode
{public Bounds bound { get; set; }//包围盒private Node root;public int maxDepth { get; set; } //最大深度public int maxChildCount { get; set; }//最大叶子数public Tree(Bounds bound)//构造函数 构造树{this.bound = bound;this.maxDepth = 5;this.maxChildCount = 4;root = new Node(bound, 0, this);}public void InsertObj(ObjData obj){root.InsertObj(obj);}public void TriggerMove(Camera camera){root.TriggerMove(camera);}public void DrawBound(){root.DrawBound();}
}

2.定义节点

objList用来管辖当前节点的物体

这里使用InsertObj() 的递归来把物体逐步分到底层节点,当一个物体同时属于两个或多个子节点,则划到当前节点。

public class Node : INode
{public Bounds bound { get; set; }//包围盒private int depth;//深度private Tree belongTree;//所属子树private Node[] childList;//当前树节点的子节点private List<ObjData> objList;//节点中的物体public Node(Bounds bound, int depth, Tree belongTree){this.belongTree = belongTree;this.bound = bound;this.depth = depth;//childList = new Node[belongTree.maxChildCount];objList = new List<ObjData>();}public void InsertObj(ObjData obj)//插入对象{Node node = null;bool bChild = false;if(depth < belongTree.maxDepth && childList == null)//如果深度小于数的最大子节点数    并且没有子节点{//如果还没到叶子节点,可以拥有儿子且儿子未创建,则创建儿子CerateChild();}if(childList != null){for (int i = 0; i < childList.Length; ++i){Node item = childList[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{objList.Add(obj);}}public void TriggerMove(Camera camera){//刷新当前节点for(int i = 0; i < objList.Count; ++i){//进入该节点中意味着该节点在摄像机内,把该节点保存的物体全部创建出来ResourcesManager.Instance.LoadAsync(objList[i]);}if(depth == 0){ResourcesManager.Instance.RefreshStatus();}//刷新子节点if (childList != null){for(int i = 0; i < childList.Length; ++i){if (childList[i].bound.CheckBoundIsInCamera(camera)){childList[i].TriggerMove(camera);}}}}private void CerateChild()//创建孩子{childList = 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);childList[index++] = new Node(cBound, depth + 1, belongTree);}}}public void DrawBound()//绘制包围盒{if(objList.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(childList != null)//孩子节点也是一样的绘制{for(int i = 0; i < childList.Length; ++i){childList[i].DrawBound();}}}
}

3.场景启动类

objList为场景中所有的物体

[System.Serializable]
public class Main : MonoBehaviour
{[SerializeField]public List<ObjData> objList = new List<ObjData>();//可观察物体列表public Bounds mainBound;//主包围盒(最大的范围)private Tree tree;//构建树private bool bInitEnd = false;//是否初始化完成private Role role;//人物(主角)public bool isDebug;public void Awake(){tree = new Tree(mainBound);//创建树根节点for(int i = 0; i < objList.Count; ++i)//把可观察的这些物体都插入到对应的树节点去{tree.InsertObj(objList[i]);}role = GameObject.Find("Role").GetComponent<Role>();bInitEnd = true;}private void Update(){if (role.bMove){tree.TriggerMove(role.mCamera);}}private void OnDrawGizmos(){if (!isDebug){return; }if (bInitEnd){tree.DrawBound();}else{Gizmos.DrawWireCube(mainBound.center, mainBound.size);//画出一个最大的包围盒}}}

四、判断区域是否在相机范围内

对bound来说,它有8个点,当它的8个点同时处于摄像机裁剪块上方/下方/前方/后方/左方/右方,那么该bound不与摄像机可视范围交叉

public static class Expand 
{public static bool CheckBoundIsInCamera(this 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;}
}

 

更多推荐

基于四叉树的场景管理

本文发布于:2024-03-10 21:42:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1729107.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:场景   四叉树

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!