【Nape教程】Nape刚体碰撞检测

Nape帮我们实现了物理碰撞模拟,通过Nape我们可以实现各种游戏模型。但是就像我在 Box2D碰撞检测 里讲的一样,只是碰撞是不够的,我还喜欢碰撞后的物体可以消失、变小等等,按照游戏需求执行任何需要的动作。这就需要我们今天要讨论的话题,自定义碰撞检测处理。这篇文章比较长,深吸一口气,来吧!

跟Box2D比起来,Nape更加符合Flash开发人员的习惯,它的回调系统跟Flash的事件模型非常相似,添加事件侦听,自定义事件处理函数。而且Nape事件类型比Box2D更为强大和完善,还等什么,马上开始吧!

创建一个Nape事件侦听
虽说Nape的回调系统和Flash事件模型非常相似,但具体写起代码来还是存在不同的。

Flash中任何继承EventDispatcher类的对象都可以通过addEventListener对指定事件添加侦听,例如,要对Sprite对象的ENTER_FRAME事件添加侦听,代码应该写成:
  1.             stage.addEventListener(Event.ENTER_FRAME, loop);
复制代码
Nape中所有的事件侦听只能通过space对象来添加。另外Nape事件侦听不是通过addEventListener()这样的函数,而是通过Listener类来实现的。简单的讲,可以理解成把addEventListener()函数拆成了两步:

创建Listener对象,这个对象中包含了碰撞事件所需的所有信息,稍后会详细介绍。
将Listener对象添加到space的listeners属性中。space.listeners是一个ListenerList对象,用来保存Nape中所有的事件侦听器。
完成上面两步就实现了Nape碰撞事件侦听,下面是具体的代码:
  1.    var listener:InteractionListener = new InteractionListener();
  2.                         napeWorld.listeners.add(listener);
复制代码
听起来似乎是这样啊!那Nape都有哪些事件类型呢?

Nape事件类型
Nape中的事件是指对象发生碰撞或状态变更时派发的事件,用CbEvent中的常量表示,这个常量也是Listener对构造函数的第一个参数,具体有下面7种:
  • BEGIN:当Nape对象之间的碰撞开始时派发CbEvent.BEGIN事件
  • ONGOING:当对象之间的处在碰撞过程中时派发CbEvent.ONGOING事件。比如刚体与Sensor碰撞时,刚体与Sensor刚体之间会有重迭部份,这是会持续派发CbEvent.ONGOING事件
  • END:当对象之间的碰撞结束要分离时派发CbEvent.END事件。
  • PRE:是在碰撞发生之前,可以侦听这个来忽略这个碰撞。比如Nape官网中的平台示例
  • WAKE:和Box2D类似,刚体运动时,其状态会标示为WAKE,当刚体不动时,会标示为SLEEP状态,不进行模拟计算,提升效率。当刚体由SLEEP变为WAKE状态时派发这个事件。
  • SLEEP:当刚体由WAKE变为SLEEP状态时派发的事件。
  • BREAK:关节对象中可以设置关节的承受力,当超出这个承受力时关节可以自动断开,这时会派发CbEvent.BREAK事件。

与Box2D仅有的BEGIN和END事件类型比起来,有没有感觉到Nape碰撞事件系统的强大?至少有那么一点点吧。别着急,继续往下看,你会发觉作者对Nape的介绍一点都没有夸大。

Nape碰撞类型
碰撞类型?不是讲过了吗?重复了吗?

No!No!上一节介绍的是Nape事件类型,是不同碰撞或状态变更时派发的事件,是事件!碰撞类型是指不同对象之间发生的碰撞。还是不明白?继续往下看。

按照刚体的特性来划分可以分为三种:1,普通的刚体,在Nape应用中最基本的模拟碰撞的刚体;2,浮力刚体,可以用来模拟水中漂浮效果的刚体;3,Sensor刚体,也就是感应刚体,这类刚体不参数碰撞模拟,但是Nape依然会对其进行碰撞检测,这和Box2D设置isSensor属性为true的刚体是一样的。

和这些不同类型的刚体相对应的,Nape提供了3中不同的碰撞类型,分别用InteractionType类的常量:COLLISION,FLUID和SENSOR表示(也是Listener构造函数的第2个参数),具体的用法分别说明如下:

  • InteractionType.COLLISION:普通刚体之间的碰撞
  • InteractionType.FLUID:刚体与浮力刚体的碰撞
  • InteractionType.SENSOR:刚体与sensor刚体的碰撞

Nape中每个事件侦听器只能设置一种碰撞类型,作为第2个参数,如果需要对所有的碰撞类型侦听,又觉得创建3个侦听器太麻烦,也可以用InteractionType.ANY表示。

具体的代码,我还是在介绍完事件侦听器之后再举例吧。

Nape事件侦听器
说了这么多,终于要讲到添加碰撞侦听的重点了。Listener是Nape中碰撞事件帧听的核心部分。所有的事件派发后都有Listener对象侦听,然后触发相应的事件处理函数。针对不同的事件,侦听器也不同,也就衍生出了Listener的四个子类:BodyListener、ConstraintListener、InteractionListener和PreListener。这几个侦听器的用途简单说明如下:
  • BodyListener:用来侦听刚体在WAKE和SLEEP状态之间切换时派发的CbEvent.WAKE或CbEvent.SLEEP事件
  • ConstraintListener:侦听关节(后续我们详细介绍)状态变化时派发的事件,这些事件有CbEvent.WAKE、CbEvent.SLEEP和CbEvent.BREAK
  • InteractionListener:在刚体或关节发生碰撞时派发的所有事件都由InteractionListener来侦听。这些碰撞事件包括CbEvent.BEGIN、CbEvent.ONGOING和CbEvent.END
  • PreListener:这个侦听器可以侦听任何碰撞的所有事件。InteractionListener同样也用来侦听碰撞事件,但是每个InteractionListener对象只能侦听其中某一个事件。PreListener可以同时侦听所有的碰撞事件,也就是说,添加了PreListener侦听后器后,碰撞在发生、进行和结束时派发的事件都会触发PreListener的事件处理函数。

本节我们主要是讲碰撞检测,所以只用到InteractionListener,其他的侦听器,以后我们会慢慢学习,如果感兴趣,也可以自己尝试一下。首先看一下InteractionListener的构造函数:
  1.   public function IneractionListener(
  2.                                 event:CbEvent, 
  3.                                 interactionType:InteractionType, 
  4.                                 options1:Null<Dynamic>, 
  5.                                 options2:Null<Dynamic>, 
  6.                                 handler:InteractionCallback -> Void, 
  7.                                 precedence:Int = 0)
复制代码
总共有6个参数,看起来挺复杂,其实大部分我们都已经很熟悉了,不过还是再仔细的看一下吧:

  • event:CbEvent:要侦听的CbEvent事件类型,前面介绍过总共有BEGIN、END、ONGOING、PRE、WAKE、SLEEP和BREAK七种。
  • interactionType:InteractionType:要侦听的碰撞类型,前面同样也介绍,根据碰撞的刚体不同,有COLLISION、FLUID、SENSOR和ANY四种。
  • options1:CbType:侦听碰撞的两个刚体中,限定的第1刚体的类型,和下面的options2搭配使用。Nape中只有符合这两种类型的刚体发生碰撞时,才会派发相应的事件。
  • options2:CbType:侦听碰撞的两个刚体中,限定的第2刚体的类型。
  • handler:InteractionCallback:处理碰撞事件的函数,和Flash中addEventListener里的事件处理函数一样。
  • precedence:Int = 0:当不同的事件侦听器同时侦听相同的刚体之间相同的碰撞事件时,触发侦听器的优先权。
这些参数我们在前面的章节里基本上都学过了,这里只是重点将以下参数options1和options2。这两个参数都是CbType类型的,分别表示两个碰撞刚体的类型。这个类型不是在碰撞类型里讲的普通刚体、浮力刚体或Sensor刚体,而是由我们自定义的不同类型,或者叫做不同分组。


举个简单的例子,比如现在舞台上有圆形和矩形两个形状的刚体,那么我们可以通过下面的代码创建两种不同的类型:
  1.    var cicleType:CbType = new CbType();
  2.                         var rectType:CbType = new CbType();
复制代码
虽然两个对象同样没有参数,但是因为是两个不同的实例对象,所以可以表示两种不同类型的刚体。如果我们可以侦听的刚体碰撞可以分为三类,对应的在侦听器中设置options1和options2的方法分别如下:

圆形与圆形之间的碰撞:
  1.     new InteractionListener(
  2.                                         CbEvent.BEGIN,
  3.                                         InteractionType.COLLISION,
  4.                                         circleType,
  5.                                         circleType,
  6.                                         onCircleVsCircle
  7.                                         );
复制代码
圆形与矩形之间的碰撞:
  1.    new InteractionListener(
  2.                                         CbEvent.BEGIN,
  3.                                         InteractionType.COLLISION,
  4.                                         rectType,
  5.                                         circleType,
  6.                                         onRectVsCircle
  7.                                         );
复制代码
矩形与矩形之间的碰撞:
  1.    new InteractionListener(
  2.                                         CbEvent.BEGIN,
  3.                                         InteractionType.COLLISION,
  4.                                         rectType,
  5.                                         rectType,
  6.                                         onRectVsRect
  7.                                         );
复制代码
看过代码之后是不是就明白多了。除了可以在Listener中用options1和options2参数有针对性的侦听具体的碰撞,Nape在事件处理函数的处理上也是比Box2D要强大的多。

比如上面例子中圆形与矩形之间的碰撞,在Box2D中通过b2Contact里的body1和body2获取的碰撞刚体,无法明确哪个是矩形,哪个是圆形。我们不得不对body1或body2分别进行两次判断。

在Nape的事件处理函数中,有一个InteractionCallback类型的参数,这个InteractionCallback对象有int1和int2两个属性,分别对应Listener中的options1和options2参数。在圆形和矩形之间碰撞的实例代码中,int1就是指的rectType类型的刚体,int2指的是circleType类型的刚体。具体的代码如下:
  1.    new InteractionListener(
  2.                                         CbEvent.BEGIN,
  3.                                         InteractionType.COLLISION,
  4.                                         rectType,
  5.                                         circleType,
  6.                                         onCircVsCircle
  7.                                         );
  8.                         private function onRectVsCircle(cb:InteractionCallback):void {
  9.                                 //int1表示矩形刚体类型rectType
  10.                                 cb.int1.castBody.userData.graphic.alpha = 1;
  11.                                 //int2表示圆形刚体类型rectType
  12.                                 cb.int2.castBody.userData.graphic.alpha=0.3;
  13.                         }
复制代码
是不是很简单?但是现在又出现一个新的问题,怎么样把刚体标示成某个CbType类型呢?这个同样很简单,通过Body对象的cbTypes属性的add方法,就可以把刚体标示为相应的CbType类型,而且可以同时标记成多种类型哦!代码举例如下:
  1.    var cicleType:CbType = new CbType();
  2.                         var circle:Body = new Body();
  3.                         //通过cbTypes.add()方法,将circle刚体标记为circleType类型
  4.                         circle.cbTypes.add(circleType);
  5.                         var rectType:CbType = new CbType();
  6.                         var rect:Body = new Body();
  7.                         //通过cbTypes.add()方法,将rect刚体标记为rectType类型
  8.                         rect.cbTypes.add(rectType);
复制代码
好啦,到这里Nape碰撞检测的内容就基本讲完了。好多吧,没事慢慢消化。接下来是该举出详细的例子说明一下啦。

下面的例子中有1个红色的矩形和2个蓝色的圆形,点击可以拖动刚体。当红色矩形刚体撞击蓝色圆形刚体,蓝色会变为半透明的。圆形之间的碰撞,会把蓝色改为完全不透明。动手试试看看效果吧!

完整的代码和注释如下,点击这里下载源文件。
代码中用到了我自定义的LDEasyNape类,具体请参考 这篇教程
  1. package
  2. {
  3.         import ldEasyNape.LDEasyNape;
  4.         import ldEasyNape.LDEasyUserData;

  5.         import nape.callbacks.CbEvent;
  6.         import nape.callbacks.CbType;
  7.         import nape.callbacks.InteractionCallback;
  8.         import nape.callbacks.InteractionListener;
  9.         import nape.callbacks.InteractionType;

  10.         [SWF( width="550", height="400", frameRate="60")]
  11.         public class T15_NapeBodyInteraction extends AbstractNapeTest
  12.         {
  13.                 private var circleType:CbType;
  14.                 private var rectType:CbType;

  15.                 private var circleVsCircleListener:InteractionListener;
  16.                 private var rectVsCircleListener:InteractionListener;

  17.                 public function T15_NapeBodyInteraction()
  18.                 {
  19.                         //在父类的构造函数中设置重力为0
  20.                         super(0);
  21.                         trace("hello world");
  22.                         LDEasyNape.version();
  23.                         //创建LDEasyUserData对象,设置圆形刚体为蓝色
  24.                         var circleData:LDEasyUserData=new LDEasyUserData();
  25.                         circleData.setGraphicAuotmatically(0x0000FF, 1);
  26.                         //创建圆形刚体类型,用于侦听器中限制碰撞的对象
  27.                         circleType = new CbType();
  28.                         //创建两个圆形刚体,并通过cbTypes的add方法,设置圆形刚体的类型为circleType
  29.                         LDEasyNape.createCircle(100,100,30,false,false, circleData).cbTypes.add(circleType);
  30.                         LDEasyNape.createCircle(300,300,30,false,false, circleData.clone()).cbTypes.add(circleType);

  31.                         //创建LDEasyUserData对象,设置矩形刚体为红色
  32.                         var rectData:LDEasyUserData=new LDEasyUserData();
  33.                         rectData.setGraphicAuotmatically(0xff0000, 1);
  34.                         //创建矩形刚体类型,用于在侦听器中限制碰撞的对象
  35.                         rectType = new CbType();
  36.                         //创建矩形刚体,并通过cbTypes的add方法,设置圆形刚体的类型为rectType
  37.                         LDEasyNape.createBox(200,200,60,60,false,false, rectData).cbTypes.add(rectType);

  38.                         //实例化InteractionListener对象,侦听的具体的碰撞信息如下:
  39.                         //参数1:cbEvent.BEGIN,侦听碰撞开始的事件
  40.                         //参数2:InteractionType.COLLISION侦听普通刚体碰撞
  41.                         //参数3和4:circelType和circleType,限制只侦听两个圆形刚体之间的碰撞
  42.                         //参数5:设置事件处理函数为onCircleVsCircle
  43.                         circleVsCircleListener=new InteractionListener(CbEvent.BEGIN,InteractionType.COLLISION,circleType,circleType,onCircleVsCircle);
  44.                         //rectVsCircleListener侦听器,除了options1和options2里限制的是矩形和圆形刚体之外
  45.                         //其他的和circleVsCircleListener侦听器是一样的
  46.                         rectVsCircleListener=new InteractionListener(CbEvent.BEGIN,InteractionType.COLLISION,rectType,circleType,onRectVsCircle);
  47.                         napeWorld.listeners.add(circleVsCircleListener);
  48.                         napeWorld.listeners.add(rectVsCircleListener);
  49.                 }
  50.                 private function onCircleVsCircle(cb:InteractionCallback):void {
  51.                         //设置圆形刚体透明度为1
  52.                         cb.int1.castBody.userData.graphic.alpha=1;
  53.                         cb.int2.castBody.userData.graphic.alpha=1;
  54.                 }
  55.                 private function onRectVsCircle(cb:InteractionCallback):void{
  56.                         //根据rectVsCircleListener中设置的options1和options2,我们知道
  57.                         //cb.int1是矩形刚体,cb.int2是圆形刚体
  58.                         //所以下面的代码将碰撞的圆形刚体透明度设置为0.3
  59.                         cb.int2.castBody.userData.graphic.alpha=0.3;
  60.                 }
  61.         }
  62. }
复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值