HTML5实现3D和2D可视化QuadTree四叉树碰撞检测

QuadTree四叉树顾名思义就是树状的数据结构,其每个节点有四个孩子节点,可将二维平面递归分割子区域。QuadTree常用于空间数据库索引,3D的椎体可见区域裁剪,甚至图片分析处理,我们今天介绍的是QuadTree最常被游戏领域使用到的碰撞检测。采用QuadTree算法将大大减少需要测试碰撞的次数,从而提高游戏刷新性能,本文例子基于HT for Web的Canvas拓扑图和WebGL的3D引擎组件,通过GraphViewGraph3dView共享同一数据模型DataModel,同时呈现QuadTree算法下的2D和3D碰撞视图效果:http://v.youku.com/v_show/id_XODQyNTA1NjY0.html

http://www.hightopo.com/demo/QuadTree/ht-quadtree.html

Screen Shot 2014-12-06 at 12.41.24 AM

QuadTree的实现有很多成熟的版本,我选择的是 https://github.com/timohausmann/quadtree-js/ 四叉树的算法很简单,因此这个开源库也就两百来行代码。使用也非常简单,构建一个Quadtree对象,第一个参数传入rect信息制定游戏空间范围,在每次requestAnimationFrame刷新帧时,先通过quadtree.clear()清除老数据,通过quadtree.insert(rect)插入新的节点矩形区域,这样quadtree就初始化好了,剩下就是根据需要调用quadtree.retrieve(rect)获取指定矩形区域下,与其可能相交需要检测的矩形对象数组。

我构建了HT(http://www.hightopo.com/)的GraphViewGraph3dView两个组件,通过ht.widget.SplitView左右分割,由于两个视图都共享同一DataModel,因此我们剩下的关注点仅是对DataModel的数据操作,构建了200个ht.Node对象,每个对象的attr属性上保存了随机的运动方向vx和vy,同时保存了将要反复插入quadtree的矩形对象,这样避免每帧更新时反复创建对象,同时矩形对象也引用了ht.Node对象,用来当通过quadtree.retrieve(rect)获取需要检测的矩形对象时,我们能指定其所关联的ht.Node对象,因为我们需要对最终检测为碰撞的图元设置上红颜色的效果,也就是ht.Node平时显示默认的蓝色,当互相碰撞时将改变为红色。

需要注意从quadtree.retrieve(rect)获取需要检测的矩形对象数组中会包含自身图元,同时这些仅仅是可能会碰撞的图元,并不意味着已经碰撞了,由于我们例子是矩形,因此采用ht.Default.intersectsRect(r1, r2)最终判断是否相交,如果你的例子是圆形则可以采用计算两个圆心距离是否小于两个半径来决定是否相交,因此最终判断的标准根据游戏类型会有差异。

采用了QuadTree还是极大了提高了运算性能,否则100个图元就需要100*100次的监测,我这个例子场景下一般也就100*(10~30)的量:http://v.youku.com/v_show/id_XODQyNTA1NjY0.html

http://www.hightopo.com/demo/QuadTree/ht-quadtree.html

Screen Shot 2014-12-06 at 12.42.35 AM

除了碰撞检测外QuadTree算法还有很多有趣的应用领域,有兴趣可以玩玩这个 https://github.com/fogleman/Quads

Screen Shot 2014-12-06 at 12.52.17 AM

所有代码如下供参考:

1function init(){   2    d = 200; 3    speed = 8; 4    dataModel = new ht.DataModel();                                 5    g3d = new ht.graph3d.Graph3dView(dataModel);                                                   6    g2d = new ht.graph.GraphView(dataModel);                    7    mainSplit = new ht.widget.SplitView(g3d, g2d);                    8    mainSplit.addToDOM();                                         9    g2d.translate(300, 220);       10    g2d.setZoom(0.8, true); 11       12    for(var i=0; i<100; i++) { 13        var node = new ht.Node(); 14        node.s3(randMinMax(5, 30), 10, randMinMax(5, 30)); 15        node.p3(randMinMax(-d/2, d/2), 0, randMinMax(-d/2, d/2)); 16        node.s({ 17            'batch': 'group', 18            'shape': 'rect', 19            'shape.border.width': 1, 20            'shape.border.color': 'white', 21            'wf.visible': true, 22            'wf.color': 'white' 23        }); 24        node.a({ 25            vx: randMinMax(-speed, speed), 26            vy: randMinMax(-speed, speed), 27            obj: { 28                width: node.getWidth(), 29                height: node.getHeight(), 30                data: node 31            } 32        });                     33        dataModel.add(node); 34    }                 35    createShape([ 36        {x: -d, y: d}, 37        {x: d, y: d}, 38        {x: d, y: -d}, 39        {x: -d, y: -d}, 40        {x: -d, y: d} 41    ]);                    42    quadtree = new Quadtree({ x: -d, y: -d, width: d, height: d });                                 43    requestAnimationFrame(update); 44}                45function update() {    46    quadtree.clear();                 47    dataModel.each(function(data){ 48        if(!(data instanceof ht.Shape)){ 49            var position = data.getPosition(); 50            var vx = data.a('vx'); 51            var vy = data.a('vy'); 52            var w = data.getWidth()/2; 53            var h = data.getHeight()/2; 54            var x = position.x + vx; 55            var y = position.y + vy; 56            if(- w < -d){ 57                data.a('vx', -vx); 58                x = -+ w; 59            } 60            if(+ w > d){ 61                data.a('vx', -vx); 62                x = d - w; 63            } 64            if(- h < -d){ 65                data.a('vy', -vy); 66                y = -+ h; 67            } 68            if(+ h > d){ 69                data.a('vy', -vy); 70                y = d - h; 71            } 72            data.setPosition(x, y);                         73            var obj = data.a('obj'); 74            obj.x = x - w; 75            obj.y = y - h; 76             77            quadtree.insert(obj); 78            setColor(data, undefined); 79        } 80    });                 81    dataModel.each(function(data){ 82        if(!(data instanceof ht.Shape)){  83            var obj = data.a('obj'); 84            var objs = quadtree.retrieve(obj); 85            if(objs.length > 1){                             86                for(var i=0; i<objs.length; i++ ) { 87                    var data2 = objs[i].data; 88                    if(data === data2){ 89                        continue; 90                    } 91                    if(ht.Default.intersectsRect(obj, data2.a('obj'))){ 92                        setColor(data, 'red'); 93                        setColor(data2, 'red'); 94                    }                                 95                }                              96            } 97        } 98    }); 99    requestAnimationFrame(update);                     100}                         101function randMinMax(min, max) { 102    return min + (Math.random() * (max - min)); 103}                       104function createShape(points){ 105    shape = new ht.Shape(); 106    shape.setPoints(points); 107    shape.setThickness(4); 108    shape.setTall(10);                                 109    shape.s({ 110        'all.color': 'red', 111        'shape.background': null, 112        'shape.border.width': 2, 113        'shape.border.color': 'red'                     114    });                 115    dataModel.add(shape);  116    return shape; 117} 118function setColor(data, color){ 119    data.s({ 120        'all.color': color, 121        'shape.background': color 122    }); 123}
点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )