随笔- 31  评论- 99  文章- 2 

松散的四叉树实战

何为四叉树?

四元树又称四叉树是一种树状数据结构,在每一个节点上会有四个子区块。四元树常应用于二维空间数据的分析与分类。 它将数据区分成为四个象限。数据范围可以是方形或矩形或其他任意形状。

概念可能太抽象,没关系,先继续阅读看看如何实现四叉树。

如何实现传统的四叉树?

请参考以下两篇文章,这两篇文章不需要VPN即可阅读,更加棒的是两篇文章都附带代码。

http://www.kyleschouviller.com/wsuxna/quadtree-source-included/

https://gamedevelopment.tutsplus.com/tutorials/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space--gamedev-374

为了照顾一下英文不好的同学,我送佛送到西,翻译部分内容,方便理解什么是四叉树。

四叉树是一种数据结构,用于将二维区域划分为更易于管理的部分。它是一个扩展的二叉树,二叉树有两个孩子节点,它有四个。

举例如下,图片所代表的二维空间被分成I,II,III,IV 四个部分。

开始时,四叉树只有根节点,根节点代表和管理整个二维空间。红色方块塞入到二维空间中,由根节点管理。

当越来越多红色方块需要塞入的二维空间中时,为了防止四叉树根节点管理方块过的,此时根节点将会分裂成4个子节点。每个子节点代表和管理父节点1/4空间。

红色方块分别转交给子节点管理。任何不能被子节点完全包含的红色方块,将由该节点的父节点管理。

 

当更多红色方块塞入到二维空间时,子节点可以继续分割新的子节点。

正如你所见,每个节点分别管理少量的红色方块。OK,你可能会想这有什么用呢?因为父节点的空间完全包含子节点的空间,故没有和父节点空间产生交集,则不会和其任意的子节点空间产生交集。

这对于过滤碰撞检测,范围检索有巨大的帮助。

比如,对于2D塔防游戏,屏幕中有大量的子弹和小怪,对所有的子弹和所有小怪全遍历检测是否碰撞,CPU肯定吃不消。如果子弹在屏幕左上角的,只需检测屏幕左上角的节点体系。

采用四叉树优化后,每个子弹只需要少量的小怪进行细致的碰撞检测。

传统四叉树缺点?

  • 对于很小的物体,如果处在世界空间的中心,则只能分配到根节点中。
  • 应用于动态场景时,节点在需要分割时才进行分割(不需要时也可以删除节点),虽然节省内存,但是需要经常性的开辟和释放内存,影响性能。
  • 应用于动态场景时,需要在对象的位置时刻在变化,对象可能随时切换在树中的节点。而且这些节点并不是在同一层级(可能引起树结构的改变),影响性能。

松散的四叉树?

松散的意思就是,让节点所管理的空间比节点自身代表的空间大一些,这样节点和节点管理空间之间会有部分重合,显得比较松散故因此得名。至于大多少最好,没有明确的理论支持。

但是截至目前大一倍是非常好的选择。在我给出的代码中,我使用nodeRect和looseRect 加以区分这两个空间。其中,looseRect尺寸为nodeRect尺寸的两倍。

不难想到,当一个物体的尺寸小于nodeRect的尺寸时,只要物体的中心处于nodeRect之中,那么物体一定包含于其looseRect之中。

对于松散的四叉树,我们不在要求nodeRect完全包含物体,只要求looseRect完全包含物体,那么对于很小的物体,即使处于特殊位置,也能够分配到相应大小的子节点中。

松散的四叉树克服了传统四叉树的第一个缺点。

 

我没有找到专门描述松散四叉树的资料,不过不用担心,松散八叉树的资料还是很多的。推荐如下:

http://cloudgrassland.com/2015/12/162-2/

https://anteru.net/blog/2008/11/14/315/index.html

https://www.mathematik.tu-clausthal.de/fileadmin/AG-StochastischeOptimierung/papers/LooseOctreePaper.pdf

http://www.tulrich.com/geekstuff/partitioning.html

 

阅读一系列文章之后,我们可以发现松散的四叉树,还可以克服传统四叉树的另外两个缺点。让它更适用于动态场景。方法就是将网格与松散的四叉树结合。

上文中已经提到,松散1倍情况下,当一个物体的尺寸小于nodeRect的尺寸时,只要物体的中心处于nodeRect之中,那么物体一定包含于其looseRect之中。

所以,对于尺寸固定的物体,它在四叉树中的层级也是固定的。并且我们给四叉树每一层,分配一个网格,用网格来把空间进行均匀分割。

已知深度的情况下(使用特定层网格),只需要选择距离物体中心最近的节点就可以确定物体应该被哪个节点的包围盒所包含,时间复杂度O(1)。也就是说当物体

运动时,可以非常轻易的更改其在四叉树中的节点位置。

松散四叉树缺点?

  • 如果松散四叉树使用网格,网格需要预先开辟足量的内存,所以会使用更多内存。同样我们无需动态的增加或减少内存。既是优点又是缺点!
  • 查询一个松散的四叉树时,需要复杂一点。因为节点是松散的,需要查询的节点会增加一点。

松散的四叉树实战!

也许你已经不厌其烦,正所谓 talking is cheap,show me the code

https://github.com/RonTang/GridLooseQuadtree/

这个是过年期间抽空写出的代码,具体没有来的及完善,不过应该可用于学习交流。

啊哈,新年的第一篇文章,祝大家新年快乐~~~

转载请标明出处,谢谢合作

posted on 2018-02-22 19:53  RonTang  阅读(...)  评论(... 编辑 收藏