javascript四叉树辅助二维平面碰撞检测
有个项目需要处理平面内物体碰撞问题。
百度了一下,发现网上二维平面碰撞检测这方面资料并不是很多。
很多资料都提到四叉树,但讲原理的多,给代码的少。我以为四叉树能直接进行碰撞检测呢,原来只是把屏幕区域生成一个四叉树,用来减少碰撞检测的次数。
然后我在github上找了一个示例项目,但思路与网上讲的有些不一样。
一般提到四叉树是这样的,一个区域内的物体数量超过一定数量后,则将这个区域分成四个子区域,并这个区域内的物体重新分配到子区域。为了防止出现递归层数太多,需要限定整棵树的层数。
github上的这个项目的思路是,先限定整棵树的深度,如果物体加入,则直接将这个物体插入到这棵树的叶子区域。
我觉得前者的思路肯定是更加合理的,所以自己把代码重写了一遍,代码如下,测试无问题:
1 export class QuadTree { 2 constructor(x, y, width, height, depth) { 3 this.x = x 4 this.y = y 5 this.width = width 6 this.height = height 7 this.sprites = [] 8 this.children = [] 9 this.leaf = true //是不是叶子节点,默认是叶子 10 this.depth = depth 11 } 12 13 // sprite {num,x,y,width,height} 14 insert(sprite) { 15 if (this._intersects(sprite)) { 16 if (this.leaf) { 17 this.sprites.push(sprite) 18 if (this.sprites.length > 2 && this.depth < 5) { 19 // 如果区域内超过两个物体,重新划分区域 20 this._divide() 21 // 将物体重新分配到各个区域 注意:一个物体可能出现在两个区域 22 for (let i = 0; i < this.sprites.length; i++) { 23 let sp = this.sprites[i] 24 for (let j = 0; j < this.children.length; j++) { 25 let quad = this.children[j] 26 if (quad._intersects(sp)) { 27 quad.insert(sp) 28 } 29 } 30 } 31 this.leaf = false // 不再是叶子 32 this.sprites.length = 0 // 清空物体 33 } 34 } else { 35 // 如果不是叶子,把物体往下分配 36 for (let j = 0; j < this.children.length; j++) { 37 let quad = this.children[j] 38 if (quad._intersects(sprite)) { 39 quad.insert(sprite) 40 } 41 } 42 } 43 } 44 } 45 46 // 返回一个可能出现碰撞的区域,是一个生成器。原代码是用reduce方法将整棵树拍平成一个数组再遍历数组进行处理,我觉得用生成器肯定是更优雅的 47 * possibles() { 48 if (this.leaf) { 49 if (this.sprites.length > 1) 50 yield this 51 } else { 52 for (let i = 0; i < this.children.length; i++) { 53 for (let q of this.children[i].possibles()) { 54 yield q 55 } 56 } 57 } 58 // console.log(Date.now()) 59 } 60 61 * spriteList() { 62 if (this.leaf) { 63 for (let i = 0; i < this.sprites.length; i++) { 64 yield this.sprites[i] 65 } 66 } else { 67 for (let i = 0; i < this.children.length; i++) { 68 for (let sp of this.children[i].spriteList()) { 69 yield sp 70 } 71 } 72 } 73 } 74 75 // 相交检测:检测方块与当前区域是否相互 76 _intersects(sprite) { 77 let sx = sprite.x + sprite.width / 2, 78 sy = sprite.y + sprite.height / 2, 79 tx = this.x + this.width / 2, 80 ty = this.y + this.height / 2 81 // console.log('sprite',sprite.x,sprite.y,sprite.width,sprite.height) 82 // console.log('quadTree',this.x,this.y,this.width,this.height) 83 return Math.abs(tx - sx) < (this.width + sprite.width) / 2 && 84 Math.abs(ty - sy) < (this.height + sprite.height) / 2 85 } 86 87 // 将当前区域划分成4个区域,即生成子节点 88 _divide() { 89 this.children = [ 90 // Top left 91 new QuadTree(this.x, this.y, this.width / 2, this.height / 2, this.depth + 1), 92 // Top right 93 new QuadTree(this.x + this.width / 2, this.y, this.width / 2, this.height / 2, this.depth + 1), 94 // Bottom left 95 new QuadTree(this.x, this.y + this.height / 2, this.width / 2, this.height / 2, this.depth + 1), 96 // Bottom right 97 new QuadTree(this.x + this.width / 2, this.y + this.height / 2, this.width / 2, this.height / 2, 98 this.depth + 1) 99 ] 100 } 101 }