cocos2d 物理引擎簡介
材質(Material)
材質(Material) 描述了抽象物體的材料屬性:
- density:密度,用於計算物體的質量
- friction:摩擦,用於模擬物體間的接觸滑動
- restitution:恢復係數,模擬物體反彈的一個係數,係數一般設為 0 到 1 之間。0 代表不反彈,1 代表完全反彈。
形狀(Shape)
形狀(Shape) 描述了抽象物體的幾何屬性,將形狀關聯到剛體,剛體才具有幾何形狀。如果需要剛體具有複雜的形狀,可以為它關聯多個形狀,每個形狀物件都與一個 PhysicsMaterial
相關,並且擁有以下屬性:type, area, mass, moment, offset 和 tag。其中有一些你可能還不熟悉,我們來逐一介紹:
- type:描述了形狀的類別,如圓形,矩形,多邊形等
- area:用於計算剛體的質量,密度和麵積決定了剛體的質量
- mass:剛體的質量,影響物體在給定的力下獲得的加速度大小,物體在一個引力場中物體受到力的大小
- moment:剛體獲得特定角加速度所需要的扭矩
- offset:在剛體的當前座標中,相對於剛體重心的偏移量
- tag:形狀物件的一個標籤,你可能還記得,所有的 Node 物件都可以被分配一個 tag,用來進行辨識,實現更容易的訪問。形狀物件的 tag 作用也一樣。
Cocos2d-x 中預定義了這些形狀物件:
PhysicsShape
:物理形狀的基類PhysicsShapeCircle
:實心的圓形,無法用它實現一個空心圓PhysicsShapePolygon
:實心且外凸的多邊形PhysicsShapeBox
:矩形,它是一種特殊的外凸多邊形PhysicsShapeEdgeSegment
:表示一種線段.PhysicsShapeEdgePolygon
:空心多邊形,由多個線段構成的多邊形邊緣。PhysicsShapeEdgeBox
:空心矩形,由四個線段組成的矩形邊緣PhysicsShapeEdgeChain
: 鍊形,它可以有效的把許多邊緣連線起來
連線/關節
連線(Contacts) 和 關節(joint) 物件描述了剛體相互關聯的方式。
世界(World)
世界(World) 是現實物理世界的一個遊戲模擬,容納著所有被新增進去的抽象物體。你可以將剛體,形狀,約束都新增到物理世界中,然後將整個世界作為一個整體進行更新。物理世界控制著所有元素的相互作用。其中,用物理 API 實現的許多互動都與 世界(World)
以上有許多需要記住的東西,請對這些都有個大概印象,以便一會用到的時候隨時回看。
PhysicsWorld
物理世界(PhysicsWorld) 是 Cocos2d-x 進行物理模擬的核心物件。物理世界會同時發生很多事情,就像我們生活的世界一樣。來想象一個簡單的現實場景——廚房,你在思考的時候,就在腦中描繪出了一個廚房的物理世界!廚房世界裡擁有一些物體,比如食物,刀具,電器,在這個世界中,這些物體會相互作用。它們會相互接觸,並對接觸做出反應。比如:用刀子切開食物,並把它放到電器中,做這樣一件事。刀子切到食物了嗎?可能切到了,也可能還沒有,還可能這個刀子根本就不適合做這個。
物理世界(PhysicsWorld)與場景(Scene)進行了深入的整合,只需要呼叫 Scene
物件的 initWithPhysics()
方法,就可以建立一個包含物理世界的場景,注意在初始化的時候要進行函式返回值的判斷。initWithPhysics()
初始化成功返回 true,失敗返回 false
if( !Scene::initWithPhysics() )
{
}
每一個 物理世界(PhysicsWorld) 都有與之相關的屬性:
- gravity:全域性重力,應用於整個物理世界,預設值為 Vec2(0.0f, -98.0f)
- speed:物理世界的速度,這裡的速度指的是這個模擬世界執行的一種比率,預設值是 1.0
- updateRate:物理世界的重新整理率,這裡的重新整理率指的是 遊戲引擎重新整理時間與物理世界重新整理時間的比值
- substeps:物理世界中每次重新整理的子步數量
重新整理物理世界的過程被稱為步進,按照預設設定,物理世界會不停地進行自動重新整理,這被稱為自動步進。每一幀,都會不停地重新整理,你可以通過 setAutoStep(false)
禁用一個物理世界的自動步進,然後通過 PhysicsWorld::step(time)
設定步進時間來手動重新整理物理世界。遊戲世界是按幀重新整理的,物理世界可以通過子步(substeps)的設定,獲得更加頻繁的重新整理,從而進行更精細的步進控制。
物理剛體(PhysicsBody) 物件具有位置和速度,你可以在物理剛體上應用力(forces),運動(movement),阻尼(damping),衝量(impulses)等等。剛體可以是靜態的,也可以是動態的,靜態的剛體在模擬世界中不會移動,看起來就好像擁有無限大的質量一樣,動態的剛體則是一種完全模擬的模擬。剛體可以被玩家手動移動,更常見的是它們受到力的作用而移動。動態剛體可以與所有型別的剛體發生碰撞。Cocos2d-x 提供了 Node::setPhysicsbody()
方法實現節點物件和物理剛體物件的關聯。
Scene* Chapter11::createScene()
{
//創造物理世界場景
auto scene=Scene::createWithPhysics();
//邊框紅線
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
//設定重力
scene->getPhysicsWorld()->setGravity(Vec2(0,-100));
//設定更新迭代時間
scene->getPhysicsWorld()->step(0.03);
auto layer =Chapter11::create();
scene->addChild(layer);
return scene;
}
boolChapter11::init()
{
if (!Layer::init())
{
returnfalse;
}
auto visibleSize =Director ::getInstance()->getVisibleSize();
//建立一個物理世界,大小和螢幕的尺寸相同,使用預設材質,debug框的寬度為3個畫素
autobody = PhysicsBody::createEdgeBox(visibleSize,PHYSICSBODY_MATERIAL_DEFAULT,3);
//恢復力和摩擦力
body->getShape(0)->setRestitution(1.0f);
//0號形狀
body->getShape(0)->setFriction(0);//第2個為0 ,沒有摩擦力
//建立一個碰撞圖形
auto edgeShape = Node::create();
//將圖形和剛剛建立的世界繫結
//addComponent一樣效果
edgeShape->setPhysicsBody(body);
//設定圖形的位置在螢幕正中間
edgeShape->setPosition(visibleSize.width / 2,visibleSize.height/2);
//新增進圖層
this->addChild(edgeShape);
PhysicsWorld有很多工廠方法,如createEdgeBox建立一個矩形的邊框,所有的引數是:
a.矩形區域,設定作為visibleSize。
b.可選引數,紋理,預設為PHYSICSBODY_MATERIAL_DEFAULT。
c.可選引數,邊框大小,預設為1。
然後,我們建立節點,並連線剛剛建立的節點的身體。螢幕為節點的位置設定好的中心,最後加入節點到場景。
在Cocos2d-x3.0節點的addChild方法可以處理物理剛體。它會自動新增節點的身體到場景的PhysicsWorld。
PhysicsBody的工程方法可以根據引數設定剛體的大小,建立相應的PhysicsBody和PhysicsShape。這是一個常見的做法由物理引擎直接建立一個剛體。然而在Cocos2d-x 3.0物理整合簡化了這個過程,所以我們並不需要編寫大量的程式碼。
設定靜態剛體
//設定精靈圓圖片
auto sp=Sprite::create ("ball.png");
//剛體的圖形圓的 半徑
auto body2=PhysicsBody::createCircle(100);
body2->setDynamic(false);//設定動態剛體 true是,false否 否就是靜態剛體
//設定剛體的密度
body2->getShape(0)->setDensity(0.7);
//設定剛體的質量
body2->setMass(10);
//設定剛體的恢復力和摩擦力 //0號形狀
body2->getShape(0)->setRestitution(1.0f);
body2->getShape(0)->setFriction(5.0f);
body2->setGroup(1) ;//分類掩碼
body2->setCollisionBitmask(1);//碰撞掩碼
body2->setContactTestBitmask(1);//接觸測試掩碼
sp->setPhysicsBody(body2);
sp->setPosition(Vec2(visibleSize.width / 2,visibleSize.height/2));
this->addChild(sp);
returntrue;
}
class PhysicsSprite_:publicSprite{
public:
bool init();
CREATE_FUNC(PhysicsSprite_);
private:
PhysicsBody* body;
};
boolPhysicsSprite_::init()
{
if (!Sprite::init())
{
returnfalse;
}
//arc4random()隨機數
int idx = arc4random( )%2;
int idy = arc4random( )%2;
//擷取矩形Rect(float x, float y, float width, float height);
初始化init
this->initWithFile("blocks.png",Rect(32*idx,32*idy,32,32));
body = PhysicsBody::createBox(Size(32,32));//Box盒子
// this->initWithFile("CloseNormal.png") ;
// body = PhysicsBody::createCircle(this->getContentSize().width/2);
//質量KG
body->setMass(1);
body->getShape(0)->setRestitution(1.0f);
body->getShape(0)->setFriction(0);
body->setGroup(1) ;//分類掩碼
body->setCollisionBitmask(1);//碰撞掩碼
body->setContactTestBitmask(1);//接觸測試掩碼
this->setPhysicsBody(body);
this->setTag(1);
returntrue;
}
voidChapter11::onExit()
{
Layer::onExit();
}
voidChapter11::onEnter()
{
Layer::onEnter();
//觸控事件
auto dispatcher = Director ::getInstance()->getEventDispatcher();
//新增加監聽器
auto touchListener =EventListenerTouchOneByOne::create();
touchListener->onTouchBegan=[=](Touch*_touch,Event*_touchEvent)->bool{
returntrue;
};
touchListener->onTouchMoved=[=](Touch *_touch,Event *_touchEvent){};
touchListener->onTouchEnded=[=](Touch*_touch,Event*_touchEvent){
auto sp =PhysicsSprite_::create();
sp->setPosition(_touch->getLocation( )) ;
this->addChild(sp);
};
//
//註冊該監聽
dispatcher->addEventListenerWithSceneGraphPriority(touchListener,this);
//碰撞檢測
//新增加監聽器
auto contactListener=EventListenerPhysicsContact::create();
contactListener->onContactBegin=[=](constPhysicsContact& contact)->bool{
//接觸,聯絡 剛體 節點
Sprite* SpriteA=(Sprite*)contact.getShapeA()->getBody()->getNode();
Sprite* SpriteB=(Sprite*)contact.getShapeB()->getBody()->getNode();
if(SpriteA&&SpriteB)
{
int tagA=SpriteA->getTag();//形狀物件的一個標籤
int tagB=SpriteB->getTag();
if (tagA==1&&tagB==1)
{ //從物理世界刪除剛體,就變成普通圖片
SpriteA->getPhysicsBody()->removeFromWorld();
SpriteB->getPhysicsBody()->removeFromWorld();
//從父節點刪除精靈
SpriteA->removeFromParent();
SpriteB->removeFromParent();
}
}
cout<<"onContactBegin"<<endl;
returntrue;
};
contactListener->onContactPreSolve=[=](PhysicsContact&contact,PhysicsContactPreSolve&solve)->bool{
cout<<"onContactPreSolve"<<endl;
returntrue;
};
contactListener->onContactPostSolve=[=](PhysicsContact& contact,constPhysicsContactPostSolve&solve){
cout<<"onContactPostSolve"<<endl;
};
contactListener->onContactSeparate=[=](PhysicsContact& contact){
cout<<"onContactSeparate"<<endl;
};
///新增到事件分發器中
//註冊該監聽
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
}