多物体碰撞检测策略

当只有两个物体时,碰撞只可能在 A - B 物体间发生。如果有三个物体,就有三种可能:A – B,B – C,C – A。如果是四个物体就有六种可能,五个物体就有十种可能。

如果物体多达 20 个,就需要分别进行判断 190 次。这就意味着在我们的 enterFrame 函数中,需要调用 190 次 hitTest 方法或距离计算。

如果使用这种方法,那么就会多用出必要判断的两倍!比如说 20 个物体就执行了 380次 if 语句(20 个影片每个判断 19 次,20 * 19 = 380)。大家现在知道学习本节内容的重要性了吧。

看一下问题,审视一下平常的做法。假设我们有六个Sprite,分别为 sprite0,sprite1,sprite2,sprite3,sprite4,sprite5。让它们运动并执行反弹,我们想要知道它们之间何时会发生碰撞。思考一下,依次获得每个Sprite的引用,然后再执行循环,再去和其它的Sprite进行比较。下面是一段伪代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
numSprites = 6 ;
for (i = 0 ; i < numSprites; i++)
{
     spriteA = sprites[i]; 
     for (j = 0 ; j < numSprites; j++)
     {
         spriteB = sprites[j]; 
         if (spriteA.hitTestObject(spriteB))
         {
             // 执行代码
         }
     }
}

六个Sprite执行了 36 次判断。看上去很合理,对吗?其实,这段代码存在着两大问题。首先,来看第一次循环,变量 i 和 j 都等于 0。因此 spriteA 所持的引用是 sprite0,而 spriteB 的引用也是一样。嗨,我们原来是在判断这个影片是否和自己发生碰撞!无语了。所以要在 hitTest 之前确认一下 spriteA != sprieB,或者可以简单地写成 i != j。代码就应该是这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
numSprites = 6 ;
for (i = 0 ; i < numSprites; i++)
{
     spriteA = sprites[i];
     for (j = 0 ; j < numSprites; j++)
     {
         spriteB = sprites[j];
         if (i != j && spriteA.hitTestObject(spriteB))
         {
             // do whatever
         }
     }
}

OK,现在已经排除了六次判断,判断次数降到了 30 次,但还是太多。下面列出每次比较的过程:

  • sprite0 与 sprite1, sprite2, sprite3, sprite4, sprite5 进行比较
  • sprite1 与 sprite0, sprite2, sprite3, sprite4, sprite5 进行比较
  • sprite2 与 sprite0, sprite1, sprite3, sprite4, sprite5 进行比较
  • sprite3 与 sprite0, sprite1, sprite2, sprite4, sprite5 进行比较
  • sprite4 与 sprite0, sprite1, sprite2, sprite3, sprite5 进行比较
  • sprite5 与 sprite0, sprite1, sprite2, sprite3, sprite4 进行比较

请看第一次判断: 用 sprite0 与 sprite1 进行比较。再看第二行: sprite1 与 sprite0 进行比较。它俩是一回事,对吧?如果 sprite0 没有与 sprite1 碰撞,那么 sprite1 也肯定不会与 sprite0 碰撞。或者说,如果一个物体与另一个碰撞,那么另一个也肯定与这个物体发生碰撞。所以可以排除两次重复判断。如果删掉重复判断,列表内容应该是这样的:

  • sprite0 与 sprite1, sprite2, sprite3, sprite4, sprite5 进行比较
  • sprite1 与 sprite2, sprite3, sprite4, sprite5 进行比较
  • sprite2 与 sprite3, sprite4, sprite5 进行比较
  • sprite3 与 sprite4, sprite5 进行比较
  • sprite4 与 sprite5 进行比较
  • sprite5 没有可比较的对象!

我们看到第一轮判断,用 sprite0 与每个影片进行比较。随后,再没有其它影片与 sprite0 进行比较。把 sprite0 放下不管,再用 sprite1 与剩下的影片进行比较。当执行到最后一个影片 sprite5 时,所有的Sprite都已经和它进行过比较了,因此 sprite5 不需要再与任何影片进行比较了。结果,比较次数降到了 15 次,现在大家明白我为什么说初始方案通常执行了实际需要的两倍了吧。

那么接下来如果写代码呢?仍然需要双重嵌套循环,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
numSprites = 6
for (i = 0 ; i < numSprites - 1 ; i++) 
{
     spriteA = sprites[i];
     for (j = i + 1 ; j < numSprites; j++)
     {
         spriteB = sprites[j];
         if (spriteA.hitTestObject(spriteB)) 
        
             // do whatever
         }
     }
}

请注意,外层循环执次数比Sprite总数少一次。就像我们在最后的列表中看到的,不需要让最后一个Sprite与其它Sprite比较,因为它已经被所有Sprite比较过了。内层循环的索引以外循环索引加一作为起始。这是因为上一层的内容已经比较过了,而且不需要和相同的索引进行比较。这样一来执行的效率达到了 100%。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值