松散四叉树+网格法实现
最近研究了一下四叉树的实现。
基本原理就不说了。
在线演示链接: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
松散四叉树+网格法实现相关推荐
- 【物理篇】从零搭建2D物理系统②——用松散四叉树结合网格法来划分场景
从一道字节跳动面试题说起 在开始今天内容之前,我想先讲一道前几天看到的字节跳动面试题: 玩家在场景中放了一个AOE技能,场景中有10万个敌人,如何知道AOE技能打到哪些敌人? 这道题的解法肯定不可能是 ...
- [笔记]松散四叉树,BVH,BSP,KD树的特性以及适用情况
[笔记]松散四叉树,BVH,BSP,KD树的特性以及适用情况 松散四叉树 层次包围盒BVH 二叉空间分割(BSP) kd树 几种结构的比较 松散四叉树 比四叉树多了入口边界(也就是四叉树的边界)和出口 ...
- 空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)
转载地址:https://www.cnblogs.com/KillerAery/p/10878367.html 1. 前言: 在游戏程序中,利用空间数据结构加速计算往往是非常重要的优化思想,空间数据结 ...
- 空间数据结构(四叉树/八叉树/BVH树/BSP树/k-d树)
转载说明: 原作者:KillerAery 出处:https://www.cnblogs.com/KillerAery/p/10878367.html 1 四叉树/八叉树 (Quadtree/Octre ...
- 华科_图形学笔记_05_初探造型技术_02
计算机图形学_华中科技大学_中国大学MOOC(慕课) 5.3.1_规则形体_01_边界表示 对于规则形体,我们有三类表示方法. 一个是边界表示,也就是用一组曲面或者是平面来描述物体的,这些面,将物体就 ...
- Mars3D中无人机航拍的数据想叠加到三维地图上,实现的流程和方法
1. 3DTiles介绍 2016年,Cesium 团队借鉴传统2DGIS的地图规范:WMTS,借鉴图形学中的层次细节模型,打造出大规模的三维数据标准:3d-Tiles,中文译名:三维瓦片. 它在模型 ...
- 3D图形学(10):游戏中的加速渲染算法
内容引自<Real Time Rendering 3rd> 一.空间数据结构(Spatial Data Structures) 空间数据结构(Spatial Data Structures ...
- 游戏场景管理(五)空间划分
一 前言 空间划分算法有很多,比如均匀网格,四/八叉树,k-d树,Bsp树,每一种算法都有自己的优缺点,我们需要从理论上理解这些算法,然后在实际项目中进行灵活的运用. 游戏中经常使用空间划分算法来优化 ...
- 【笔记】unity大地图分块加载
1.大地图分块加载 chunk的大小可动态调整 写工具做地块拆分 如果地块是由1个个小格子单元组成的,则可按位置进行划分保存成多个chunk预设 如果地 ...
- 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 ...
最新文章
- Annu. Rev. Genet:植物微生物组——系统性见解与展望
- 任我行二 - 高德地图篇1 - 显示地图
- 关于火狐中ashx中输出javascript一闪而过的问题
- 数据结构与算法——二分查找与二叉查找树汇总整理
- Single Responsibility Principle (SRP) - OO设计的单一职责原则
- Adobe产品在Lion上的已知问题
- unixlinux命令,20个 Unix/Linux 命令技巧
- JSP教程【2】JSP基本语法
- 如何查找共享计算机的用户名和密码错误,访问共享文件夹提示“未知的用户名或密码错误...
- 实战--接入最坑的支付宝
- 计算机教程无线路由器桥接上网,2个无线路由器桥接教程详细步骤
- python中turtle画树
- HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程
- Android接收读取短信内容
- Android中全局搜索(QuickSearchBox)详解(一)
- 节奏模仿练习——视唱练耳
- 什么网站适合高防服务器,什么叫高防服务器
- 850是什么意思_【铲车850代表什么意思】专区-850-铲车-铁甲网
- kali2020之快速搜索文件工具——安装篇
- Java 三大器之监听器Listener
热门文章
- [linux kernel] 内核下RX8025对接系统时钟
- 百度网盘python客户端——筑梦之路
- 【短时幅度谱】短时幅度谱估计在语音增强方面的MATLAB仿真
- 服务器硬盘scsi 最大容量,解读SCSI硬盘
- 拼接大屏数据展示_八步教会你如何制作数据可视化大屏
- java通用排序工具类
- 计算机病毒知识 360回答,计算机病毒与反病毒技术 课后习题答案
- 浅谈微信卡券功能开发(2)
- 宁夏政务网 紫图高拍仪控件和文件上传控件的若干问题及解决方法
- 线上幽灵:世界头号黑客米特尼克自传(体验头号黑客传奇人生,洞悉头号黑客思维模式!启明,绿盟,安天,安全宝,百度,腾讯,阿里……众安全专家一致推荐!)...