基于四叉树/网格的碰撞-将逻辑付诸实践


13

首先,我只是在短时间内编写了自己的游戏逻辑,所以如果这看起来很简单,我深表歉意。

我已经阅读了很多有关四叉树和基于网格的碰撞检测的信息。我了解逻辑-除非物体基本靠近,否则基本上不检查碰撞。但从未提及如何实际执行此操作。

我脑子里有几种可能的方法,但不确定哪种方法最好

通用碰撞测试-没有优化

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

存储邻居(方法1) 但是,如果我们要优化冲突以仅检查附近的对象该怎么办。我们是否仍然遍历所有对象,还是创建带有近对象以进行检查的数组?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

循环所有对象,但仅检查邻居是否有碰撞(方法3) 。另一个可能性是,我们仍会遍历所有对象,但在测试碰撞之前检查对象是否在附近。

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

在图块数据中存储对象(方法3) 使用基于图块的系统允许使用其他选项;将特定图块上的对象存储在图块数据本身中。检查对象在哪个图块上,周围的图块包含可能与之碰撞的任何对象:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

我总是尝试以现实世界为例。如果我想比较具有相同颜色的项目,即使它们与颜色不匹配,检查每个项目似乎也不合逻辑(方法2,请检查每个项目)。我可能会收集具有相同颜色的物品(彼此靠近的对象)并检查它们(方法1),而不是检查所有内容。

这不是一个适当的比较,因为碰撞检查中的项目一直在移动,因此顺序混乱了。那让我感到困惑。

检查每个项目会更有效,从而消除保持生成邻居数组的压力。

还是找到邻居从而不必遍历那么多对象来检查冲突是否更有效率?

不断更改每个图块上的数据似乎也很费力,所以我不确定这是一个好主意。

我一直在思考一种塔防游戏,其中如果在射击目标之前物体在射程内,则该塔需要检测物体。检查所有项目似乎很愚蠢,而有时根本没有任何物体在附近。

我为冗长的帖子致歉,总是无法解释自己!


这是什么语言?Javascript?
Kyle C

2
动作脚本3,但该语言非常不相关。只想找出什么是处理和构造此内容的最佳方法:)
omgnoseat 2011年

Answers:


8

您需要减少实际碰撞检查的次数。是的,我知道。因此,让我们详细说明一下:

您的第一个没有任何优化的碰撞检查算法:一次运行将执行多少次检查?如果n是对象的数量,则大约为n *(n-1)个检查。对于许多对象(> 100),这将非常慢。

您的邻居检查方法不会更快。如果您的对象不是静态的并且移动很多,则需要在游戏循环中每次为每个对象构建这些邻居列表。实际上,这比第一种算法差,因为您正在执行n *(n-1)来构建邻居列表,然后检查每个对象是否与邻居中的一个碰撞。

您需要划分游戏空间。假设您的游戏空间为400x400像素宽。您可以将其划分为4个子空间,每个子空间的大小为200x200,并检查每个对象所属的子空间(一个对象可以在一个以上子空间内)。然后,您只需要检查每个子空间中的对象是否与同一子空间中的其他对象发生冲突。

因此,运行时成本为:用于构建4个子空间列表+(n / 4)*((n-1)/ 4)的n比以前的运行时成本好得多。可以通过使子空间更小(例如50x50)来进一步减少此空间。

所以我们的算法现在看起来像这样:

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

这有点类似于您的图块数据概念。但是子空间的大小不必与图块相同。

我们可以做的最后一步是使用四叉树进一步优化这一点。令k为子空间的计数。为了构建空间对象列表,我们正在进行k * n次检查,如果您的游戏世界很大,则会进行大量检查。

为了降低成本,我们使用四叉树。四叉树是划分游戏空间的另一种方法。无需将我们的400x400空间划分为64个50x50子空间并检查每个对象当前在64个子空间中的哪个子空间,而是将游戏空间划分为4个子空间,这四个子空间是游戏空间(200x200)大小的一半,而游戏空间又被划分为较小的子空间(100x100),这些子空间又又分为50x50子空间。这是我们的四叉树。

现在,如果我们想知道一个对象属于哪个50x50子空间,我们就检查它属于200x200子空间中的哪个。然后,我们在四叉树中更深一层,并检查刚发现的200x200子空间内的4个100x100子空间。重复此过程,直到我们知道对象属于哪个50x50子空间。那么现在需要检查几张呢?对于四叉树的每个级别为4(请记住,一个对象可以位于两个子空间的边缘)。因此,我们需要进行4 * 3检查,才能将对象分配给正确的50x50子空间。比64个支票好得多。

因此,我们的四叉树算法需要进行4 * 3 * n次检查才能构建子空间列表,然后需要进行类似k *(n / k)次检查来检查每个对象的碰撞。


多数民众赞成在一个非常清晰和易于理解的篷,谢谢!如何处理对象在哪个子空间上?子空间是否作为对象处理,其上的对象另存为数组并相互检查?还是只通过检查主循环中的X和Y来检查它在哪个子空间上?似乎一直在构建和更改数组的效率很低。因此,我想确保使用适当的方法:)当对象的大小变化时,我还会看到一个问题。我猜最小的子空间应该和最大的物体一样大吗?
omgnoseat 2011年

我很高兴为您提供帮助:)子空间将是一个对象,该对象将维护它包含的4个子空间。最小的子空间有所不同,因为它不再包含子空间,而是对象列表。考虑更新成本:子空间树在游戏开始时构建一次。在主循环的每个循环中,最小子空间中的对象列表将被清除并再次填充。因此,仅需要列表添加/删除。无需更改任何数组。最小的子空间应足够大以包含多个游戏对象。大小取决于您的游戏,以后可以进行调整。
斯蒂芬

谢谢,忘了我们还将检查最小子空间中实际对象之间的碰撞,这很有意义,它现在应该能够容纳多个对象。我已经开始进行测试,并且进展顺利。我遇到的最大麻烦是检测对象属于哪个子空间。我一直在子空间的子项X和Y值之间循环,这是一个合适的方法吗?
omgnoseat 2011年

如何处理延伸到多个子空间(即,其位置在边界上或附近)的对象?
2011年

简单:我不知道。如果对象位于每个子空间的边界上,则它们可以是多个子空间的一部分。因此,一个对象最多可以是4个子空间的一部分。但这是少数。
斯蒂芬·
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.