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 }

 

posted @ 2022-08-05 17:15  zbit  阅读(149)  评论(0编辑  收藏  举报