瘸腿蛤蟆笔记40-cocos2d-x-3.2 Box2d物理引擎实现作用力

瘸腿蛤蟆原创笔记,欢迎转载,转载请标明出处: http://blog.csdn.net/notbaron/article/details/39161771

源码下载: http://download.csdn.net/detail/notbaron/7886717

上篇回顾

本篇名言:有两种东西,我们对它们的思考愈是深沉和持久,它们所唤起的那种愈来愈大的惊奇和敬畏就会充溢我们的心灵,这就是繁星密布的苍穹和我心中的道德律。[康德]

上篇中,蛤蟆学习了Box2d物理引擎中的自由落体实现,接下去蛤蟆继续学习其他的Box2d引擎的使用。这次我们使用Box2d物理引擎来学习作用力的情况。

        

理论介绍

         本次的理论知识基本和上次基本一致,需要补充如下内容:

b2QueryCallback这个类是用于AABB查询的。就一个虚函数virtualbool ReportFixture(b2Fixture* fixture) = 0; 在查询AABB的时候,一有发现Fixture就会被调用,通过返回FALSE来结束查询 AABB。AABB类可以理解为一个方形的区域。

EventDispatcher  _eventDispatcher事件分发器,这个变量在CCNode.h源文件中定义。继承于CCNode的类可以直接使用,用于指定事件处理的监听器。用于分发各种事件。

 

 

EventListener事件监听器类,该类继承于ref类。是事件监听的基本类。我们通过不同的回调函数来自定义监听。

 

b2MouseJoint类,这个鼠标接口关节类。在Body上指定一个点用于跟踪世界的点。这是个软约束有最大的力。允许约束进行拉伸,不需要巨大的力。这个类并没有在手册中出现。该类继承与base joint类,用于通过不同的方式约束两个Body。可以设置限制和电机。

 

b2Transform类,在b2Math.h头文件中定义,实现平移和旋转,用来表示钢架的位置和方向。其中有两个变量p和q。其中p表示位置有x和y坐标,q表示角度,q中有两个变量s和c分别是sin和cos值。

b2FrictionJointDef,该类定义在b2FrictionJoint.h头文件中,定义摩擦关节。包含一个结构体如下:b2FrictionJointDef()

         {

                   type= e_frictionJoint;

                   localAnchorA.SetZero();

                   localAnchorB.SetZero();

                   maxForce= 0.0f;

                   maxTorque= 0.0f;

         }

Type表示 关节类型,Box2d支持如下关节类型:

e_unknownJoint,

e_revoluteJoint,

e_prismaticJoint,

e_distanceJoint,

e_pulleyJoint,

e_mouseJoint,

e_gearJoint,

e_wheelJoint,

e_weldJoint,

e_frictionJoint,

e_ropeJoint,

e_motorJoint

localAnchorA设置BodyA的锚点

localAnchorB设置BodyB的锚点

maxForce设置最大摩擦力

maxTorque设置最大力矩

 

具体步骤

         我们接着使用上次学习的模板,大家可以在

 

中下载这段代码。

1、修改RayCastNew的构造函数如下

RayCastNew()

         {

b2MouseJoint* m_mouseJoint;                 //定义一个b2MouseJoint变量,鼠标接口类。

                  b2Vec2 m_mouseWorld;

                   b2Body* m_body;

                   b2Vec2 gravity;

                   gravity.Set(0.0f,0.0f);

                   m_world= new b2World(gravity);

                   constfloat32k_restitution = 0.4f;

 

                   b2Body* ground;

                   {

                            b2BodyDef bd;

                            bd.position.Set(0.0f,20.0f);

                            ground= m_world->CreateBody(&bd);

                            b2EdgeShape shape;

                            b2FixtureDef sd;

                            sd.shape= &shape;

                            sd.density= 0.0f;

                            sd.restitution= k_restitution;

                            // Left vertical

                            shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(-20.0f, 20.0f));

                            ground->CreateFixture(&sd);

 

                            // Right vertical

                            shape.Set(b2Vec2(20.0f, -20.0f),b2Vec2(20.0f, 20.0f));

                            ground->CreateFixture(&sd);

                            // Top horizontal

                            shape.Set(b2Vec2(-20.0f, 20.0f),b2Vec2(20.0f, 20.0f));

                            ground->CreateFixture(&sd);

                            // Bottom horizontal

                            shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(20.0f, -20.0f));

                            ground->CreateFixture(&sd);

                   }

 

                   {

                            b2Transform xf1;

                            xf1.q.Set(0.3524f* b2_pi);

                            xf1.p= xf1.q.GetXAxis();

                            b2Vec2 vertices[3];

                            vertices[0]= b2Mul(xf1, b2Vec2(-1.0f, 0.0f));

                            vertices[1]= b2Mul(xf1, b2Vec2(1.0f, 0.0f));

                            vertices[2]= b2Mul(xf1, b2Vec2(0.0f, 0.5f));

                            b2PolygonShape poly1;

                            poly1.Set(vertices,3);

                            b2FixtureDef sd1;

                            sd1.shape= &poly1;

                            sd1.density= 4.0f;

 

                            b2Transform xf2;

                            xf2.q.Set(-0.3524f* b2_pi);

                            xf2.p= -xf2.q.GetXAxis();

 

                            vertices[0]= b2Mul(xf2, b2Vec2(-1.0f, 0.0f));

                            vertices[1]= b2Mul(xf2, b2Vec2(1.0f, 0.0f));

                            vertices[2]= b2Mul(xf2, b2Vec2(0.0f, 0.5f));

                            b2PolygonShape poly2;

                            poly2.Set(vertices,3);

                            b2FixtureDef sd2;

                            sd2.shape= &poly2;

                            sd2.density= 2.0f;

                            b2BodyDef bd;

                            bd.type= b2_dynamicBody;

                            bd.angularDamping= 2.0f;

                            bd.linearDamping= 0.5f;

 

                            bd.position.Set(0.0f,2.0);

                            bd.angle= b2_pi;

                            bd.allowSleep= false;

                            m_body= m_world->CreateBody(&bd);

                            m_body->CreateFixture(&sd1);

                            m_body->CreateFixture(&sd2);

                   }

                   {

                            b2PolygonShape shape;

                            shape.SetAsBox(0.5f,0.5f);

                            b2FixtureDef fd;

                            fd.shape= &shape;

                            fd.density= 1.0f;

                            fd.friction= 0.3f;

 

                            for (int i =0; i < 10; ++i)

                            {

                                     b2BodyDef bd;

                                     bd.type= b2_dynamicBody;

 

                                     bd.position.Set(0.0f,5.0f + 1.54f * i);

                                     b2Body* body = m_world->CreateBody(&bd);

 

                                     body->CreateFixture(&fd);

 

                                     float32 gravity = 10.0f;

                                     float32 I = body->GetInertia();

                                     float32 mass = body->GetMass();

 

                                     // For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)

                                     float32 radius = b2Sqrt(2.0f* I / mass);

 

                                     b2FrictionJointDef jd;

                                     jd.localAnchorA.SetZero();

                                     jd.localAnchorB.SetZero();

                                     jd.bodyA= ground;

                                     jd.bodyB= body;

                                     jd.collideConnected= true;

                                     jd.maxForce= mass * gravity;

                                     jd.maxTorque= mass * radius * gravity;

 

                                     m_world->CreateJoint(&jd);

                            }

                   }

                   GLESDebugDraw * _debugDraw =newGLESDebugDraw(1.5);

                   m_world->SetDebugDraw(_debugDraw);

                   uint32 flags = 0;

                   flags+=b2Draw::e_shapeBit;

                   _debugDraw->SetFlags(flags);

                   m_world->DrawDebugData();

         }

 

2、添加鼠标回调函数

在文件Box2dh中定义,Box2d.cpp中实现。

bool Box2D::onTouchBegan(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation();   

    auto nodePosition = convertToNodeSpace( touchLocation ); 

    return m_test->MouseDown(b2Vec2(nodePosition.x,nodePosition.y));

}

 

void Box2D::onTouchMoved(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation();   

    auto nodePosition = convertToNodeSpace( touchLocation );   

   m_test->MouseMove(b2Vec2(nodePosition.x,nodePosition.y));       

}

 

void Box2D::onTouchEnded(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation();   

    auto nodePosition = convertToNodeSpace( touchLocation );   

   m_test->MouseUp(b2Vec2(nodePosition.x,nodePosition.y));

}

3、真正处理的鼠标回调函数

这些函数放在RayCastNew.h文件中。

virtual bool MouseDown(const b2Vec2& p){

                   m_mouseWorld= p;

                   if (m_mouseJoint !=nullptr)

                   {

                            return false;

                   }

                   // Make a small box.

                   b2AABB aabb;

                   b2Vec2 d;

                   d.Set(0.001f,0.001f);

                   aabb.lowerBound= p - d;

                   aabb.upperBound= p + d;

                   // Query the world for overlapping shapes.

                   QueryCallback callback(p);

                  m_world->QueryAABB(&callback,aabb);

                   if (callback.m_fixture)

                   {

                            b2Body* body = callback.m_fixture->GetBody();

                            b2MouseJointDef md;

                            md.bodyA= ground;

                            md.bodyB= body;

                            md.target= p;

                            md.maxForce= 1000.0f * body->GetMass();

                            m_mouseJoint= (b2MouseJoint*)m_world->CreateJoint(&md);

                            body->SetAwake(true);

                            return true;

                   }

                   returnfalse;     

         }

         virtualvoidMouseUp(constb2Vec2& p)

         {

                   if (m_mouseJoint)

                   {

                            m_world->DestroyJoint(m_mouseJoint);

                            m_mouseJoint= nullptr;

                   }

                  

         }

         void MouseMove(constb2Vec2& p)

         {

                   m_mouseWorld= p;

 

                   if (m_mouseJoint)

                   {

                            m_mouseJoint->SetTarget(p);

                   }

         }

4、HelloWorld.cpp文件中增加监听

    auto listener =EventListenerTouchOneByOne::create();

   listener->setSwallowTouches(true);

   listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);

   listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);

   _eventDispatcher->addEventListenerWithFixedPriority(listener, 1);

_touchListener= listener;

具体移动函数如下

void HelloWorld::onTouchMoved(Touch*touch, Event* event)

{

    auto diff =touch->getDelta();   

    auto node = getChildByTag( 2 );

    auto currentPos = node->getPosition();

   node->setPosition(currentPos + diff);

}

 

 

 

代码解释

         代码总体思路是这样的,

         HelloWorld.cpp中的Layer,会调用Box2d这个Layer。然后在Box2d这个Layer中,调用RayCastNew类,该类继承于b2ContactListenerRayCastNew中的构造函数完成所有Body的定义及创建。该类中刚还定义了几个处理鼠标移动的函数MouseUp, MouseMove, MouseDown. 在Box2d的L ayer中注册了鼠标函数,会调用RayCastNew中的MouseUp, MouseMove, MouseDown函数。而HelloWorld这个 Layer也注册了鼠标函数,他们实现的是Box2d这个Layer的移动,方便显示。其他物理碰撞都是有Box2d源代码实现的哈,蛤蟆这里就先不管他们了。

 

1、构造函数介绍

RayCastNew()

         {

b2MouseJoint* m_mouseJoint; //定义一个b2MouseJoint变量,鼠标接口类。

                   b2Vec2 m_mouseWorld; //定义一个变量

                   b2Body* m_body;//定义一个Body变量

                   b2Vec2 gravity;//定义一个重力向量

                   gravity.Set(0.0f,0.0f);//设置重力向量变量值

                   m_world= new b2World(gravity);//通过重力变量创建物理世界。

                   constfloat32k_restitution = 0.4f; //定义一个回复变量0.4

 

                   b2Body* ground;//定义一个地面的Body

                   {

                            b2BodyDef bd;//定义Body参数变量

                            bd.position.Set(0.0f,20.0f);//设置该Body位置

                            ground= m_world->CreateBody(&bd);//创建该Body,其他参数默认,默认是静态Body

                            b2EdgeShape shape;//定义边缘形状shape

                            b2FixtureDef sd;//定义fixture变量

                            sd.shape= &shape;//设置FIXTURE的shape

                            sd.density= 0.0f;//设置fixture密度

                            sd.restitution= k_restitution;//设置fixture回复力

                            shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(-20.0f, 20.0f));//设置shape的形状的左边线

                            ground->CreateFixture(&sd);//将Body和fixture绑定

                            shape.Set(b2Vec2(20.0f, -20.0f),b2Vec2(20.0f, 20.0f)); //设置shape的形状的右边边线

                            ground->CreateFixture(&sd); //将Body和fixture绑定

                            shape.Set(b2Vec2(-20.0f, 20.0f),b2Vec2(20.0f, 20.0f)); //设置shape的形状的上边线

                            ground->CreateFixture(&sd); //将Body和fixture绑定

                            shape.Set(b2Vec2(-20.0f, -20.0f),b2Vec2(20.0f, -20.0f)); //设置shape的形状的下边线

                            ground->CreateFixture(&sd); //将Body和fixture绑定

                   }

 

                   {

                            b2Transform xf1; //定义一个b2Transform变量

                            xf1.q.Set(0.3524f* b2_pi);//设置弧度为0.352*3.14=1.1

                            xf1.p= xf1.q.GetXAxis();//设置p

                            b2Vec2 vertices[3];//定义数组

                            vertices[0]= b2Mul(xf1, b2Vec2(-1.0f, 0.0f)); //这里只要知道,是两个点处理之后,得到一个坐标点即可

                            vertices[1]= b2Mul(xf1, b2Vec2(1.0f, 0.0f));

                            vertices[2]= b2Mul(xf1, b2Vec2(0.0f, 0.5f));

                            b2PolygonShape poly1;//定义多边形变量

                            poly1.Set(vertices,3); //设置多边形变量

                            b2FixtureDef sd1;//定义一个Fixture定义变量

                            sd1.shape= &poly1;//设置Fixture的shape为多边形

                            sd1.density= 4.0f; //设置Fixture的密度

 

                            b2Transform xf2; //定义另一个b2Transform变量

                            xf2.q.Set(-0.3524f* b2_pi); //设置弧度为-0.352*3.14=-1.1

                            xf2.p= -xf2.q.GetXAxis();//设置p

 

                            vertices[0]= b2Mul(xf2, b2Vec2(-1.0f, 0.0f));

                            vertices[1]= b2Mul(xf2, b2Vec2(1.0f, 0.0f));

                            vertices[2]= b2Mul(xf2, b2Vec2(0.0f, 0.5f));

                            b2PolygonShape poly2; //定义多边形变量

                            poly2.Set(vertices,3); //设置多边形变量

                            b2FixtureDef sd2; //定义一个Fixture定义变量

                            sd2.shape= &poly2; //设置Fixture的shape为多边形

                            sd2.density= 2.0f; //设置Fixture的密度

                            b2BodyDef bd;//定义一个Body定义的变量

                            bd.type= b2_dynamicBody; //设置Body为动态Body

                            bd.angularDamping= 2.0f; //设置Body为角度阻尼为2.0

                            bd.linearDamping= 0.5f; //设置Body为线性阻尼为0.5.

 

                            bd.position.Set(0.0f,2.0); //设置Body位置

                            bd.angle= b2_pi; //设置Body的角度为π(就是180°)

                            bd.allowSleep= false; //设置Body为不能睡眠

                            m_body= m_world->CreateBody(&bd);//创建该Body

                            m_body->CreateFixture(&sd1);//绑定body和fixture.

                            m_body->CreateFixture(&sd2); //绑定body和fixture.

                   }

                   {

                            b2PolygonShape shape; //定义一个多边形变量

                            shape.SetAsBox(0.5f,0.5f);//设置为正方形

                            b2FixtureDef fd; //定义一个Body定义的变量

                            fd.shape= &shape; //设置Fixture的shape为多边形

                            fd.density= 1.0f; //设置Fixture的密度

                            fd.friction= 0.3f; //设置Fixture的摩擦力

 

                            for (int i =0; i < 10; ++i) //循环,创建10个Body

                            {

                                     b2BodyDef bd; 定义一个Body定义的变量

                                     bd.type= b2_dynamicBody; //定义一个Body为动态Body

 

                                     bd.position.Set(0.0f,5.0f + 1.54f * i); //设置Body位置

                                     b2Body* body = m_world->CreateBody(&bd); //更具定义创建Body

                                     body->CreateFixture(&fd);// //绑定body和fixture.

 

                                     float32 gravity = 10.0f;

                                     float32 I = body->GetInertia();//获得      Body的转动惯性,物理中我们知道

                                     float32 mass = body->GetMass();//获取Body质量

                                     // 对于一个圆圈: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)

                                     float32 radius = b2Sqrt(2.0f* I / mass);//通过转动惯量,获取半径。

 

                                     b2FrictionJointDef jd; //定义一个摩擦关节

                                     jd.localAnchorA.SetZero();//设置BodyA的锚点为0

                                     jd.localAnchorB.SetZero();//设置BodyB的锚点为0

                                     jd.bodyA= ground; //设置BodyA为地面

                                     jd.bodyB= body; //设置BodyB的Body

                                     jd.collideConnected= true;//设置关节允许碰撞

                                     jd.maxForce= mass * gravity;//设置最大力是质量乘以重力

                                     jd.maxTorque= mass * radius * gravity; //设置最大力是质量乘以半径和重力

                                     m_world->CreateJoint(&jd);//根据关节定义创建这个关节(循环中每个Body都和Groud连接摩擦关节)

                            }

                   }

                   GLESDebugDraw * _debugDraw =newGLESDebugDraw(1.5);//定义GLESDebugDraw类,用于画底面

                   m_world->SetDebugDraw(_debugDraw);//注册GLESDebugDraw

                   uint32 flags = 0;

                   flags+=b2Draw::e_shapeBit;//设置需要画的内容

                   _debugDraw->SetFlags(flags);//加载需要画内容的位

                   m_world->DrawDebugData();//画出地面Body.

         }

 

2、鼠标回调函数介绍

bool Box2D::onTouchBegan(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation(); //获取触摸位置坐标

    auto nodePosition = convertToNodeSpace( touchLocation );  //转化坐标系

    return m_test->MouseDown(b2Vec2(nodePosition.x,nodePosition.y));//调用MouseDown函数

}

void Box2D::onTouchMoved(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation();    //获取触摸位置坐标

    auto nodePosition = convertToNodeSpace( touchLocation ); //转化坐标系

   m_test->MouseMove(b2Vec2(nodePosition.x,nodePosition.y));  //调用MouseMove函数

}

void Box2D::onTouchEnded(Touch*touch, Event* event)

{

    auto touchLocation =touch->getLocation();    //获取触摸位置坐标

    auto nodePosition = convertToNodeSpace( touchLocation ); //转化坐标系

   m_test->MouseUp(b2Vec2(nodePosition.x,nodePosition.y)); //调用MouseMove函数

}

3、真正处理的鼠标回调函数

virtual bool MouseDown(const b2Vec2& p){

                   m_mouseWorld= p; //传递按下鼠标的点,给变量m_mouseWorld

                   if (m_mouseJoint !=nullptr) //如果m_mouseJoint变量不为空,就退出

                   {

                            return false;

                   }

                   // Make a small box.

                   b2AABB aabb;//定义变量b2AABB,b2AABB是一个轴对齐的方形。

                   b2Vec2 d; //定义一个变量

                   d.Set(0.001f,0.001f);//设置变量的值

                   aabb.lowerBound= p - d;//根据数据按下的点,设置一个方形,设置aabb 的下限

                   aabb.upperBound= p + d;// 根据数据按下的点,设置一个方形,设置aabb 的上限。

                   // Query the world for overlapping shapes.

                   QueryCallback callback(p); //定义QueryCallback类,如果鼠标点上有Fixture就会调用ReportFixture函数并返回

                   m_world->QueryAABB(&callback,aabb);//调用QueryAABB,该函数在b2world.cpp中定义

                   if (callback.m_fixture) //如果点击的有fixture,这个变量在callback中

                   {

                            b2Body* body = callback.m_fixture->GetBody(); //通过Fixture获取body.

                            b2MouseJointDef md;//定义鼠标关节变量

                            md.bodyA= ground; //关节的一个BodyA是地面

                            md.bodyB= body; //关节的一个BodyB是Body

                            md.target= p;//定义关节的目标是鼠标点

                            md.maxForce= 1000.0f * body->GetMass();//定义关节的最大力

                            m_mouseJoint= (b2MouseJoint*)m_world->CreateJoint(&md); //根据关节定义创建一个鼠标关节。

                            body->SetAwake(true);//唤醒这个Body,我们可以看到唤醒和睡眠的时候Body颜色是不一样的。

                            return true;

                   }

                   returnfalse;     

         }

         virtualvoidMouseUp(constb2Vec2& p)

         {

                   if (m_mouseJoint) //如果关节变量不为空

                   {

                            m_world->DestroyJoint(m_mouseJoint);//毁掉该关节

                            m_mouseJoint= nullptr;//并赋值为null

                   }

                  

         }

         void MouseMove(constb2Vec2& p)

         {

                   m_mouseWorld= p;//获取鼠标移动到的点

                   if (m_mouseJoint)//如果鼠标关节不为空

                   {

                            m_mouseJoint->SetTarget(p);//重新设置鼠标关节的目标为新的点。

                   }

         }

4、HelloWorld.cpp中的监听

如下代码主要是实现移动b2BOD 这个Layer的,当然大家可以去掉试试,去掉的话就不能移动了。

   auto listener =EventListenerTouchOneByOne::create(); //创建一个监听变量

   listener->setSwallowTouches(true);//设置触摸容许传递

   listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);//设置具体监听函数

   listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this); //设置具体监听函数

   _eventDispatcher->addEventListenerWithFixedPriority(listener, 1);//分发监听设置

    _touchListener= listener;//设置监听器

具体移动函数如下

void HelloWorld::onTouchMoved(Touch*touch, Event* event)

{

    auto diff =touch->getDelta();    //获取移动的差异距离

    auto node = getChildByTag( 2 );//获取tag为2 的 L ayer,这里就是box2d这个Layer

    auto currentPos = node->getPosition();//获取该node的位置

   node->setPosition(currentPos + diff);//设置该node的新位置

}

 

 

你可能感兴趣的:(瘸腿蛤蟆笔记40-cocos2d-x-3.2 Box2d物理引擎实现作用力)