本例子使用的是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__) */
// 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);
}
}
// 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 已被调谐使用米,千克和秒来作单位,所以你可以用米来考虑长度。然而, 改变单位系统是可能的。