cocos2dx-3.x物理引擎Box2D介绍

  • 物理引擎
  • Cocos2d-x引擎内置了两种物理引擎,它们分别是Box2DChipmunk,都是非常优秀的2D物理引擎,而且x引擎将它们都内置在SDK中。Box2D使用较为广泛,在这里选择Box2D来进行学习。  物理引擎模拟的内容: 重力:在游戏中模拟重力加速度,当游戏中人物跳跃起来后会受到重力影响而向下移动,在没有地面的场景,人物和物体会由于重力而做自由落体运动。牵引力(动力):在游戏中比如汽车的引擎,人物本身能够提供向前进行的动力,这种牵引力是持续不断地作用在物体上的,物体因此可以向作用力的方向移动。 摩擦力:物体在地面等接触面上移动时,会受到摩擦力的影响,它可以使正在运动的物体由于摩擦力的作用而停下来。 冲击力:比如爆炸会产生一次性的冲击力,会对爆炸范围内的物体产生一瞬间的力的作用,使其运动起来。 碰撞检测:当一个物体与另一个物体碰撞后,两个物体会因为碰撞发生作用力与反作用力,会让其之前的运动受到影响。 还有浮力关节链接等等物理概念。  Box2D简介: Box2D是由C++开发的一款轻量级的二维刚体仿真库,主要用于编写2D游戏,开发者可以使用它让游戏中的物体运动起来更真实。让游戏世界更具交互性。Box2D物理引擎是一个程序性动画系统。做动画常有两种方法:一种是预先准备好动画所需的图像数据,比如某种格式的2D图片,再一帧一帧地播放。这种预先准备的可称为数据性动画。另一种是以一定方法,动态计算出动画所需的数据,比如移动后的新位置、旋转的角度等等,根据这些数据再进行绘图。这种动态计算的可称为程序性动画。Box2D就是用物理学的方法,推导出游戏世界物体的位置、角度等数据。而Box2D也仅仅是推导出数据,至于得到数据之后怎么处理就是开发者自己的事情了。  Box2D的一些基本对象: 物理世界world:一个物理世界就是各种刚体bodies夹具fixtures约束constraints等物理引擎中基本对象相互作用的集合。所有的物理对象都是在物理世界已经建立好的基础上,在物理世界中生成的。物理世界具有一个范围,在2D坐标系中,物理世界的范围就是一个矩形区域,区域内的物理对象可以相互作用,发生物理碰撞等影响,一旦物理对象到了区域之外,将不再进行物理运算,不再产生任何物理作用。Box2D支持创建多个世界,但这通常没有必要。物理世界是Box2D引擎最为重要的对象,游戏必须要持有物理世界对象,这样才能访问物理世界中的各种对象,知道它们的状态,然后将这些状态更新到游戏界面中反映出各种模拟的物理现象。 刚体rigid body:大多数游戏对象在物理世界中都被抽象成为刚体对象,它是物理世界中十分坚硬的物质,物理引擎假定刚体都是不会发生形变的,它上面任意两点之间的距离都保持不变。在Box2D物理引擎中,b2Body类就是代表刚体的类型。在设计实现物理游戏时,刚体通常都对应着游戏中的一个具有具体外形的角色。刚体在Box2D中主要分为两大类,一类是可以移动位置或者旋转的动态刚体,这种刚体通常用于表示游戏中的活动物体;另一类是位置无法移动和旋转的静态刚体,这种刚体通常用于表示游戏中的地面平台等静物。夹具fixture:每个刚体都需要定义一个或者多个夹具,夹具是一个属性容器,它具有形状属性shape密度属性density摩擦属性friction恢复属性restitution。当一个刚体具有了夹具之后,它就可以参与物理世界的碰撞检测,摩擦力运算和弹力运算了。 形状shape2D几何外形对象,比如圆形circlr或者多边形polygon。形状定义好之后会被附加到某个夹具之上,作为夹具的外形属性存在,它是夹具的重要组成部分,家具在刚体碰撞运算时会通过形状来进行检测。形状类中保存的主要是形状的几何数据信息,比如一个圆形circle主要是保存它的半径信息,只要知道了半径就能知道圆形的具体大小;另一个比较常用的形状是四边形rect,它主要记录的是四边形的宽度和高度信息。 关节joint:关节就是种约束,用于将两个或多个刚体固定到一起。Box2D支持不同的关节类型——转动revolute棱柱prismatic距离distance等。比如卡通人物的手臂运动,就可以定义一个和人类一样的肘关节,关节两端是上臂和前臂两个刚体。一些关节可以有限制limits马达motors。 关节限制joint limit:关节限制限定了一个关节点运动范围。例如人类的胳膊肘只能在某一角度范围内运动。 关节马达joint motor:根据关节的自由度,关节马达可以驱动关节所连接的物体。例如你可以使用一个马达来驱动一个肘的旋转。  在游戏中引入Box2D物理世界: 因为x引擎内置了Box2D物理引擎,所以需要物理引擎的地方只要引入“Box2D/Box2D.h”头文件即可,以下代码就是建立物理世界,也就是初始化Box2D物理引擎的过程,这个过程都是放在游戏场景初始化阶段,把物理世界对象作为游戏世界的一部分完成初始化过程。

    view sourceprint?

    01.//定义重力加速度

    02.b2Vec2 gravity;

    03.//设置垂直方向的重力加速度

    04.gravity.Set(0.0f, -9.8f);

    05./*使用刚刚定义好的重力加速度生成物理世界对象,

    06.这样世界中的所有对象都会受到重力加速度的影响*/

    07.b2World* phyWorld = new b2World(gravity);

    08.//物理世界的对象都参与碰撞检测,无休眠对象

    09.phyWorld->SetAllowSleeping(false);

    10.//连续碰撞检测,避免发生物体穿过另一个物体的事件

    11.phyWorld->SetContinuousPhysics(true);

    12.//设置碰撞监听器

    13.phyWorld->SetContactListener(listener);

    通过以上代码,就可以在x引擎中建立一个物理世界,以上代码的最后一步,用于设置物理世界中各种物体碰撞的监听对象——listener,它是b2ContactListener类型。在物理引擎捕捉到世界中的物体对象发生碰撞后,会使用碰撞监听器b2ContactListener的回调方法,来实现碰撞的发现和响应功能。我们要做的就是定义好一个碰撞检测器,实现它的碰撞回调函数。有以下函数需要实现。 virtual void BeginContact(b2Contact* contact):碰撞开始时的回调函数,一般简单的碰撞检测使用; virtual void EndContact(b2Contact* contact):碰撞发生后的回调函数,一般简单的碰撞检测使用; virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold):碰撞求解前的回调函数,求解就是指计算碰撞产生的冲击力,需要计算碰撞冲击力造成的破坏等效果时,需要使用此回调函数; virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse):碰撞求解后的回调函数,需要计算碰撞冲击力造成的破坏等效果时,需要使用此回调函数。
    这四个回调方法中,前两个功能有限但使用起来简单,后两个提供的信息量大,但使用起来比较复杂,这个要根据游戏的具体要求而定,如果我们的游戏过程对物理要求不高,仅仅是实现碰撞检测功能,那么我们主要使用BeginContact(b2Contact* contact)这个回调函数就足够了,如果我们要处理碰撞之前和碰撞之后的效果,根据碰撞中产生的相互作用力来计算物理碰撞后的移动,则我们必须好好的利用全部这四个函数,它们联合作用起来,可以模拟出比较真实而复杂的物理碰撞效果。 在游戏初始化时引入Box2D物理世界,然后实现碰撞回调函数之后,我们的物理世界初始化工作就完成了,剩下的工作是根据游戏要求,预先生成或者在游戏过程中动态生成各种物理对象。比如刚体对象。  定义物体对象,实现重力效果: 在物理模拟游戏当中,一般都会有大量的刚体存在于这个物理世界内。有时候这些刚体是在游戏初始化时建立的,他们有位置、密度、体积等预制好的属性;而另一种情况是根据游戏过程实时动态地生成刚体,并为刚体设置位置等属性。Box2D是一个高效的物理引擎,所以实时动态生成刚体的速度非常快,只要数量不是非常巨大,就不会影响游戏的运行速度,刚体通常都会对应这个游戏中的某个角色或者是角色的一部分,比如在飞行射击游戏中,飞机的身体就可以用一个刚体或者多个刚体的组合来代表,刚体就是游戏角色在物理世界的抽象,刚体碰撞的物理变化最终还要反馈到游戏中的角色身上。

    view sourceprint?

    01.//首先生成b2BodyDef这个结构体的实例

    02.b2BodyDef spriteBodyDef;

    03.//指定刚体定义的类型是动态刚体,表明刚体是可以在物理世界中移动的

    04.spriteBodyDef.type = b2_dynamicBody;

    05.//设置刚体定义的初始位置

    06.spriteBodyDef.position.Set(5.0f,5.0f);

    07.//接下来使用spriteBodyDef对象来生成真正的刚体Body

    08.b2Body* spriteBody = phyWorld->CreateBody(&spriteBodyDef);

    09.//生成一个矩形形状,定义大小范围

    10.b2PolygonShape spriteShape;

    11.spriteShape.SetAsBox(10.0f, 10.0f);

    12.//接下来生成刚体Body将要使用的夹具对象

    13.b2FixtureDef spriteShapeDef;

    14.//指定夹具的外形就是刚刚生成的矩形

    15.spriteShapeDef.shape = &spriteShape;

    16.//设定其物理密度

    17.spriteShapeDef.density = 10.0f;

    18.//设定自己所属的碰撞组

    19.spriteShapeDef.filter.categoryBits = 0x0010;//第2组

    20.//指定自己会与哪个组发生碰撞

    21.spriteShapeDef.filter.maskBits = 0x0001;//第1组

    22.//使用定义好的夹具生成刚体

    23.spriteBody->CreateFixture(&spriteShapeDef);

    此时,物理世界中就有了一个刚体对象,这里要强调的是刚体的夹具定义时的碰撞分组信息,其中filter.categoryBits把刚体的夹具定义在第2组,接下来的filter.maskBits定义为第1组;这样此刚体的夹具就会与处于第一组的刚体夹具发生碰撞,而其他组或者位于同一组的刚体夹具,即使有了接触也不会发生碰撞事件,这是Box2D物理引擎的碰撞分组筛选功能。这在游戏中非常有用。在定义好这个刚体之后,在没有设置它的位置时,它会默认出现在物理世界的原点,也就是坐标(0,0)的点。  实现物体的碰撞检测: b2ContactListener:它是整个Box2物理世界中发生碰撞的监听以及响应类,所有发生在Box2D物理世界中的碰撞时间都能够被b2ContactListener类型的监听器检测并在碰撞响应函数中被处理。通常我们都是将自己实现的游戏世界作为碰撞检测的接口实现类,也就是说在定义某个我们自己的游戏世界类(这里我们假设将它称为World类)时,我们让它继承自b2ContactListener,这样World类的实例对象就可以对物理世界的碰撞捕捉和处理了。 例如

    view sourceprint?

    1.class World :public b2ContactListener

    对应物理世界的处理回调函数也在此声明,这个例子中我们只对发生碰撞那一刻的事件做响应,其他事件不做详细处理,所以碰撞检测的函数声明如下。

    view sourceprint?

    01.//碰撞事件回调函数

    02.virtual void BeginContact(b2Contact* contact);

    03.

    04.virtual void EndContact(b2Contact* contact)

    05.{

    06.B2_NOT_USED(contact);//关闭此事件,不做处理

    07.}

    08.

    09.virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)

    10.{

    11.B2_NOT_USED(contact);//关闭此事件,不做处理

    12.B2_NOT_USED(oldManifold);//关闭此事件,不做处理

    13.}

    14.

    15.virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)

    16.{

    17.B2_NOT_USED(contact);//关闭此事件,不做处理

    18.B2_NOT_USED(impulse);//关闭此事件,不做处理

    19.}


    使用关节来连接刚体: 连接器:连接器可以使两个或者多个刚体连接到一起,起到限制世界当中物体自身或物体之间的作用。  距离连接器(Distance Joint):最为常见也是最为简单的连接器,是通常所说的在两个刚体上两个点之间保持一定距离的距离连接器。当你指定一个距离连接器时,相应的两个刚体应该已经在应有的位置上了。然后在世界坐标系中指定两个锚点定点,第一个锚定点连接body1,第二个锚定点连接body2。这些点代表着应该保持的距离的常量。  这样不论两个物体怎样运动,它们之间都会保持着固定的距离,就像使用一只杆子连接了这两个物体一样。距离连接器也可以变成软的,就像连接一个弹簧一样,在定义中通过调节频率(frequency)和阻尼率(damping ratio)两个常量来取得柔软的效果,以下是定义一个弹簧效果的距离连接器。

    view sourceprint?

    1.b2DistanceJointDef jointDef;

    2.jointDef.Initialize(body1, body2, body1->GetPosition(),body2->GetPosition());

    3.jointDef.collideConnected = true;

    4.jointDef.frequencyHz = 4.0f;

    5.jointDef.dampingRatio = 0.5f;

    6.jointDef.length = 10;

    7.phyWorld->CreateJoint(&jointDef);

    距离连接器的应用场合非常广泛,固定距离连接器可以模拟翘翘板、捆绑物体的绳子这些物理现象;软性的连接器则可以用来模拟弹跳板、橡皮筋等物理现象。 
    旋转连接器(Revolute Joint):旋转连接器同时作用于两个刚体,并使两个刚体共享同一个锚定点,经常称之为铰链点(hinge point)。相对于两个物体的旋转来说,旋转连接器有一个自由度范围。这个角度称为连接角(joint angle)。  定制一个旋转连接,我们需要在世界中提供两个刚体和一个简单的锚定点。初始化方法假设物体已经在正确的位置。在以下例子代码中,两个刚体通过旋转连接器以第一个物体的质心作为铰链点(hinge Point)连接在一起。当bodyB逆时针旋转的时候,转动连接器的角度为正值。就像Box2D中的所有其他角一样,旋转是以弧度为基准。一般来说,旋转连接器使用Initialize()方法创建完成之后,旋转连接器的角度为零,和两个物体当前的角度无关。在一些场合下你可以希望控制连接角(joint angle),以下代码给出了旋转连接器的建立和连接角的限制设定:

    view sourceprint?

    1.b2RevoluteJointDef jointDef;

    2.jointDef.Initialize(body1,body2, b2Vec2(body1->GetPosition().x-15,body1->GetPosition().y+15));

    3.jointDef.lowerAngle = -0.5 * b2_pi;

    4.jointDef.upperAngle = 0.25 * b2_pi;

    5.jointDef.enableLimit = true;

    6.phyWorld->CreateJoint(&jointDef);

    旋转连接器的应用也非常广泛,凡是涉及到旋转开关的地方,都可以使用旋转连接器来实现;比如:汽车的轮子。我们只要将动力或者扭矩作用在轮子刚体上让连接器旋转,汽车就可以向前或者向后移动了。 qqGj0vK0y8a90sbBrL3T1rvT0NK7uPa3vc/yyc+1xNfU08m2yKGjCjxpbWcgc3JjPQ=="http://www.it165.net/uploadfile/files/2014/1229/20141229161044804.png" alt="\">

    view sourceprint?

    1.b2PrismaticJointDef jointDef;

    2.jointDef.Initialize(body1,body2,body2->GetPosition(), body2->GetPosition());

    3.jointDef.lowerTranslation = -100.0f;

    4.jointDef.upperTranslation = 100.0f;

    5.jointDef.enableLimit = true;

    6.phyWorld->CreateJoint(&jointDef);

    平移连接器应用场合在游戏中也很广泛:当一个物体与另一个物体在某个平面交叉移动时,就需要用到平移连接器,比如垂直升降的电梯,就可以使用平移连接器来模拟实现。 
    Box2D调试渲染: 在测试过程中,所有的刚体外形都应该是可见的,这样我们才能观测出各种物理碰撞等现象的详细过程和结果,这就需要我们引入GLESDebugDraw类。它是使用OpenGLES底层绘图方法,将刚体的外形准确绘制到屏幕上的功能类。GLESDebugDraw类位于Cocos2d-x SDKGLES-Render.h文件中。所有如果游戏需要使用到Box2D物理引擎并且需要调试,

debugDraw = new GLESDebugDraw();//这里新建一个debug渲染模块
phyWorld->SetDebugDraw(debugDraw);//设置
uint32 flags = 0;
flags += b2Draw::e_shapeBit;//形状
flags += b2Draw::e_aabbBit;//AABB块
flags += b2Draw::e_centerOfMassBit;//物体质心
flags += b2Draw::e_jointBit;//关节
debugDraw->SetFlags(flags);
scheduleUpdate();//每一帧都会调用一个叫update的方法,进行刷新屏幕

scheduleUpdate函数将会在每一帧中,调用一个update的方法。这个方法可以这么写:

  • view sourceprint?

    1.void HelloWorld::update(float dt){

    2.phyWorld->Step(0.03f,10,10);

    3.}

    这样,在每一帧的时候,都会进行屏幕刷新,物理引擎中的内容都变为可见形式的了。 
    Box2D速度与性能注意事项: 物理引擎实际上是比较占用硬件资源的,因为引擎中大量的刚体碰撞检测,各种作用力的效果计算都非常耗费CPU运算时间。尤其是当刚体数量大量增长时,Box2D的计算量将成几何级的增长。所以使用Box2物理引擎时,一定要注意性能优化以及提高仿真度的几个技巧。 (1)区分静态刚体和动态刚体:如果有个物体可以使用静态刚体来定义,那就一定不要使用动态刚体来定义它。因为静态刚体仅仅进行碰撞检测,不会考虑作用力对它的影响,相对于动态刚体来说,静态刚体的计算量会小很多,可以减少CPU的负担。 (2)启动动态刚体休眠属性:在物理引擎初始化时,可以通过设置是否允许动态刚体休眠来改善性能。如果设置允许动态刚体休眠,一些受到很小作用力或者没有受到作用力的刚体,它们会保持静止不动的状态,此时它们会进入休眠Sleep状态。进入Sleep状态的刚体,将不会进行物理运算,这样就可以减少运算时间,知道它们再次受到作用力处于运动状态后,才会从休眠状态被唤醒,继续参与物理计算。 (3)设置单位转换参数:游戏屏幕是以像素为单位长度的,而物理引擎是以米为单位长度的,这里就存在一个米与像素的单位换算关系。物理引擎有一个合理的单位工作范围,在Box2D中,物体的大小最好在0.1米10米之间,如果超过这个范围,物体的表现可能会变得不真实。所以设计游戏中的物体大小时,其像素单位也需要有一个合理的范围。我们通常将换算值设置为32,这样在游戏中32*32像素大小的物体,在物理引擎中它就是1米*1米大小,在这样一个合理的大小范围内,Box2D引擎的模拟效果会比较真实。 (4)设置子弹属性:动态刚体有时会高速移动,如果刚体在两帧之间移动的距离超过了碰撞物体的身长,那碰撞检测就失去了检测功能,出现刚体直接穿过障碍物的现象。所以如果某个刚体的运动速度很快时,需要设置此刚体为子弹类型,也就是高速移动的动态刚体类型,使用SetBullet(true)函数。这样的刚体在移动时会计算每一个单位的移动是否发生碰撞,不会发生直接穿过障碍物的现象。
    复杂多边形的代码生成工具: 当游戏物体的形状变得丰富而复杂时,手动书写代码定义物体外形是不现实的,这就需要借助一些有效的工具来定义物体的外形,vertexhelper软件是一款开源免费的定点绘制工具。通过可视化的界面可以绘制出任意一种你想要的多边形。而组成这些多边形的顶点数据,将会以C++代码的形式给出,经过拷贝粘贴,就可以将复杂的多边形生成代码放到自己的类中。

时间: 2024-10-11 07:25:57

cocos2dx-3.x物理引擎Box2D介绍的相关文章

HTML5之2D物理引擎 Box2D for javascript Games 系列 翻外篇--如何结合createJS应用box2d.js

太久没有更新了,新年回来工作,突然有收到网友的邮件提问,居然还有人在关注,惭愧,找了下电脑上还有一点儿存着,顺便先发这一个番外篇吧,好歹可以看到真实的效果,等我考完英语,一定会更新下一章,"愤怒的小鸟篇" 此篇,并不是书中的篇符,而是通过希望通过结合实际的canvas 绘图库实现box2d物理引擎在各绘图库上应用,绘图库网上有很多现成的 如:createjs, pixi.js 等,Egret或者其它游戏引擎有自己的物理引擎扩展库,所以就不说了. 现在通过之前的学习,基本掌握了刚体等基础

HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分

这是系列第二部分,之前部分在本博客中找 源码demo存放在https://github.com/willian12345/Box2D-for-Javascript-Games 向世界添加刚体 刚体(Bodies)是我们用Box2D创建物理游戏的重要对象.任何你可以移动的或交互 的对象都是刚体(Bodies). 愤怒的小鸟(Angry Birds)中创建的小鸟和小猪是刚 体,同样在图腾破坏者(Totem Destroyer)中的黄金神像和图腾砖块也是刚体. 本章将带你学习创建各种类型的Box2D刚

cocos2dx自带物理引擎-创建物理世界

首先在createScene()里 auto scene = Scene::createWithPhysics(); 创建带有物理的场景 然后再OnEnter里创建边界框 auto body = PhysicsBody::createEdgeBox( visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3); 创建一个节点承载边界 auto edgeShape = Node::create(); 将图形和刚刚创建的世界绑定 edgeShape->setPhysi

python下的Box2d物理引擎的配置

/******************************* I come back! 由于已经大四了,正在找工作 导致了至今以来第二长的时间内没有更新博客.向大家表示道歉 *******************************/ 前言 Box2d物理引擎 Box2d是一款开源的2d物理引擎,存在很多的版本,C++,Java,html5和python等等 著名的愤怒的小鸟就是使用了这款物理引擎进行开发的 目前也有很多的2d游戏引擎内置了对Box2d物理引擎的支持,比如cocos2d,

iOS开发——高级篇——UIDynamic 物理引擎

一.UIDynamic 1.简介什么是UIDynamicUIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象重力.弹性碰撞等现象 物理引擎的价值广泛用于游戏开发,经典成功案例是“愤怒的小鸟”让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏 知名的2D物理引擎Box2dChipmunk 2.使用步骤要想使用UIDynamic来实现物理仿真效果,大致的步骤如下

物理引擎Havok教程(一)搭建开发环境

物理引擎Havok教程(一)搭建开发环境 网上关于Havok的教程实在不多,并且Havok学习起来还是有一定难度的,所以这里写了一个系列教程,希望可以帮到读者.这是第一期. 一.Havok物理引擎简单介绍       Havok引擎,全称为Havok游戏动力开发工具包(Havok Game Dynamics SDK),一般称为Havok,是一个用于物理系统方面的游戏引擎,为电子游戏所设计,注重在游戏中对于真实世界的模拟.使用碰撞功能的Havok引擎能够让很多其它真实世界的情况以最大的拟真度反映在

实例介绍Cocos2d-x中Box2D物理引擎:HelloBox2D

我们通过一个实例介绍一下,在Cocos2d-x 3.x中使用Box2D物理引擎的开发过程,熟悉这些API的使用.这个实例运行后的场景如图所示,当场景启动后,玩家可以触摸点击屏幕,每次触摸时候,就会在触摸点生成一个新的精灵,精灵的运行自由落体运动. HelloBox2D实例 使用Box2D引擎进行开发过程,如图12-15所示.下面我们就按照这个步骤介绍一下代码部分.首先看一下看HelloWorldScene.h文件,它的代码如下: [html] view plaincopy #ifndef __H

实例介绍Cocos2d-x中Box2D物理引擎:使用关节

下面我们将使用Box2D物理引擎技术进行重构.使得关节能够掌握如何在Box2D使用关节约束.HelloWorldScene.cpp中与使用关节的相关代码如下: [html] view plaincopy void HelloWorld::addNewSpriteAtPosition(Vec2 p) { log("Add sprite %0.2f x %02.f",p.x,p.y); //创建物理引擎精灵对象A auto spriteA = Sprite::create("Bo

实例介绍Cocos2d-x中Box2D物理引擎:碰撞检测

在Box2D中碰撞事件通过实现b2ContactListener类函数实现,b2ContactListener是Box2D提供的抽象类,它的抽象函数:virtual void BeginContact(b2Contact* contact).两个物体开始接触时会响应,但只调用一次.virtual void EndContact(b2Contact* contact).分离时响应.但只调用一次.virtual void PreSolve(b2Contact* contact, const b2Ma