出处:https://www.evernote.com/shard/s262/sh/bf561bda-0458-4502-835b-903846f81d68/68e6634bddec74c5a2d5f8dd839eba0a
本例子使用的是cocos2d-x 3.0版本
- 首先创建一个cocos2d的c++工程,这里就不多做说明了!本例子的项目名称是MyGame
- 其次,在MyGame的工程设置里Build Settings下,把CC_ENABLE_CHIPMUNK_INTEGRATION=1改为CC_ENABLE_BOX2D_INTEGRATION=1,同样把项目所引入的cocos2d的工程里改一下,但cocos2d的工程修改的地方有点特别!如上图的工程,非target!
- 到此,box2d的环境已经设定好了!
现在工程已经创建完成了,环境也设定好了,下代码工程:
创建一个layer,用于放置所有的box2d对像和项目的精灵
其中的头文件如下:
// // Box2dTest.h // MyGame // // Created by Poche on 5/17/14. // // #ifndef __MyGame__Box2dTest__ #define __MyGame__Box2dTest__ #include "cocos2d.h" #include "Box2D/Box2D.h" class Box2dTest : public cocos2d :: Layer { public : Box2dTest(); virtual ~Box2dTest(); CREATE_FUNC ( Box2dTest ); /** *@brief 初始化 box2d 测试 layer , box2d 世界是独立于 cocos 的 scene 的 * *@Modified by poche at 2014-05-18 18:00:17 * *@return 是否初始化成功 **/ bool init(); /** *@brief 初始化 box2d 世界 * *@Modified by poche at 2014-05-18 18:01:43 **/ void initWorld(); private : b2World *m_world; /// box2d 世界 /// layer 上的点击加调 CC_SYNTHESIZE_RETAIN ( cocos2d :: EventListenerTouchAllAtOnce *, m_touchListener ,_touchListener); public : /** *@brief 添加精灵到指定坐标 * *@Modified by poche at 2014-05-18 18:02:44 *@param point 坐标 **/ void AddSprite( cocos2d :: Point point); /** *@brief 每帧检查 box2d 物体并且更新 cocos 的精灵的状态(位置等) * *@Modified by poche at 2014-05-18 18:03:25 *@param dt 时间间隔 **/ void update( float dt); /** *@brief 添加点击回调事件 * *@Modified by poche at 2014-05-18 18:04:39 **/ void addLayerTouchListener(); /** *@brief 点击结束后的加调方法 * *@Modified by poche at 2014-05-18 18:04:56 *@param touches 点击集合 *@param event 事件类型 **/ void touchesEnd( const std :: vector < cocos2d :: Touch *> touches, cocos2d :: Event *event); virtual void onExit(); }; #endif /* defined(__MyGame__Box2dTest__) */
下面看一下.cpp的代码:
// // Box2dTest.cpp // MyGame // // Created by Poche on 5/17/14. // // #include "Box2dTest.h" USING_NS_CC ; /// 在 box2d 里,其用到的单位是现实中的单位,米等 /// 所以在游戏里,我们需要作出转换 /// 在 cocos2d 里,每 32 个像素就相当 box2d 里的一米 #define PTM_RATIO 32 const int tagSprite = 1 ; Box2dTest ::Box2dTest() : m_touchListener ( NULL ) { } Box2dTest ::~Box2dTest() { CC_SAFE_RELEASE ( m_touchListener ); /// 记得移除对像,要不然会泄露 delete m_world ; m_world = NULL ; } bool Box2dTest ::init() { /// 添加点击响应 addLayerTouchListener (); /// 初始化世界 initWorld (); return true ; } void Box2dTest ::initWorld() { auto size = Director :: getInstance ()-> getWinSize (); /// 设置 box2d 世界重力方向 b2Vec2 gravity( 0.0f , - 10.0f ); bool doSleep = true ; /// 生成 box2d 世界 m_world = new b2World (gravity); /// 是否允许睡眠 m_world -> SetAllowSleeping (doSleep); m_world -> SetContinuousPhysics ( true ); /// 地表物体定义 b2BodyDef groundBodyDef; /// 通过地表物体定义生成地表物体 b2Body * groundBody = m_world -> CreateBody (&groundBodyDef); /// 形状 b2PolygonShape groundBox; /// 作为 box 形态布局 /// 第一个参数 物体的宽 /// 第二个参数 物体的高 /// 第三个参数 物体的位置 /// 第四个参数 物体中角度 /// 墙底 groundBox. SetAsBox (size. width / PTM_RATIO , 0 , b2Vec2 ( 0 , 0 ), 0 ); /// 把已经生成的物体放入世界中 /// 第一个参数 要放入的物体 /// 第二个参数 物体的质量 groundBody-> CreateFixture (&groundBox, 0 ); /// 墙顶 groundBox. SetAsBox (size. width / PTM_RATIO , 0 , b2Vec2 ( 0 , size. height / PTM_RATIO ), 0 ); groundBody-> CreateFixture (&groundBox, 0 ); /// 左墙 groundBox. SetAsBox ( 0 , size. height / PTM_RATIO , b2Vec2 ( 0 , 0 ), 0 ); groundBody-> CreateFixture (&groundBox, 0 ); /// 左墙 groundBox. SetAsBox ( 0 , size. height / PTM_RATIO , b2Vec2 (size. width / PTM_RATIO , 0 ), 0 ); groundBody-> CreateFixture (&groundBox, 0 ); /// cocos2d 里的精灵 auto brick = SpriteBatchNode :: create ( "CloseNormal.png" ); addChild (brick, 2 , tagSprite ); /// 每帧对 cocos2d 的精灵更新 schedule ( schedule_selector ( Box2dTest :: update )); } void Box2dTest ::AddSprite( cocos2d :: Point point) { auto batch = ( SpriteBatchNode *) getChildByTag ( tagSprite ); auto sprite = Sprite :: createWithTexture (batch-> getTexture (), Rect ( 0 , 0 , 32 , 32 )); batch-> addChild (sprite); sprite-> setPosition ( Point (point. x , point. y )); /// 创建动态物体 /// 物体定义 b2BodyDef bodyDef; /// 物体为动态物体 bodyDef. type = b2_dynamicBody ; /// 物体的位置 bodyDef. position . Set (point. x / PTM_RATIO , point. y / PTM_RATIO ); /// 将已经生成的精灵捆绑到物体上 bodyDef. userData = sprite; /// 创建物体 b2Body * body = m_world -> CreateBody (&bodyDef); b2PolygonShape dynamicBox; dynamicBox. SetAsBox ( 0.5f , 0.5f ); b2FixtureDef fixtureDef; fixtureDef. shape = &dynamicBox; /// 设置摩擦系数 fixtureDef. friction = 0.3f ; /// 设置密度 fixtureDef. density = 1.0 ; body-> CreateFixture (&fixtureDef); } void Box2dTest ::update( float dt) { int velocityIterations = 8 ; int positionIterations = 1 ; // 每次游戏循环你都应该调用 b2World::Step m_world -> Step (dt, velocityIterations, positionIterations); for ( b2Body * b = m_world -> GetBodyList (); b; b = b-> GetNext ()) { if (b-> GetUserData () != NULL ) { /// 更新 cocos2d 对像 auto sprite = ( Sprite *)b-> GetUserData (); sprite-> setPosition ( Point (b-> GetPosition (). x * PTM_RATIO , b-> GetPosition (). y * PTM_RATIO )); sprite-> setRotation ( - 1 * CC_RADIANS_TO_DEGREES (b-> GetAngle ()) ); } } } void Box2dTest ::addLayerTouchListener() { set_touchListener ( EventListenerTouchAllAtOnce :: create ()); get_touchListener ()-> onTouchesEnded = CC_CALLBACK_2 ( Box2dTest :: touchesEnd , this ); _eventDispatcher -> addEventListenerWithSceneGraphPriority ( get_touchListener (), this ); } void Box2dTest ::onExit() { Layer :: onExit (); unschedule ( schedule_selector ( Box2dTest :: update )); _eventDispatcher -> removeEventListener ( get_touchListener ()); } void Box2dTest ::touchesEnd( const std :: vector < cocos2d :: Touch *> touches, cocos2d :: Event *event) { for ( auto & touch : touches) { auto location = touch-> getLocationInView (); location = Director :: getInstance ()-> convertToGL (location); AddSprite (location); } }
解释:
在这里要说说box2d中的单位,由于box2d中使用的是米作为长度单位。但在 中,我们使用的 图片都是使用像素作为基本单位。
凡是要向世界 对象中添加物体的,都需要将像素单位转化为米单位。凡是要将世界对象中的物体显示在屏幕上时,要将物体的米单位转化为像素单位,在cocos2d里,大多用到的32个像素为一米,能否改就不得而知了!
SetAsBox 函数接收了半个宽度和半个高度,这样的话,地面盒就是 100 个单位宽(x 轴)以及 20 个单位高(y 轴)。Box2D 已被调谐使用米,千克和秒来作单位,所以你可以用米来考虑长度。然而, 改变单位系统是可能的。