这里讲两个Box2d物理引擎的例子,一个新的cocos2d3.x版本,另一个是旧的2.x版。
关于Box2d的基础知识,这里不再详述,详细参考:http://www.jb51.cc/article/p-xtkktxxp-gy.html
新版本:
HelloNewBox2d.h:
#ifndef __HELLONEWBox2D_H__ #define __HELLONEWBox2D_H__ #include "cocos2d.h" #include "Box2D\Box2D.h" using namespace cocos2d; class HelloNewBox2d : public Layer { private: CCTexture2D* _spriteTexture; public: HelloNewBox2d(); ~HelloNewBox2d(); // there's no 'id' in cpp,so we recommend returning the class instance pointer static Scene* createNewBox2dScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone virtual bool init(); // implement the "static create()" method manually CREATE_FUNC(HelloNewBox2d); // 初始化物理引擎设置 void initSprite(); virtual bool onTouchBegan(cocos2d::Touch* touch,cocos2d::Event* event); void onAcceleration(Acceleration* acc,Event* event); // 在指定位置添加精灵 void addNewSpriteAtPosition(Vec2 p); }; #endif // __HELLOWORLD_SCENE_H__
HelloNewBox2d.cpp:
#include "HelloNewBox2d.h" USING_NS_CC; #define SCENERATIO 480/1024 Scene* HelloNewBox2d::createNewBox2dScene() { auto Box2dScene = Scene::create(); // 初始化物理引擎 Box2dScene->initWithPhysics(); auto Box2dLayer = HelloNewBox2d::create(); Box2dScene->addChild(Box2dLayer); return Box2dScene; } HelloNewBox2d::HelloNewBox2d() :_spriteTexture(NULL) { } HelloNewBox2d::~HelloNewBox2d() { // 关闭重力感应 Device::setAccelerometerEnabled(false); } bool HelloNewBox2d::init() { if (!Layer::init()) { return false; } // 初始化物理引擎 this->initSprite(); setTouchEnabled(true); return true; } void HelloNewBox2d::initSprite() { // 获取精灵纹理缓存 _spriteTexture = Sprite::create("blocks.png")->getTexture(); // 设置添加多点触控监听 auto touchListener = EventListenerTouchOneByOne::create(); touchListener->onTouchBegan = CC_CALLBACK_2(HelloNewBox2d::onTouchBegan,this); _eventDispatcher->addEventListenerWithSceneGraPHPriority(touchListener,this); // 打开设备重力感应 Device::setAccelerometerEnabled(true); // 设置重力感应监听 auto accListener = EventListenerAcceleration::create( CC_CALLBACK_2(HelloNewBox2d::onAcceleration,this)); _eventDispatcher->addEventListenerWithSceneGraPHPriority(accListener,this); // 获得OpenGL可见区域的矩形(一般大小为窗口屏幕的大小) Rect s_visibleRect = Director::getInstance()->getOpenGLView()->getVisibleRect(); // 可见区域的中心点(窗口屏幕中心点) Vec2 vec = Vec2(s_visibleRect.origin.x + s_visibleRect.size.width / 2,s_visibleRect.origin.y + s_visibleRect.size.height / 2); // 创建节点 auto node = Node::create(); // 设置刚体,且为边框属性 node->setPhysicsBody(PhysicsBody::createEdgeBox(s_visibleRect.size)); node->setPosition(vec); this->addChild(node); } void HelloNewBox2d::addNewSpriteAtPosition(Vec2 p) { // 产生0和1的随机数 int idx = CCRANDOM_0_1() > .5f ? 0 : 1; int idy = CCRANDOM_0_1() > .5f ? 0 : 1; // 从缓存中获取精灵,并设置添加刚体(考虑屏幕适应性问题) auto sp = Sprite::createWithTexture(_spriteTexture,Rect(idx*32.0f*SCENERATIO,idy*32.0f*SCENERATIO,32.0f*SCENERATIO,32.0f*SCENERATIO)); sp->setPhysicsBody(PhysicsBody::createBox(Size(32.0f*SCENERATIO,32.0f*SCENERATIO))); this->addChild(sp); sp->setPosition(p); } bool HelloNewBox2d::onTouchBegan(Touch* touch,Event* event) { auto location = touch->getLocation(); // 添加新的精灵 addNewSpriteAtPosition(location); return true; } void HelloNewBox2d::onAcceleration(Acceleration* acc,Event* event) { static float prevX = 0,prevY = 0; #define kFilterFactor 0.05f float accelX = (float)acc->x * kFilterFactor + (1 - kFilterFactor)*prevX; float accelY = (float)acc->y * kFilterFactor + (1 - kFilterFactor)*prevY; prevX = accelX; prevY = accelY; auto v = Vec2(accelX,accelY); v = v * 200; // 设置物理世界的重力 this->getScene()->getPhysicsWorld()->setGravity(v); }
旧版本:
HelloBox2d.h:
#ifndef __HELLOBox2D_H__ #define __HELLOBox2D_H__ #include "cocos2d.h" #include "Box2D\Box2D.h" using namespace cocos2d; class HelloBox2d : public Layer { private: // 世界,可看做游戏世界 b2World* _world; CCTexture2D* _spriteTexture; public: HelloBox2d(); ~HelloBox2d(); // there's no 'id' in cpp,so we recommend returning the class instance pointer static Scene* createBox2dScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone virtual bool init(); // implement the "static create()" method manually CREATE_FUNC(HelloBox2d); void setSpriteTexture(); // 初始化物理引擎设置 void initPhysics(); // 根据传入的数值进行更新 virtual void update(float dt); virtual bool onTouchBegan(cocos2d::Touch* touch,cocos2d::Event* event); // 在指定位置添加精灵 void addNewSpriteAtPosition(cocos2d::Vec2 p); }; #endif // __HELLOWORLD_SCENE_H__
HelloBox2d.cpp:
#include "HelloBox2d.h" USING_NS_CC; #define PTM_RATIO 32 Scene* HelloBox2d::createBox2dScene() { auto Box2dScene = Scene::create(); auto Box2dLayer = HelloBox2d::create(); Box2dScene->addChild(Box2dLayer); return Box2dScene; } HelloBox2d::HelloBox2d():_world(NULL) { } HelloBox2d::~HelloBox2d() { CC_SAFE_DELETE(_world); } bool HelloBox2d::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; } void HelloBox2d::initPhysics() { Size s = Director::getInstance()->getVisibleSize(); // 重力参数,参数:1、水平方向,正值,重力向左;1、竖直方向,正值,重力向上 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),s.height / PTM_RATIO)); groundBody->CreateFixture(&groundBox,0); // 左边 groundBox.Set(b2Vec2(0,b2Vec2(0,0)); groundBody->CreateFixture(&groundBox,0); // 右边 groundBox.Set(b2Vec2(s.width / PTM_RATIO,0)); groundBody->CreateFixture(&groundBox,0); } void HelloBox2d::addNewSpriteAtPosition(Vec2 p) { // 创建物理引擎精灵对象 auto sprite = Sprite::create("blocks.png"); sprite->setPosition(Vec2(p.x,p.y)); this->addChild(sprite); // 物体定义 b2BodyDef bodyDef; // 定义动态刚体类型 bodyDef.type = b2_dynamicBody; bodyDef.position.Set(p.x / PTM_RATIO,p.y / PTM_RATIO); // 创建刚体 b2Body *body = _world->CreateBody(&bodyDef); // 设置用户数据,辨别和调用创建好的刚体 body->SetUserData(sprite); // 定义2米见方的盒子形状 b2PolygonShape dynamicBox; dynamicBox.SetAsBox(.5f,.5f); // 夹具定义 b2FixtureDef fixtureDef; //设置夹具的形状 fixtureDef.shape = &dynamicBox; //设置密度 fixtureDef.density = 1.0f; //设置摩擦系数 fixtureDef.friction = 0.3f; //使用夹具固定形状到物体上 body->CreateFixture(&fixtureDef); } void HelloBox2d::update(float dt) { float timeStep = 0.03f; int32 velocityIterations = 8; int32 positionIterations = 1; // 引擎自己检查节点位置和速率,进行实时更新 _world->Step(timeStep,velocityIterations,positionIterations); // 遍历世界中创建的刚体,不断更新刚体,即盒子的角度和位置 for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != nullptr) { Sprite* sprite = (Sprite*)b->GetUserData(); sprite->setPosition(Vec2(b->GetPosition().x * PTM_RATIO,b->GetPosition().y * PTM_RATIO)); sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle())); } } } bool HelloBox2d::onTouchBegan(cocos2d::Touch* touch,cocos2d::Event* event) { auto loc = touch->getLocation(); addNewSpriteAtPosition(loc); return true; }
效果图:
这里是源代码,由于文件过于庞大,只上传代码部分,复制覆盖项目即可,运行时,在AppDelegate.cpp文件中修改运用的场景即可,两个例子实现的功能一致。
auto Box2dscene = HelloBox2d::createBox2dScene(); auto newBox2dscene = HelloNewBox2d::createNewBox2dScene(); // run director->runWithScene(newBox2dscene);下载地址:http://download.csdn.net/detail/u013707014/9016823
HelloBox2d.cpp: