最近研究了一下四叉树的实现。

基本原理就不说了。

在线演示链接:https://timohausmann.de/quadtree.js/dynamic.html

个人觉得这个每一帧都要去清空并重建四叉树,效率不高。

源码:https://github.com/timohausmann/quadtree-js/blob/master/quadtree.js

 //remove duplicatesreturnObjects = returnObjects.filter(function(item, index) {return returnObjects.indexOf(item) >= index;});

源码里面重复添加节点以及返回结果的过滤,性能不好。

看了知乎上的另外一篇文章:https://zhuanlan.zhihu.com/p/180560098

觉得思路挺好的。

按照思路用ts实现了一遍,大概300行代码。初步测试结果看起来正常,1000个物体相互碰撞在5ms以内。

export class QPoint {public x:number;public y:number;public get w():number {return this.x;}public get h():number {return this.y;}constructor(x:number,y:number){this.x = x;this.y = y;}
}export class QRect {public origin:QPoint;public size:QPoint;public get centerX():number{ return this.origin.x + this.size.w * 0.5;}public get centerY():number{ return this.origin.y + this.size.h * 0.5;}public get xMin():number{return this.origin.x };public get yMin():number{return this.origin.y };public get xMax():number{return this.origin.x + this.size.w};public get yMax():number{return this.origin.y + this.size.h};public static intersects( rect:QRect, target:QRect):boolean{if( rect.yMin > target.yMax ){return false;}if( rect.yMax < target.yMin ){return false;}if( rect.xMax < target.xMin ){return false;}if( rect.xMin > target.xMax ) {return false;}return true;}constructor(origin:QPoint,size:QPoint){this.origin = origin;this.size = size;}}class LinkQuadTreeNode<T> {public next:LinkQuadTreeNode<T>;public treeNode:QuadTreeNode<T>;constructor(treeNode:QuadTreeNode<T> = null){this.treeNode = treeNode;}public copy(other:LinkQuadTreeNode<T>){this.next = other.next;this.treeNode = other.treeNode;}
}class DataNode<T> {public data:T;public rect:QRect;public depth:number;public lastLinkTreeInfo:LinkQuadTreeNode<T> = new LinkQuadTreeNode<T>();public curLinkTreeInfo:LinkQuadTreeNode<T> = new LinkQuadTreeNode<T>();constructor(data:T,depth:number,rect:QRect){this.data = data;this.depth = depth;this.rect = rect;}public get isLinkChange():boolean{let t1 = this.lastLinkTreeInfo.next;let t2 = this.curLinkTreeInfo.next;while(t2){if(t1 != t2){return true;}t1 = t1.next;t2 = t2.next;}return false;}
}class QuadTreeNode<T>{protected curDepth:number;protected nodesConut:number;protected originX:number;protected originY:number;protected treeNodes:QuadTreeNode<T>[] = [];protected children:DataNode<T>[] = [];protected rootTree:QuadTree<T>;private static looseRectTmp:QRect = new QRect(new QPoint(0,0),new QPoint(0,0));/*** 仅用来读*/public get looseRect():QRect{let size = this.rootTree.getSizeByDepth(this.curDepth);QuadTreeNode.looseRectTmp.origin.x = this.originX - size.w/2;QuadTreeNode.looseRectTmp.origin.y = this.originY - size.h/2;QuadTreeNode.looseRectTmp.size.x = size.x * 2;QuadTreeNode.looseRectTmp.size.y = size.y * 2;return QuadTreeNode.looseRectTmp;}public get isLeaf():boolean{ return this.rootTree.isMaxDepth(this.curDepth);}constructor(originX:number,originY:number,curDepth:number,rootTree:QuadTree<T>){this.originX = originX;this.originY = originY;this.curDepth = curDepth;this.rootTree = rootTree;this.nodesConut = 0;!this.isLeaf && this.splitQuadTree();}/*** 四叉树节点*/splitQuadTree(){let depth = this.curDepth+1;let size = this.rootTree.getSizeByDepth(depth);//left bottomlet treeNode = new QuadTreeNode<T>(this.originX,this.originY,depth,this.rootTree);this.treeNodes.push(treeNode);//right bottomtreeNode = new QuadTreeNode<T>(this.originX+size.w,this.originY,depth,this.rootTree);this.treeNodes.push(treeNode);//left uptreeNode = new QuadTreeNode<T>(this.originX,this.originY+size.h,depth,this.rootTree);this.treeNodes.push(treeNode);//right uptreeNode = new QuadTreeNode<T>(this.originX+size.w,this.originY+size.h,depth,this.rootTree);this.treeNodes.push(treeNode);}/*** 更新链接索引路径* @param centerX * @param centerY * @param maxDepth * @param linkInfo */updatePosInfo(centerX:number,centerY:number,maxDepth:number,linkInfo:LinkQuadTreeNode<T>){if(this.curDepth+1 > maxDepth){return;}let size = this.rootTree.getSizeByDepth(this.curDepth+1);let row = Math.min(1,Math.floor((centerY - this.originY) / size.h));row = Math.max(0,row);let col = Math.min(1,Math.floor((centerX - this.originX) / size.w));col = Math.max(0,col);let idx = row * 2 + col;let treeNode = this.treeNodes[idx];let nextInfo = new LinkQuadTreeNode<T>(treeNode);linkInfo.next = nextInfo;treeNode.updatePosInfo(centerX,centerY,maxDepth,nextInfo);}/*** 查询碰撞节点* @param rect * @param outItms 接收数组* @param outVisItms 访问过的节点*/quary(rect:QRect,outItms:T[],outVisItms:T[]){let queue:QuadTreeNode<T>[] = [];if(this.checkNeedVisit(rect)){queue.push(this);} let index = 0;while(index < queue.length){let treeNode = queue[index]for(let i=0 , n=treeNode.children.length ; i<n;i++){let dataNode = treeNode.children[i];outVisItms.push(dataNode.data);if(QRect.intersects(dataNode.rect,rect)){outItms.push(dataNode.data);}}if(!treeNode.isLeaf){for(let i=0, n = treeNode.treeNodes.length; i<n ; i++){if(treeNode.treeNodes[i].checkNeedVisit(rect)){queue.push(treeNode.treeNodes[i]);} }}index++;}}/*** 更新四叉树* @param item * @param rect */update(item:T,rect:QRect){let dataNode:DataNode<T> = item["$dataNode"];//插入if(!dataNode){let tmpDepth = this.rootTree.getDepthBySize(rect.size.w,rect.size.h);dataNode = new DataNode<T>(item,tmpDepth,rect);item["$dataNode"] = dataNode;}else{dataNode.rect = rect;}dataNode.curLinkTreeInfo.treeNode = this;dataNode.curLinkTreeInfo.next = null;this.updatePosInfo(dataNode.rect.centerX,dataNode.rect.centerY,dataNode.depth,dataNode.curLinkTreeInfo);if(dataNode.isLinkChange){this.remove(dataNode);this.insert(dataNode);dataNode.lastLinkTreeInfo.copy(dataNode.curLinkTreeInfo);}}/*** 插入节点* @param dataNode */insert(dataNode:DataNode<T>){let linkInfo = dataNode.curLinkTreeInfo;while(linkInfo && linkInfo.treeNode){linkInfo.treeNode.nodesConut ++;if(!linkInfo.next){linkInfo.treeNode.children.push(dataNode);}linkInfo = linkInfo.next;}}/*** 移除节点* @param dataNode */remove(dataNode:DataNode<T>){let linkInfo = dataNode.lastLinkTreeInfo;while(linkInfo && linkInfo.treeNode){linkInfo.treeNode.nodesConut --;if(!linkInfo.next){let index = linkInfo.treeNode.children.indexOf(dataNode);linkInfo.treeNode.children.splice(index, 1);}linkInfo = linkInfo.next;}}private checkNeedVisit(rect:QRect){return this.nodesConut > 0 &&  QRect.intersects(this.looseRect,rect);}
}/*** 松散网格型四叉树*/
export default class QuadTree<T>{public maxDepth:number;public gridMinWidth:number;public gridMinHeight:number;public depthSize:QPoint[] = [];protected quadTreeNode:QuadTreeNode<T>;public getSizeByDepth(dep:number):QPoint{return this.depthSize[dep];}public isMaxDepth(depth:number):boolean{return depth >= this.maxDepth -1;}private disDepth(width:number,height:number):number{let dw = Math.max(0,Math.floor(Math.log2(width / this.gridMinWidth + 1)));let dh = Math.max(0,Math.floor(Math.log2(height/ this.gridMinHeight + 1)));return Math.max(dw,dh);}public getDepthBySize(width:number,height:number):number{let disDepth = this.disDepth(width,height);return this.maxDepth - disDepth - 1;}/*** * @param originX 左下角x* @param originY 左下角y* @param conWidth 宽度* @param conHeight 高度* @param checkMinWidth 检测的物体的最小宽度*/constructor(originX:number,originY:number,conWidth:number,conHeight:number,checkMinWidth:number){this.gridMinWidth = checkMinWidth;this.gridMinHeight = checkMinWidth;this.maxDepth = this.disDepth(conWidth,conHeight);let tmp = Math.pow(2,this.maxDepth-1);this.gridMinWidth = conWidth / tmp;this.gridMinHeight = conHeight / tmp;//初始化层级sizefor(let i=0; i < this.maxDepth; i++){this.depthSize.push(new QPoint(conWidth/Math.pow(2,i),conHeight/Math.pow(2,i)));}this.quadTreeNode = new QuadTreeNode(originX,originY,0,this);}/*** 更新四叉树* @param item * @param rect */update(item:T,rect:QRect){this.quadTreeNode.update(item,rect);}/*** 查询碰撞节点* @param rect * @param outItms 接收数组* @param outVisItms 访问过的节点*/quary(rect:QRect,outItms:T[],outVisItms:T[]){this.quadTreeNode.quary(rect,outItms,outVisItms);}
}

效果:

CocosCreator工程源码:https://gitee.com/fuatnow/quad-tree.git

松散四叉树+网格法实现相关推荐

  1. 【物理篇】从零搭建2D物理系统②——用松散四叉树结合网格法来划分场景

    从一道字节跳动面试题说起 在开始今天内容之前,我想先讲一道前几天看到的字节跳动面试题: 玩家在场景中放了一个AOE技能,场景中有10万个敌人,如何知道AOE技能打到哪些敌人? 这道题的解法肯定不可能是 ...

  2. [笔记]松散四叉树,BVH,BSP,KD树的特性以及适用情况

    [笔记]松散四叉树,BVH,BSP,KD树的特性以及适用情况 松散四叉树 层次包围盒BVH 二叉空间分割(BSP) kd树 几种结构的比较 松散四叉树 比四叉树多了入口边界(也就是四叉树的边界)和出口 ...

  3. 空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)

    转载地址:https://www.cnblogs.com/KillerAery/p/10878367.html 1. 前言: 在游戏程序中,利用空间数据结构加速计算往往是非常重要的优化思想,空间数据结 ...

  4. 空间数据结构(四叉树/八叉树/BVH树/BSP树/k-d树)

    转载说明: 原作者:KillerAery 出处:https://www.cnblogs.com/KillerAery/p/10878367.html 1 四叉树/八叉树 (Quadtree/Octre ...

  5. 华科_图形学笔记_05_初探造型技术_02

    计算机图形学_华中科技大学_中国大学MOOC(慕课) 5.3.1_规则形体_01_边界表示 对于规则形体,我们有三类表示方法. 一个是边界表示,也就是用一组曲面或者是平面来描述物体的,这些面,将物体就 ...

  6. Mars3D中无人机航拍的数据想叠加到三维地图上,实现的流程和方法

    1. 3DTiles介绍 2016年,Cesium 团队借鉴传统2DGIS的地图规范:WMTS,借鉴图形学中的层次细节模型,打造出大规模的三维数据标准:3d-Tiles,中文译名:三维瓦片. 它在模型 ...

  7. 3D图形学(10):游戏中的加速渲染算法

    内容引自<Real Time Rendering 3rd> 一.空间数据结构(Spatial Data Structures) 空间数据结构(Spatial Data Structures ...

  8. 游戏场景管理(五)空间划分

    一 前言 空间划分算法有很多,比如均匀网格,四/八叉树,k-d树,Bsp树,每一种算法都有自己的优缺点,我们需要从理论上理解这些算法,然后在实际项目中进行灵活的运用. 游戏中经常使用空间划分算法来优化 ...

  9. 【笔记】unity大地图分块加载

    1.大地图分块加载     chunk的大小可动态调整     写工具做地块拆分         如果地块是由1个个小格子单元组成的,则可按位置进行划分保存成多个chunk预设         如果地 ...

  10. cesium之3D tiles格式介绍

    文章目录 1. 介绍 2. 文件扩展和MIME类型 3. JSON编码 4. URIs 5. 单位 6. 概念 6.1 坐标参考系 6.2 瓦片 6.2.1 几何误差 6.2.2 细化 6.2.2.1 ...

最新文章

  1. Annu. Rev. Genet:植物微生物组——系统性见解与展望
  2. 任我行二 - 高德地图篇1 - 显示地图
  3. 关于火狐中ashx中输出javascript一闪而过的问题
  4. 数据结构与算法——二分查找与二叉查找树汇总整理
  5. Single Responsibility Principle (SRP) - OO设计的单一职责原则
  6. Adobe产品在Lion上的已知问题
  7. unixlinux命令,20个 Unix/Linux 命令技巧
  8. JSP教程【2】JSP基本语法
  9. 如何查找共享计算机的用户名和密码错误,访问共享文件夹提示“未知的用户名或密码错误...
  10. 实战--接入最坑的支付宝
  11. 计算机教程无线路由器桥接上网,2个无线路由器桥接教程详细步骤
  12. python中turtle画树
  13. HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程
  14. Android接收读取短信内容
  15. Android中全局搜索(QuickSearchBox)详解(一)
  16. 节奏模仿练习——视唱练耳
  17. 什么网站适合高防服务器,什么叫高防服务器
  18. 850是什么意思_【铲车850代表什么意思】专区-850-铲车-铁甲网
  19. kali2020之快速搜索文件工具——安装篇
  20. Java 三大器之监听器Listener

热门文章

  1. [linux kernel] 内核下RX8025对接系统时钟
  2. 百度网盘python客户端——筑梦之路
  3. 【短时幅度谱】短时幅度谱估计在语音增强方面的MATLAB仿真
  4. 服务器硬盘scsi 最大容量,解读SCSI硬盘
  5. 拼接大屏数据展示_八步教会你如何制作数据可视化大屏
  6. java通用排序工具类
  7. 计算机病毒知识 360回答,计算机病毒与反病毒技术 课后习题答案
  8. 浅谈微信卡券功能开发(2)
  9. 宁夏政务网 紫图高拍仪控件和文件上传控件的若干问题及解决方法
  10. 线上幽灵:世界头号黑客米特尼克自传(体验头号黑客传奇人生,洞悉头号黑客思维模式!启明,绿盟,安天,安全宝,百度,腾讯,阿里……众安全专家一致推荐!)...