既然已有了cocos2d-x,為什麼還要Box2d呢,是因為cocos2d-x作為一個圖像引擎,只是用於顯示圖像,圖像之間可以任意的重合,如果想要做到類物理學的碰撞等運動效果,就需要用到Box2d這個物理引擎用來模仿物理世界中的物體;
本講主要簡單講述如何創建動態物體,靜態物體,漂浮物體,以及它們與圖像的綁定;
下面直接通過一個例子來看三種物體的創建方法;
首先需要說明的一點是:在Box2d中,使用的單位是米,而不是像素,所以,在進行位置轉換的時候,需要按比例縮放,Box2d中,比較理想的距離大小是10米,所以,得對屏幕寬高進行一定的比例縮放(如下面例子中定義的RADIO,就是我定義的比例);
HelloWorldScene.cpp
#include "HelloWorldScene.h"
#define RADIO 80
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
world = new b2World(b2Vec2(0,-10)); //x 方向加速度為0,y方向加速度為10
world->SetContactListener(this); // 添加物體撞擊事件監聽
addGround();
addRect();
scheduleUpdate(); // 開始執行handler,第幀執行一次update方法
return true;
}
void HelloWorld::update(float dt){
world->Step(dt,8,3); // 三個參數分別表示當前幀和最後一幀之間的時間間隔,由於update方法是程序每一幀執行一次,所以直接傳入幀頻dt;
//第二個參數表示速度迭代次數,官方建議8;
//第三個參數表示位置迭代次數,官方建議3;
Sprite *s;
for(b2Body *b = world->GetBodyList();b;b= b->GetNext()){
if(b->GetUserData()){
s = (Sprite*)b->GetUserData();
s->setPosition(b->GetPosition().x * RADIO ,b->GetPosition().y *RADIO); // 更新物體的位置
}
}
}
void HelloWorld::BeginContact(b2Contact* contact){ // b2ContactListener物體發生碰撞時的回調接口
log("contact?");
if(contact->GetFixtureA()->GetBody() == ground ||contact->GetFixtureB()->GetBody() == ground){
log("yeah,contacted!");
}
}
void HelloWorld::addRect(){ // 添加一個動態的Rect,並且綁定一個Sprite;
b2BodyDef def;
def.type = b2_dynamicBody; // 指定type為動態物體 @1
def.position = b2Vec2(3,1); // 起始位置(3,1)
def.linearVelocity = b2Vec2(0,10); // (初始)線性速度,如果type為漂浮的,則物體會沒這個方向做勻速運動,如果為動態,則會進行預先設定的加速度進行速度變換;
b2PolygonShape shape;
shape.SetAsBox(0.5,0.5); // 設置一個Shape,兩個參數分別表示左半邊和上半邊的大小,所以,這裡兩個0.5其實就定義了一個1x1的塊
b2FixtureDef fixtureDef;
fixtureDef.density = 1; // 指定物體密度
fixtureDef.friction = 0.3; // 指定物體摩擦
fixtureDef.shape = &shape; // 指定物體的shape
b2Body *body = world->CreateBody(&def);
auto s = Sprite::create();
s->setTextureRect(Rect(0 , 0, 30, 30)); // 設置sprite所顯示的區域
addChild(s);
body->CreateFixture(&fixtureDef); // 設置FixtureDef之後,兩個物體之間就會有碰撞的效果了,如果沒有碰撞的效果,則物體會一直沿某一個方向不停運動;
body->SetUserData(s); // 將Sprite綁定到物體上
}
void HelloWorld::addGround(){ //添加一個靜態物體,在為裡為添加到底部,阻止上一步中創建的自由落體的物體
b2BodyDef def;
def.type = b2_staticBody;
def.position = b2Vec2(0, 0);
b2PolygonShape shape;
shape.SetAsBox(400/RADIO, 10 / RADIO); // 設置物體大小
ground = world->CreateBody(&def);
auto s = Sprite::create();
s->setTextureRect(Rect(0,0,1600,40));
b2FixtureDef fixtureDef;
fixtureDef.density = 0.5;
fixtureDef.friction = 0.3;
fixtureDef.shape = &shape;
addChild(s);
ground->CreateFixture(&fixtureDef);
ground->SetUserData(&def);
}
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
USING_NS_CC;
class HelloWorld : public Layer,public b2ContactListener
{
private :
b2World *world;
b2Body *ground;
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// a selector callback
void menuCloseCallback(cocos2d::Ref* pSender);
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
virtual void update(float dt);
void addRect();
void addGround();
/// Called when two fixtures begin to touch.
virtual void BeginContact(b2Contact* contact) ;
};
#endif // __HELLOWORLD_SCENE_H__
@1: 這裡的type有三種類型如下