基于四叉树2D碰撞检测以及D3简单分析

敏捷侠
• 阅读 4833

前言

《数据结构-使用JS实现四叉树》 上文中简单介绍了四叉树的一些实现和应用场景 本篇文章应评论区各位小伙伴的留言 基于四叉树实现一下2D的碰撞检测。

话不多说开始今天的内容。

首先看下quadtree测试效果图

基于四叉树2D碰撞检测以及D3简单分析

正文coding部分

实现思路

  • 数据结构采取四叉树
  • 碰撞节点比对进行四叉树查找

    sample采取canvas2d进行绘制,JavaScript进行实现。

实现过程

定义quadtree数据结构
/**
    *四叉树构造函数
    *@四叉树 2D类碰撞 需要四叉树边界/节点层数/
    *@param{Rect}节点的边界({x,y,width,height})
    *@param{number}[max\u objects=10]在拆分为4个子节点之前,节点可以容纳的最大对象数 默认:4
    *@param{number}[max\u levels=4]根四叉树内的总最大级别 默认为 4
    *@param{number}[level=0] 深度级别,子节点需要 默认:0  初始深度
*/
function Quadtree(bounds, max_objects, max_levels, level) {
        
        this.max_objects    = max_objects || 4;
        this.max_levels     = max_levels || 4;
        
        this.level  = level || 0;
        this.bounds = bounds;
        
        this.objects    = [];
        this.nodes      = [];
 };

添加objects/如果超出max_objects那么就拆分子节点

/*
    *将对象插入节点。如果节点
    *超过容量时,将拆分并添加所有
    *对象到其相应的子节点。
    *@param{Rect}要添加的对象的pRect边界({x,y,width,height})
    *@memberof四叉树
*/
Quadtree.prototype.insert = function(pRect) {

    var i = 0,
        indexes;

    //如果存在子节点,那么找到子节点像子节点添加数据(子节点就是子树)
    if(this.nodes.length) {
        indexes = this.getIndex(pRect);

        for(i=0; i<indexes.length; i++) {
            this.nodes[indexes[i]].insert(pRect);     
        }
        return;
    }

    //如果没找到不存在nodes 向objects添加
    this.objects.push(pRect);

    // 如果objects超出最大max_objects 那么就拆分
    // 开始
    if(this.objects.length > this.max_objects && this.level < this.max_levels) {

        //拆分子节点 (split方法可以移步仓库看代码,很简单。后续不贴了)
        if(!this.nodes.length) {
            this.split();
        }

        //将所有objects添加到对应的nodes 
        for(i=0; i<this.objects.length; i++) {
            indexes = this.getIndex(this.objects[i]); //找到对应的nodes
            for(var k=0; k<indexes.length; k++) {
                this.nodes[indexes[k]].insert(this.objects[i]);
            }
        }

        //清空这个objects
        this.objects = [];
        // 超出max_objects逻辑结束
    }
 };

获取

/**
    *确定对象属于哪个节点
    *@param{Rect}要检查的区域的pRect边界({x,y,width,height})
    *@return{number[]}相交子节点的索引数组(0-3=右上、左上、左下、右下)
    *@memberof四叉树
*/
Quadtree.prototype.getIndex = function(pRect) {
    
    var indexes = [],
        verticalMidpoint    = this.bounds.x + (this.bounds.width/2), //x中心
        horizontalMidpoint  = this.bounds.y + (this.bounds.height/2);    //y中心
    // 判断属于哪个区域
    var startIsNorth = pRect.y < horizontalMidpoint,
        startIsWest  = pRect.x < verticalMidpoint,
        endIsEast    = pRect.x + pRect.width > verticalMidpoint,
        endIsSouth   = pRect.y + pRect.height > horizontalMidpoint;    

    //右上 quad
    if(startIsNorth && endIsEast) {
        indexes.push(0);
    }
    
    //左上 quad
    if(startIsWest && startIsNorth) {
        indexes.push(1);
    }

    //左下 quad
    if(startIsWest && endIsSouth) {
        indexes.push(2);
    }

    //右下 quad
    if(endIsEast && endIsSouth) {
        indexes.push(3);
    }
    return indexes;
};

给定一个rect结构进行碰撞

/**
    *返回所有可能与给定对象碰撞的对象
    *@param{Rect}要检查的对象的pRect边界({x,y,width,height})
    *@return{Rect[]}数组,包含所有检测到的对象
    *@memberof四叉树
*/
Quadtree.prototype.retrieve = function(pRect) {

    var indexes = this.getIndex(pRect),
        returnObjects = this.objects;

    //if we have subnodes, retrieve their objects
    if(this.nodes.length) {
        for(var i=0; i<indexes.length; i++) {
            returnObjects = returnObjects.concat(this.nodes[indexes[i]].retrieve(pRect));
        }
    }

    //objects去重
    returnObjects = returnObjects.filter(function(item, index) {
        return returnObjects.indexOf(item) >= index;
    });

    return returnObjects;
};

清除四叉树

/**
    * 清除四叉树
    * @memberof Quadtree
*/
Quadtree.prototype.clear = function() {
    
    this.objects = [];
 
    for(var i=0; i < this.nodes.length; i++) {
        if(this.nodes.length) {
            this.nodes[i].clear();
          }
    }

    this.nodes = [];
};

quadtree其他相关仓库 D3 Quadtree

拿D3来做一下学习,首先实现更为全面(可以看下面截图里具体的一个结构模块)有add cover,data,find.... 而且细节做的很棒,值得学习。下面简单举个例子

基于四叉树2D碰撞检测以及D3简单分析

就拿一个模块来说: find.js 也是寻找相应的nodes 然后追加 同样的实现,区别在于可以理解为访问做了记录/不访问重复点,而且有就近访问原则(这个可以做一些其他工作)i = (y >= ym) << 1 | (x >= xm),有兴趣的可以去学习一下。
基于四叉树2D碰撞检测以及D3简单分析

总的来说D3内部存在一些能够帮助到你思维转变提升的东西,学习还是很要必要的。对了, 实践过程中可以尝试和d3-force模块结合使用,效果更佳。

结语

四叉树作为树的一种结构,上面的例子很好的体现了四叉树在2D碰撞的实践,有任何疑问请随时留言。

最后~ 托更一天本人感到非常抱歉!!!

其他链接

上述示例代码仓库

D3 Quadtree

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
敏捷侠
敏捷侠
Lv1
保持对生活的爱和热忱,把每一天活得热气腾腾。
文章
4
粉丝
0
获赞
0