当前位置:嗨网首页>书籍在线阅读

35-应用碰撞检测

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.12.5 应用碰撞检测

当进行碰撞检测时,系统将对每个物体外围的边框进行检测。边框是环绕游戏物体四角的最小矩形。创建以下函数可以实现此功能。

function boundingBoxCollide(object1, object2){
  var left1 = object1.x;
  var left2 = object2.x;
  var right1 = object1.x + object1.width;
  var right2 = object2.x + object2.width;
  var top1 = object1.y;
  var top2 = object2.y;
  var bottom1 = object1.y + object1.height;
  var bottom2 = object2.y + object2.height;
  if (bottom1 < top2)return(false);
  if (top1 > bottom2)return(false);
  if (right1 < left2)return(false);
  if (left1 > right2)return(false);
  return(true);
};

可以将任意两个游戏对象传入这个函数,只要它们包含x、y、width和height属性。如果两个物体重叠,函数就返回true,否则返回false。

Geo Blaster Basic中的checkCollision()函数包括太多内容。完整代码可以参考例8-12。与其将代码重新打印一次,还不如剖析其中的一些基本概念。

值得注意的是紧挨着for循环构造器的“标签”。

使用下面代码中所用的标签将有助于简化碰撞检测的过程。

rocks: for (var rockCtr=rocksLength;rockCtr>=0;rockCtr--){

需要遍历所有需要进行碰撞检测的不同类型的物体。但是不需要检查已经和其他物体碰撞而爆炸的物体。为了保证所进行的碰撞检测都是必须的且次数最少,需要使用标签和break语句实现一个流程。

以下是这个流程背后的逻辑。

(1)创建一个rocks:标签,开始遍历rocks数组。

(2)在rocks的循环中创建一个missiles:标签,遍历playerMissiles数组。

(3)对数组中最后一个陨石和导弹进行边框类型的碰撞检测。请注意,从数组的尾部开始循环。这样,当从数组中移除元素时(碰撞发生时),不会影响到数组中其他还没有检测过的成员。

(4)如果陨石与导弹碰撞,则将它们分别从各自的数组中移除。然后调用break rocks和break missiles语句。任何类型的物体被移除时,都必须中断循环并开始检测该数组中下一个元素。

(5)继续遍历其余的导弹,直到所有导弹都与当前陨石进行了检测(除非在发生陨石与导弹碰撞时执行了break rocks语句)。

(6)检查每一个飞碟、每一个飞碟导弹,以及玩家与陨石的碰撞。玩家不需要标签,因为只有一个玩家对象的实例。飞碟与飞碟导弹都将遵循与导弹一样的逻辑。如果其中一个与陨石发生了碰撞,则首先将其从对应的数组中移除,然后中断循环返回到对应的标签位置。

(7)一旦检测了陨石与游戏中所有其他物体间的碰撞,就检查玩家导弹与飞碟间的碰撞。基本逻辑相同,同样使用循环标签,反向遍历数组。当物体从数组中移除时,中断循环并返回标签位置。

(8)检查飞碟导弹与玩家之间的碰撞。

多年来,人们找到了一个非常有效的方法用于检测多对象数组之间的碰撞。这当然不是唯一的实现方法。如果读者不喜欢使用循环标签的方法,那么可以参考下面的方法。

① 为每个对象添加一个名为hit的布尔类型属性,在创建对象时将其置为false。

② 遍历所有陨石,检测陨石与其他游戏物体间的碰撞。无论是正向遍历还是反向遍历都没有影响。

③ 当调用boundBoxCollide()函数时,确认每个对象的hit属性都是false,否则跳过碰撞检测。

④ 如果两个对象发生碰撞,则将两个物体的hit属性置为true。此时,没有必要将对象从数组中移除。

⑤ 遍历所有玩家导弹,检测与飞碟间的碰撞;然后遍历飞碟,检测与玩家的碰撞。

⑥ 当碰撞检测流程完毕,重新遍历每个对象(反向遍历),将所有hit属性为true的对象从数组中移除。

本书已经介绍了这两种方法以及相应的变化。第二种方法更加简洁。不过,当处理很大数量的对象时,最后对所有对象的遍历会增加一些处理器的开销。如果读者想尝试一下,可尝试实现第二种方法作为练习。