1. 程式人生 > >Cocos2d-x 3.0中 物理碰撞檢測中onContactBegin回撥函式不響應問題

Cocos2d-x 3.0中 物理碰撞檢測中onContactBegin回撥函式不響應問題

好吧,其實這篇也是臨時冒出來的,最近朋友要做個物理遊戲,以前做物理還是用box2d,呃,確實要花些功夫才能搞懂其中的精髓,但是聽講這套引擎重新封裝了一次,要容易很多,所以就簡單嘗試了一下,感覺確實要簡單不少,不過在這其中還是遇到了些問題,首先就來說說onContactBegin這個回撥函式響應問題。

先說說情況,簡單做了一個打磚塊的遊戲,前面一切都很順利,只是做到碰撞檢測的時候,發現回撥函式弄死都不呼叫。開始我以為函式寫錯了,後來查了api,testCpp都沒有錯,在3.0的api中,沒有關於PhysicsBody,PhysicsWorld這些類的說明,所以大家想查移步到3.2的api中吧。

onContactBegin函式的引數就一個,

bool onContactBegin(PhysicsContact& contact),這和3.0beta版本又有些不同,以前是2個,所以在新增事件監聽的時候,不要寫錯,like this,

auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

回撥函式是在當兩個物體有接觸的時候就會響應一次,裡面的引數從名字中也能看出來,Contact接觸,自然會涉及到兩個物體。

好了,下面該到重點了,為啥這個onContactBegin函式不響應呢?問題出在三個掩碼值上,這裡推薦一下看下這篇博文

或者開啟引擎的原始碼,可以在CCPhysicsShape這個標頭檔案裡看下這段程式碼,

/**
     * A mask that defines which categories this physics body belongs to.
     * Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and contactTestBitMask properties, you define which physics bodies interact with each other and when your game is notified of these interactions.
     * The default value is 0xFFFFFFFF (all bits set).
     */
    inline void setCategoryBitmask(int bitmask) { _categoryBitmask = bitmask; }
    inline int getCategoryBitmask() const { return _categoryBitmask; }
    
/**
     * A mask that defines which categories of bodies cause intersection notifications with this physics body.
     * When two bodies share the same space, each body’s category mask is tested against the other body’s contact mask by performing a logical AND operation. If either comparison results in a non-zero value, an PhysicsContact object is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask for interactions you are interested in.
     * The default value is 0x00000000 (all bits cleared).
     */
    inline void setContactTestBitmask(int bitmask) { _contactTestBitmask = bitmask; }
    inline int getContactTestBitmask() const { return _contactTestBitmask; }
   
 /**
     * A mask that defines which categories of physics bodies can collide with this physics body.
     * When two physics bodies contact each other, a collision may occur. This body’s collision mask is compared to the other body’s category mask by performing a logical AND operation. If the result is a non-zero value, then this body is affected by the collision. Each body independently chooses whether it wants to be affected by the other body. For example, you might use this to avoid collision calculations that would make negligible changes to a body’s velocity.
     * The default value is 0xFFFFFFFF (all bits set).
     */
    inline void setCollisionBitmask(int bitmask) { _collisionBitmask = bitmask; }
    inline int getCollisionBitmask() const { return _collisionBitmask; }

說白了,兩個物體間,能不能碰撞,能不能傳送接觸事件資訊,關鍵就看這個三個引數值。

總結來說:

一個body的CategoryBitmask和另一個body的ContactTestBitmask的邏輯與的結果不等於0時,接觸事件將被髮出,否則不傳送。
一個body的CategoryBitmask和另一個body的CollisionBitmask的邏輯與結果不等於0時,會碰撞,否則不碰撞。

這三個引數都有自己的預設值,採用16位表示,

CategoryBitmask,        預設值為 0xFFFFFFFF
ContactTestBitmask,  預設值為 0x00000000
CollisionBitmask,        預設值為 0xFFFFFFFF

大家可以簡單的算一下,如果對這個計算不了解,可以查檢視哈,或者掏出你電腦上的計算器也可以哇。

按照前面的總結來說,如果我們建立的body都採用預設值的話,那麼

CategoryBitmask & ContactTestBitmask = 0
CategoryBitmask & CollisionBitmask = -1

這樣看來,情況就清楚了,如果採用預設的數值,碰撞是可以檢測的,但是碰撞事件是不會發出的,so我們的onContactBegin就被遮蔽了,那麼當然不會做事情。

所以如果想我們的兩個物體即發生碰撞又可以檢測到,那麼很簡單,不讓它們&的值不等於0就ok了,所以可以將兩個需要碰撞的物體的這個三個掩碼值都設定成1,

m_ball->getPhysicsBody()->setCategoryBitmask(0x01);  
m_ball->getPhysicsBody()->setContactTestBitmask(0x01); 
m_ball->getPhysicsBody()->setCollisionBitmask(0x01);
block->getPhysicsBody()->setCategoryBitmask(0x01);
block->getPhysicsBody()->setContactTestBitmask(0x01);
block->getPhysicsBody()->setCollisionBitmask(0x01);
這樣它們之間怎麼按位與的結果都是1,就可以有相應了。
bool HelloWorld::onContactBegin(PhysicsContact& contact)
{
	auto sp1 = (Sprite*)contact.getShapeA()->getBody()->getNode();
	auto sp2 = (Sprite*)contact.getShapeB()->getBody()->getNode();

	if (sp1->getTag() == 1)
		sp1->removeFromParentAndCleanup(true);
	if (sp2->getTag() == 1)
		sp2->removeFromParentAndCleanup(true);

	return true;
}

前面說了接觸是肯定有兩個物體的,所以打磚塊的邏輯在這裡就是判斷下,其中一個的tag,如果是我們的磚塊,說明碰到了,那麼我們移除就ok了。

效果就是這樣了。