Nape刚体碰撞检测

作者: ladeng6666 分类: Nape 发布时间: 2013-02-20 22:56 阅读: 3,878

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

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

  • 认识Nape事件侦听:阐述Nape中添加事件侦听的大致流程。
  • Nape事件侦听器:简单说明Nape中4中不同的事件侦听器
  • InteractionListener实现碰撞检测:涉及到碰撞事件类型CbEvent,和碰撞行为类型InteractionType。
  • InteractionCallback获取碰撞信息:类似Box2D中的b2Contact类,强大的碰撞信息收集器InteractionCallback。
  • 实例说明:通过一个示例,演示碰撞事件侦听的代码。

认识Nape事件侦听


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

Flash中任何继承EventDispatcher类的对象都可以通过addEventListener对指定事件添加侦听,例如,要对Sprite对象的ENTER_FRAME事件添加侦听,代码应该写成:

Nape中所有的事件侦听只能通过space对象来添加。另外Nape事件侦听不是通过addEventListener()这样的函数,而是通过Listener类来实现的。简单的讲,可以理解成把addEventListener()函数拆成了两步:

  1. 创建Listener对象,这个对象中包含了碰撞事件所需的所有信息,稍后会详细介绍。
  2. 将Listener对象添加到space的listeners属性中。space.listeners是一个ListenerList对象,用来保存Nape中所有的事件侦听器。

完成上面两步就实现了Nape碰撞事件侦听,下面是具体的代码:

听起来似乎是这样啊!那Nape都有哪些事件类型呢?

Nape事件侦听器


Listener是Nape中碰撞事件帧听的核心部分。所有的事件派发后都有Listener对象侦听,然后触发相应的事件处理函数。针对不同的事件,侦听器也不同,也就衍生出了Listener的四个子类:BodyListener、ConstraintListener、InteractionListener和PreListener。这几个侦听器的用途简单说明如下:

  • BodyListener:用来侦听刚体的状态WAKE和SLEEP状态之间切换,并在事件发生时派发CbEvent.WAKE或CbEvent.SLEEP事件
  • ConstraintListener:侦听关节状态WAKE、SLEEP和BREAK的变化,并对应的派发事件CbEvent.WAKE、CbEvent.SLEEP和CbEvent.BREAK
  • InteractionListener:在刚体发生碰撞时派发的所有事件都由InteractionListener来侦听。这些碰撞事件包括CbEvent.BEGIN、CbEvent.ONGOING和CbEvent.END
  • PreListener:这是一个特殊的InteractionListener侦听器,同样也用来侦听碰撞事件,不同的是,PreListener在物理碰撞模拟之前执行,这意味着我们可以在碰撞前对碰撞模拟进行干预,如忽略碰撞而不进行碰撞模拟,调整碰撞的弹性系数、摩擦力等等。进而实现单边平台等等效果。

 

InteractionListener实现碰撞检测


本节我们主要是讲刚体碰撞检测,所以我们重点看一下InteractionListener侦听器,其构造函数如下:

总共有6个参数,具体说明如下:

  • event:CbEvent:要侦听碰撞事件类型。Nape将刚体的碰撞事件分为3种类型,由CbEvent类的3个常量表示,包括BEGIN、END、ONGOING。
    • BEGIN:当刚体之间开始发生碰撞时派发CbEvent.BEGIN事件
    • ONGOING:两个刚体发生碰撞后,至分离之前,会不断派发CbEvent.ONGOING事件。比如刚体与Sensor碰撞时,刚体与Sensor刚体之间会有重叠部份,这是会持续派发CbEvent.ONGOING事件
    • END:当对象之间的碰撞结束,并分离时派发CbEvent.END事件。
  • interactionType:InteractionType:要侦听的碰撞行为类型。根据刚体类型的不同,刚体之家的碰撞也被分成了4类。有InteractionType的4个常量表示:
    • COLLISION:正常刚体之间的碰撞。
    • FLUID:刚体与模拟流体浮力的刚体发生的碰撞。
    • SENSOR:刚体与Sensor感应区域刚体的碰撞。
    • ANY:包含以上3中类型的任何碰撞类型。
  • options1:OptionType:Nape可以通过OptionType类对碰撞侦听的两个碰撞对象类型进行描述,只有符合该类型的对象发生了碰撞,才会被侦听都,这样可以更准确的侦听碰撞事件。OptionType描述碰撞对象类型的方法有两种,一个是直接通过CbType类的常量来设定,这些常量包括
    • ANY_BODY:每个刚体默认的类型描述
    • ANY_COMPOUND:每个刚体组合默认的类型描述
    • ANY_CONSTRAINT:每个关节默认的类型描述
    • ANY_SHAPE:每个形状默认的类型描述

除此之外,我们可以新建一个CbType对象,并通过body.cbTypes.add()将其,添加到刚体的描述中。例如下面的代码:

  • options1是对第一个碰撞对象类型的描述,和下面的options2搭配使用。Nape中只有符合这两种类型的对象发生碰撞时,才会派发相应的事件。
  • options2: OptionType:侦听碰撞的两个刚体中,第2碰撞对象类型描述。
  • handler:InteractionCallback:处理碰撞事件的函数,和Flash中addEventListener里的事件处理函数一样。
  • precedence:Int = 0:当不同的事件侦听器同时侦听相同的刚体之间相同的碰撞事件时,触发侦听器的优先权。

举个简单的例子,比如现在舞台上有圆形和矩形两个形状的刚体,那么我们可以通过下面的代码创建两种不同的类型:

虽然两个对象同样没有参数,但是因为是两个不同的实例对象,所以可以表示两种不同类型的刚体。如果我们可以侦听的刚体碰撞可以分为三类,对应的在侦听器中设置options1和options2的方法分别如下:

  • 圆形与圆形之间的碰撞:

  •  圆形与矩形之间的碰撞:

  • 矩形与矩形之间的碰撞:

看过代码之后是不是就明白多了。

InteractionCallback获取碰撞信息


除了可以在Listener中用options1和options2参数有针对性的侦听具体的碰撞,Nape在碰撞信息的传递上,也Box2D要强大的多。

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

在Nape的事件处理函数中,有一个InteractionCallback类型的参数,包含了所有的碰撞信息,具体由它的3个参数保存:

  • int1:Interactor:碰撞刚体中,符合InteractionListener中的第1个碰撞对象类型描述的刚体。
  • int2:Interactor:碰撞刚体中,符合InteractionListener中的第2个碰撞对象类型描述的刚体。
  • arbiters:保存了arbiter对象的碰撞信息列表。Nape中的arbiter类似于Box2D中的b2Contact类型,你可以在《什么是Arbiter》一节中找到详细的说明。

在圆形和矩形之间碰撞的实例代码中,int1就是指的rectType类型的刚体,int2指的是circleType类型的刚体。具体的代码如下:

是不是很简单?

好啦,到这里Nape碰撞检测的内容就基本讲完了。好多吧,没事慢慢消化。接下来是该举出详细的例子说明一下啦。

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

Sorry, either Adobe flash is not installed or you do not have it enabled

完整的代码和注释如下,点击这里下载源文件

代码中用到了我自定义的LDEasyNape类,具体请参考这篇教程

 

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

18条评论
  • 阿柑

    2013 年 4 月 13 日 下午 4:48

    拉登大师~请教一下,爆炸的效果怎么做呢?就是刚体把其他刚体炸开~~

    1. ladeng6666

      2013 年 4 月 13 日 下午 10:09

      请参考emanueleferonato的这篇文章
      http://www.emanueleferonato.com/2012/01/05/create-real-explosions-with-box2d-exploding-objects-and-setting-the-center-of-explosion-with-mouse-click/

  • DreamofC

    2013 年 7 月 5 日 下午 4:24

    PRE应该是碰撞前吧

  • Jerry

    2013 年 7 月 15 日 下午 7:16

    拉登大叔,想问一个问题啊,为什么上面demo可以保持固定而不受重力影响啊

  • michael

    2014 年 10 月 9 日 下午 10:35

    请问一下,怎么实现碰撞过滤呢,比如从一个平台往上跳时,不碰撞,但是从上落到这个平台上时是碰撞的,类似b2ContactFilter的ShouldCollide

    1. ladeng6666

      2014 年 10 月 10 日 下午 9:16

      你好,谢谢你的关注,你说的应该是单边平台的效果。要实现单边平台,需要对碰撞事件进行忽略,请留下你的邮箱,回头我会专门写一篇关于单边平台的教程,届时发邮件通知你。

      1. michael

        2014 年 10 月 11 日 下午 3:50

        谢谢你的热心,在NAPE的论坛上找到解决了!

        1. ladeng6666

          2014 年 10 月 11 日 下午 4:55

          Nape论坛,google上的那个吗?最新在整理box2d的内容,所以回复的晚了,没帮上忙

    2. ladeng6666

      2014 年 10 月 10 日 下午 10:04

      大致过程是这样的,在space.listenners中添加PreListener事件侦听,在侦听函数中返回PreFlag.INGORE即可忽略当前碰撞

      1. michael

        2014 年 10 月 11 日 下午 3:57

        nape这个碰撞事件分类真不错,少写了很多if

        1. ladeng6666

          2014 年 10 月 11 日 下午 4:55

          是的,AS开发的话,物理引擎还是推荐Nape

  • Pingback: BodyListener侦听刚体状态 - Ladeng6666
  • 游戏

    2015 年 5 月 7 日 上午 6:50

    拉登大叔,我建了一个无重力的space,然后有一个小球被四个墙体围起来。然后把它们所有的反弹系数elasticity都设为了1,它们所有的dynamicFriction与其它带有Friction字样的属性的摩擦力全部都设为0,反弹时仍然会有速率的损失,是什么原因?enterFrame事件侦听器里发现ball.velocity.length在不断的变小,虽然每次只减少一点点,但确实是在不断减少,可官方的API手册中写的是无损反弹。是还需要其它属性的设置吗?

    1. ladeng6666

      2015 年 5 月 7 日 下午 8:34

      发源文件到我的邮箱ladeng6666@163.com,我研究一下吧!谢谢你的支持!

    2. ladeng6666

      2015 年 5 月 7 日 下午 8:35

      另外,加我的QQ:1160662553,咱们实时交流!

      1. 游戏

        2015 年 5 月 8 日 上午 5:37

        好,大神的QQ。

      2. 游戏

        2015 年 5 月 9 日 下午 2:32

        拉登大神,给你QQ邮留言了,但我看你一直没有上QQ,我就把资料整理到自己的博客里了:http://blog.zinewow.com/post/515.html,我把能去掉的摩擦力系数都设为了0,弹性系数设为了最大值1,按着官方的说是无损反弹,可实际上它还是会有速率的损失,虽然每次损失的很小,但确定是有损失,过个大概几十秒可以明显的感觉到速度变小了。

        1. ladeng6666

          2015 年 5 月 9 日 下午 9:32

          就像我在QQ里回复你的,记得将space对象的worldAngularDrag 和worldLinearDrag设置为0,这两个属性用来设置全局的线性阻尼和旋转阻尼。

发表评论

电子邮件地址不会被公开。 必填项已用*标注