zoukankan      html  css  js  c++  java
  • 实例介绍Cocos2d-x中Box2D物理引擎:HelloBox2D

    我们通过一个实例介绍一下,在Cocos2d-x 3.x中使用Box2D物理引擎的开发过程,熟悉这些API的使用。这个实例运行后的场景如图所示,当场景启动后,玩家可以触摸点击屏幕,每次触摸时候,就会在触摸点生成一个新的精灵,精灵的运行自由落体运动。
      



    HelloBox2D实例

    使用Box2D引擎进行开发过程,如图12-15所示。下面我们就按照这个步骤介绍一下代码部分。首先看一下看HelloWorldScene.h文件,它的代码如下:

    [html] view plaincopy
     
    1. #ifndef __HELLOWORLD_SCENE_H__  
    2. #define __HELLOWORLD_SCENE_H__  
    3.   
    4.   
    5. #include "cocos2d.h"  
    6. #include "Box2D/Box2D.h"                                                ①  
    7.   
    8.   
    9. #define PTM_RATIO 32                                                    ②  
    10.   
    11.   
    12. class HelloWorld : public cocos2d::Layer  
    13. {  
    14.     b2World* world;                                                     ③  
    15.   
    16.   
    17. public:  
    18.     static cocos2d::Scene* createScene();  
    19.     virtual bool init();    
    20.   
    21.   
    22.     virtual void update(float dt);                                              ④  
    23.     virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);            ⑤  
    24.   
    25.   
    26.     CREATE_FUNC(HelloWorld);  
    27.       
    28.     void initPhysics();                                                     ⑥  
    29.     void addNewSpriteAtPosition(cocos2d::Vec2 p);                               ⑦  
    30.   
    31.   
    32. };  
    33.   
    34.   
    35. #endif // __HELLOWORLD_SCENE_H__  



    上述第①行代码#include "Box2D/Box2D.h"是引入使用Box2D引擎需要头文件。第②行代码#define PTM_RATIO 32是定义宏PTM_RATIO,PTM_RATIO是屏幕上多少像素为1米,32表示屏幕上32像素表示1米,在Box2D中单位使用MKS公制系统,即:长度单位采用米,质量单位采用千克,时间单位采用秒。
    代码第③行world是声明物理世界b2World成员变量。第④行代码是游戏循环函数。第⑤行代码是触摸点击响应函数。第⑥行代码是声明初始化物理引擎函数initPhysics。第⑦行是声明addNewSpriteAtPosition函数,是在触摸点创建一个精灵对象。
    HelloWorldScene.cpp中HelloWorld::init()函数代码如下:

    [html] view plaincopy
     
    1. bool HelloWorld::init()  
    2. {  
    3.     if ( !Layer::init() )  
    4.     {  
    5.         return false;  
    6.     }  
    7.   
    8.   
    9.     Size visibleSize = Director::getInstance()->getVisibleSize();  
    10.     Vec2 origin = Director::getInstance()->getVisibleOrigin();  
    11.   
    12.   
    13.     // 初始化物理引擎  
    14.     this->initPhysics();                                                 ①  
    15.   
    16.   
    17.     setTouchEnabled(true);                                                
    18.     //设置为单点触摸  
    19.     setTouchMode(Touch::DispatchMode::ONE_BY_ONE);                            
    20.     //开始游戏循环  
    21.     scheduleUpdate();                                                   ②  
    22.   
    23.   
    24.     return true;  
    25. }  



    上述代码第①行调用initPhysics()函数初始化物理引擎。第②行代码scheduleUpdate()是开始游戏循环,一旦开启游戏循环就会回调HelloWorld::update(float dt)函数。
    HelloWorldScene.cpp中初始化物理引擎HelloWorld::initPhysics()函数代码如下:

    [html] view plaincopy
     
    1. void HelloWorld::initPhysics()  
    2. {  
    3.     Size s = Director::getInstance()->getVisibleSize();  
    4.       
    5.     //重力参数  
    6.     b2Vec2 gravity;                                                         ①  
    7.     gravity.Set(0.0f, -10.0f);                                              ②  
    8.     //创建世界  
    9.     world = new b2World(gravity);                                           ③  
    10.     // 允许物体是否休眠  
    11.     world->SetAllowSleeping(true);                                           ④  
    12.     // 开启连续物理测试  
    13.     world->SetContinuousPhysics(true);                                       ⑤  
    14.       
    15.     //地面物体定义  
    16.     b2BodyDef groundBodyDef;                                            ⑥  
    17.     //左下角  
    18.     groundBodyDef.position.Set(0, 0);                                       ⑦  
    19.   
    20.   
    21.     //创建地面物体  
    22.     b2Body* groundBody = world->CreateBody(&groundBodyDef);                      ⑧  
    23.   
    24.   
    25.     //定义一个有边的形状  
    26.     b2EdgeShape groundBox;                                              ⑨  
    27.   
    28.   
    29.     // 底部  
    30.     groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));                        ⑩  
    31.     //使用夹具固定形状到物体上  
    32.     groundBody->CreateFixture(&groundBox,0);                                 ⑪  
    33.   
    34.   
    35.     // 顶部  
    36.     groundBox.Set(b2Vec2(0,s.height/PTM_RATIO),   
    37.                     b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));                    
    38.     groundBody->CreateFixture(&groundBox,0);                                   
    39.   
    40.   
    41.     // 左边  
    42.     groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0));                           groundBody->CreateFixture(&groundBox,0);                                   
    43.   
    44.   
    45.     // 右边  
    46.     groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO),   
    47.                         b2Vec2(s.width/PTM_RATIO,0));                                 
    48.     groundBody->CreateFixture(&groundBox,0);                                   
    49.   
    50.   
    51. }  



    代码第①行b2Vec2 gravity是声明重力变量,b2Vec2是一个二维矢量,它的两个属性为浮点数x和y,表示在x轴和y轴方向的矢量。第②行代码gravity.Set(0.0f, -10.0f)是设置矢量值,其中(0.0f, -10.0f)表示只有重力作用物体,-10.0f表示沿着y轴向下。
    第③行代码world = new b2World(gravity)是创建物理世界b2World对象,这里采用了new创建物理世界对象,在C++中new关键字分配内存,释放内存是delete关键字。World是成员变量,需要自己释放成员变量一般是在析构函数中释放,代码如下:
    HelloWorld::~HelloWorld()
    {
        CC_SAFE_DELETE(world); 
    }
    其中CC_SAFE_DELETE(world)是安全释放world变量,CC_SAFE_DELETE宏代表安全释放内存。
    第④行代码world->SetAllowSleeping(true)是允许物体睡眠与否,如果允许休眠,可以提高物理世界中物体的处理效率,只有在发生碰撞时才唤醒该对象。
    第⑤行代码world->SetContinuousPhysics(true)是开启连续物理测试[ 开启连续物理测试,这是因为计算机只能把一段连续的时间分成许多离散的时间点,再对每个时间点之间的行为进行演算,如果时间点的分割不够细致,速度较快的两个物体碰撞时就可能会产生“穿透”现象,开启连续物理将启用特殊的算法来避免该现象。]。
    第⑥行代码是声明形状定义(b2BodyDef)变量。第⑦行代码groundBodyDef.position.Set(0, 0)是设置形状的位置。第⑧行代码b2Body* groundBody = world->CreateBody(&groundBodyDef)是通过形状定义变量groundBodyDef创建地面物体。
    第⑨行代码是声明一个有边形状定义b2EdgeShape变量,第⑩行代码groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0))是设置有边形状的开始位置(0,0)和结束位置(s.width/PTM_RATIO,0),s.width/PTM_RATIO是将像素换算成米。第⑪行代码是使用夹具固定形状到物体上。用类似的函数定义顶部、左边和右边的物体。
    HelloWorldScene.cpp中创建精灵HelloWorld::addNewSpriteAtPosition函数代码如下:

    [html] view plaincopy
     
    1. void HelloWorld::addNewSpriteAtPosition(Vec2 p)  
    2. {      
    3.     log("Add sprite %0.2f x %02.f",p.x,p.y);  
    4.   
    5.   
    6.     //创建物理引擎精灵对象  
    7.     auto sprite = Sprite::create("BoxA2.png");                                  ①  
    8.     sprite->setPosition( Vec2( p.x, p.y) );  
    9.     this->addChild(sprite);  
    10.   
    11.   
    12.     //物体定义  
    13.     b2BodyDef bodyDef;                                                  ②  
    14.     bodyDef.type = b2_dynamicBody;                                          ③  
    15.     bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);                         ④  
    16.     b2Body *body = world->CreateBody(&bodyDef);                              ⑤  
    17.     body->SetUserData(sprite);                                               ⑥  
    18.   
    19.   
    20.     // 定义2米见方的盒子形状  
    21.     b2PolygonShape dynamicBox;                                          ⑦  
    22.     dynamicBox.SetAsBox(1, 1);                                          ⑧  
    23.   
    24.   
    25.     // 夹具定义  
    26.     b2FixtureDef fixtureDef;                                                ⑨  
    27.     //设置夹具的形状  
    28.     fixtureDef.shape = &dynamicBox;                                         ⑩  
    29.     //设置密度  
    30.     fixtureDef.density = 1.0f;                                              ⑪  
    31.     //设置摩擦系数  
    32.     fixtureDef.friction = 0.3f;                                             ⑫  
    33.     //使用夹具固定形状到物体上    
    34.     body->CreateFixture(&fixtureDef);                                        ⑬  
    35.   
    36.   
    37. }  



    上述代码第①行是创建精灵(Sprite)对象,精灵(Sprite)对象与物理引擎物体是没有关系,我们需要在游戏循环函数中更新。
    代码第②行是声明动态物体定义变量,代码第③行bodyDef.type = b2_dynamicBody是设置物体类型为动态物体,物体分为静态和动态物体。第④行代码是设置物体的位置,它的单位是米。第⑤行代码b2Body *body = world->CreateBody(&bodyDef)是创建物体对象。第⑥行代码body->SetUserData(sprite)是将精灵放置到物体的UserData属性中,这样便于我们从物体中获取相关联的物体。
    第⑦行代码b2PolygonShape dynamicBox是声明多边形形状定义变量。第⑧行代码dynamicBox.SetAsBox(1, 1)是设置多边形为矩形盒子形状,由于坐标原点在盒子的左下角,SetAsBox是设置盒子的中心为(1,1),那么这个盒子的就是2米见方的大小。
    第⑨行代码是b2FixtureDef fixtureDef是声明夹具定义变量。第⑩行代码是设置夹具的形状。第⑪行代码fixtureDef.density = 1.0f是设置形状的密度。第⑫行代码fixtureDef.friction = 0.3f是设置摩擦系数,范围是0.0~1.0之间。第⑬行代码body->CreateFixture(&fixtureDef) 是使用夹具固定形状到物体上,这样物体就有了形状。
    HelloWorldScene.cpp中游戏循环函数HelloWorld::update代码如下:

    [html] view plaincopy
     
    1. void HelloWorld::update(float dt)  
    2. {  
    3.     float timeStep = 0.03f;   
    4.     int32 velocityIterations = 8;     
    5.     int32 positionIterations = 1;     
    6.   
    7.   
    8.     world->Step(timeStep, velocityIterations, positionIterations);  
    9.   
    10.   
    11.     for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())                       ①  
    12.     {  
    13.         if (b->GetUserData() != nullptr) {                                     
    14.             Sprite* sprite = (Sprite*)b->GetUserData();    
    15.             sprite->setPosition( Vec2( b->GetPosition().x *   
    16.                 PTM_RATIO, b->GetPosition().y * PTM_RATIO) );      
    17.             sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) );  
    18.         }  
    19.     }                                                                           ②  
    20. }  



    其中代码①~②这段代码可以同步物理引擎中的物体与精灵位置和状态。

    更多内容请关注国内第一本Cocos2d-x 3.2版本图书《Cocos2d-x实战:C++卷》
    本书交流讨论网站:http://www.cocoagame.net
    更多精彩视频课程请关注智捷课堂Cocos课程:http://v.51work6.com
    欢迎加入Cocos2d-x技术讨论群:257760386

    欢迎关注智捷iOS课堂微信公共平台

  • 相关阅读:
    构建之法读书笔记04
    团队冲刺06
    12.23——周总
    团队冲刺05
    团队冲刺04
    用户场景分析
    团队冲刺03
    返回一个二维数组中最大联通子数组的和
    团队冲刺02
    FFT/NTT基础题总结
  • 原文地址:https://www.cnblogs.com/iOS-Blog/p/4011992.html
Copyright © 2011-2022 走看看