我们通过一个实例介绍一下,在Cocos2d-x 3.x中使用Box2D物理引擎的开发过程,熟悉这些API的使用。这个实例运行后的场景如图所示,当场景启动后,玩家可以触摸点击屏幕,每次触摸时候,就会在触摸点生成一个新的精灵,精灵的运行自由落体运动。
使用Box2D引擎进行开发过程,如图12-15所示。下面我们就按照这个步骤介绍一下代码部分。首先看一下看HelloWorldScene.h文件,它的代码如下:
- #ifndef __HELLOWORLD_SCENE_H__
- #define __HELLOWORLD_SCENE_H__
- #include "cocos2d.h"
- #include "Box2D/Box2D.h" ①
- #define PTM_RATIO 32 ②
- class HelloWorld : public cocos2d::Layer
- {
- b2World* world; ③
- public:
- static cocos2d::Scene* createScene();
- virtual bool init();
- virtual void update(float dt); ④
- virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event); ⑤
- CREATE_FUNC(HelloWorld);
- void initPhysics(); ⑥
- void addNewSpriteAtPosition(cocos2d::Vec2 p); ⑦
- };
- #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()函数代码如下:
- bool HelloWorld::init()
- {
- if ( !Layer::init() )
- {
- return false;
- }
- Size visibleSize = Director::getInstance()->getVisibleSize();
- Vec2 origin = Director::getInstance()->getVisibleOrigin();
- // 初始化物理引擎
- this->initPhysics(); ①
- setTouchEnabled(true);
- //设置为单点触摸
- setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
- //开始游戏循环
- scheduleUpdate(); ②
- return true;
- }
上述代码第①行调用initPhysics()函数初始化物理引擎。第②行代码scheduleUpdate()是开始游戏循环,一旦开启游戏循环就会回调HelloWorld::update(float dt)函数。
HelloWorldScene.cpp中初始化物理引擎HelloWorld::initPhysics()函数代码如下:
- void HelloWorld::initPhysics()
- {
- Size s = Director::getInstance()->getVisibleSize();
- //重力参数
- b2Vec2 gravity; ①
- gravity.Set(0.0f, -10.0f); ②
- //创建世界
- world = new b2World(gravity); ③
- // 允许物体是否休眠
- world->SetAllowSleeping(true); ④
- // 开启连续物理测试
- world->SetContinuousPhysics(true); ⑤
- //地面物体定义
- b2BodyDef groundBodyDef; ⑥
- //左下角
- groundBodyDef.position.Set(0, 0); ⑦
- //创建地面物体
- b2Body* groundBody = world->CreateBody(&groundBodyDef); ⑧
- //定义一个有边的形状
- b2EdgeShape groundBox; ⑨
- // 底部
- groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0)); ⑩
- //使用夹具固定形状到物体上
- groundBody->CreateFixture(&groundBox,0); ⑪
- // 顶部
- groundBox.Set(b2Vec2(0,s.height/PTM_RATIO),
- b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));
- groundBody->CreateFixture(&groundBox,0);
- // 左边
- groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0)); groundBody->CreateFixture(&groundBox,0);
- // 右边
- groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO),
- b2Vec2(s.width/PTM_RATIO,0));
- groundBody->CreateFixture(&groundBox,0);
- }
代码第①行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函数代码如下:
- void HelloWorld::addNewSpriteAtPosition(Vec2 p)
- {
- log("Add sprite %0.2f x %02.f",p.x,p.y);
- border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; line-height: 18px; margin-bottom: 0