前言
大家好,我IT侠又双叒叕来了,上一篇文章说了下ECS框架在H5下是如何使用的。本篇文章就来聊聊主角-四叉树。
还是在开头说一下:
-
不了解四叉树的可以自行查阅一下相关资料。因为篇幅问题,IT侠会下方简单的介绍下。
-
因为demo是基于ECS框架,所以如果不了解ECS框架的话,可以看看我的ECS框架系列文章,获取我已经开源的ECS框架。或者扫文末的二维码联系我,给我“加个鸡腿”支持一下!19.9 交个朋友。
正文
最终效果
先给大家看看demo运行起来的效果,看看四叉树在对象很多的情况下的威力
- 每一个"田字"矩形是一个4叉树节点,方便调试的使用
- 高亮黄色的就是需要检查是否与主角发生碰撞的块
- 高亮紫色的就是当前和主角发生碰撞的块
生成了超级多块后四叉树的表现
最后是关闭了调试线后的效果
四叉树
四叉树(quad-tree)是一种数据结构,是一种每个节点最多有四个子树的数据结构。四叉树常应用于二维空间数据的分析与分类。所以把四叉树应用在二维的有效率之碰撞侦测上是非常适合的。
代码实现
源码里的方法我就不一一列举出来了,因为四叉树的实现不是本文的重点,只介绍下四叉树构造函数,还有包含哪些方法,以及方法的简单解释。github上开源很多四叉树的实现,如果想深入了解可以自行查阅学习。
constructor(bounds: IBounds, maxObjects?: number, maxLevels?: number, level?: number,location?:number,parent?:QuadTree) {
// 树节点的边界(包围盒)
this.bounds = bounds;
// 节点上最多存放物体的数量,为了测试默认设置为3,具体设定开发者可以自行决定
this.maxObjects = maxObjects || 3;
// 4叉树的最大层级
this.maxLevels = maxLevels || 4;
// 当前节点在树中的层级
this.level = level || 0;
// 当前节点上所有对象
this.objs = [];
//4个象限(节点),如果该节点上的对象超过设定的最大值后就进行分裂
this.nodes = [];
//标记一下当前节点是那个象限
this.location = location;
// 当前节点的父节点,方便后续删除子树
this.parent = parent || null;
}
四叉树类有以下成员方法组成:
- getIndex 返回当前对象在哪些象限上
- insert 将一个对象加入到当前节点上
- clear 清空树上所有节点和节点上的对象
- recovery 如果一个子节点上没有任何对象,那么进行一次初始化
- split分裂子树节点,生成4个叶子节点(象限)
- retrieve 获取树上可能与指定对象发生碰撞的对象
- getBounds 获取所有节点的包围盒,方便调试
- toJSON 转成json格式,方便调试输出树信息
ECS中使用四叉树
当有Canvas组件的实体产生时候,捕获该实体,创建一个四叉树,根节点包围盒就用canvas节点的位置和宽高。然后将四叉树对象保存到ecs对象上方便其他系统访问。
_canvas.on("Canvas", function (e) {
//获取Canvas组件上的canvas属性
//该属性 保存的就是canvas对象
let canvas = e.Canvas.canvas;
// 创建一个四叉树
toy.qt = new toy.QuadTree(
{
x: canvas.width / 2,
y: canvas.height / 2,
width: canvas.width,
height: canvas.height
});
});
接下来写一个系统"关心一下"带有 “Mob”,“Appearance”,“Props” 这3个组件的实体的产生。因为有这3个组件的实体我们认定是一个怪物,怪物会和主角发生碰撞,所以需要放进四叉树中等待提取。
调用四叉树实例上的 insert 方法把怪物的包围盒和怪物实体一起打包到一个对象上保存进四叉树上的节点中。
_character.on(["Mob",