天天看點

使用b2MouseJoint實作滑鼠拖拽剛體的效果



要在Box2d中實作滑鼠拖拽效果(當然在移動裝置上就不是滑鼠拖拽而是手指拖拽了),可以使用Box2d中定義的b2MouseJoint來制作。大體思路是,首先在ccTouchBegan事件中擷取滑鼠(以下不再區分滑鼠或是手指)所在位置的剛體(如果有的話),接着在ccTouchMoved事件中通過更新b2MouseJoint的target的位置(更新為滑鼠的目前位置),最後在ccTouchEnded事件中清理掉b2MouseJoint讓剛體受力自由運動。

首先我們來完成擷取滑鼠所在位置的剛體的代碼。

以cocos2d iOS with Box2d為模闆建立Box2d工程(本文使用的是Box2d2.3.1版本),接着我們去掉HelloWorldLayer的addNewSpriteAtPosition方法中為盒子對象添加的紋理貼圖部分(這樣我們看到的就是原始的剛體骨架了,個人喜好哈哈),接着我們在HelloWorldLayer的init方法中把menu相關的代碼都給注釋掉,讓我們的界面看着更簡潔一些,然後使用循環,調用addNewSpriteAtPosition方法向場景中添加若幹個盒子剛體來用于測試:

for (int i =0; i < 20; i++) {

   [self addNewSpriteAtPosition:ccp(s.width/2,s.height/2)];

}

添加完成後試運作一下:

使用b2MouseJoint實作滑鼠拖拽剛體的效果

這裡我們添加了20個剛體。

接着把HelloWorldLayer的ccTouchesEnded方法全部注釋掉。

接着我們讓HelloWorldLayer繼承ccTouchOneByOneDelegate協定,在HelloWorldLayer中重載兩個方法用于注冊和反注冊touch事件分發:

-(void)onEnterTransitionDidFinish {

   [[[CCDirector sharedDirector]touchDispatcher] addTargetedDelegate:self priority:1 swallowsTouches:true];

}

-(void) onExit{

   [[[CCDirector sharedDirector]touchDispatcher] removeDelegate:self];

}

下面我們來說一下如何擷取滑鼠位置的剛體。

首先,滑鼠的位置可以在touch事件中使用傳入的參數touch來擷取:

CGPointtouchLocation = [[CCDirector sharedDirector] convertToGL:[touchlocationInView:[touch view]]];

接着我們通過Box2d的AABB Query來擷取某個區域内的所有fixture。

在工程中添加一個類MyQueryCallback,繼承自b2QueryCallback:

類聲明:

#import"Box2D.h"

classMyQueryCallback : public b2QueryCallback {

public:

   b2Body* body;

   MyQueryCallback();

   bool ReportFixture(b2Fixture* fixture);

};

類的方法實作:

#import"MyQueryCallback.h"

MyQueryCallback::MyQueryCallback(){

   body = nil;

}

boolMyQueryCallback::ReportFixture(b2Fixture* fixture) {

   body = fixture->GetBody();

   return false;

}

在方法實作中我們實作了ReportFixture方法,該方法作為AABBQuery的回調方法被調用。類中我們定義了一個b2Body剛體用來儲存查詢到的剛體對象。傳回false表示停止繼續查詢,傳回true則表示繼續查詢。

接着我們在HelloWorldLayer實作ccTouchBegan方法:

-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {

 //擷取滑鼠位置

   CGPoint touchLocation = [[CCDirectorsharedDirector] convertToGL:[touch locationInView:[touch view]]];

   MyQueryCallback callback;

   b2AABB aabb;

float margin = 0.00000000001f;

//定義一個AABB線框

   aabb.lowerBound.Set((touchLocation.x -margin) / PTM_RATIO, (touchLocation.y + margin) / PTM_RATIO);   //線框左上角

   aabb.upperBound.Set((touchLocation.x +margin) / PTM_RATIO, (touchLocation.y - margin) / PTM_RATIO);   //線框右下角

   world->QueryAABB(&callback,aabb);   //查詢線框内的物體

   if (callback.body != nil) {

       //建立b2MouseJoint關節

   }

   return true;

}

在上面的代碼中,首先以滑鼠點選的位置為中心建立一個很小的矩形區域(即AABB),再定義我們自己的MyQueryCallback執行個體,調用QueryAABB方法來查詢在AABB中的剛體,将結果回報到MyQueryCallback中(通過ReportFixture方法)。

判斷如果callback的body成員不為空(nil),說明滑鼠點選的位置有剛體存在,然後就可以利用得到的剛體來建立b2MouseJoint了,下面是建立的具體代碼:

if (mouseJoint!= nil) {

   world->DestroyJoint(mouseJoint);

   mouseJoint = nil;

}

body =callback.body;

b2MouseJointDefmouseJointDef;

b2BodyDefbodyDef;

b2Body*emptyBody = world->CreateBody(&bodyDef);   //建立一個空的剛體,這個剛體在b2MouseJoint中實際沒有用到,隻是為了滿足建立Joint的一緻性而使用,注意 不能是nil

mouseJointDef.bodyA= emptyBody;

mouseJointDef.bodyB= body;   //bodyB就是滑鼠所在位置的剛體

mouseJointDef.target.Set(touchLocation.x/ PTM_RATIO, touchLocation.y / PTM_RATIO);   //設定Target,剛體就會向Target“飛去”

mouseJointDef.maxForce= 200;   //設定最大受力,受力越大,物體向Target飛過去的加速度就越快

mouseJoint =world->CreateJoint(&mouseJointDef);

注:需要在HelloWorldLayer中添加兩個成員變量:

b2Body* body;

b2Joint*mouseJoint;

上面的代碼中首先判斷前一個mouseJoint是否已經清理掉了(因為這個成員是複用的,是以如果沒有清理掉容易出問題)。接着通過定義b2MouseJointDef中的各項參數來建立關節(代碼已經做過注釋,不多贅述)。

完成後,還需要實作ccTouchMoved和ccTouchEnded兩個方法:

-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {

   CGPoint touchLocation = [[CCDirectorsharedDirector] convertToGL:[touch locationInView:[touch view]]];

   if (body != nil) {

       b2Vec2 target(touchLocation.x /PTM_RATIO, touchLocation.y / PTM_RATIO);

       ((b2MouseJoint*)mouseJoint)->SetTarget(target);

   }

}

-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {

   if (mouseJoint != nil) {

       world->DestroyJoint(mouseJoint);

       mouseJoint = nil;

       body = nil;

   }

}

ccTouchMoved方法在滑鼠移動的時候要實時更新MouseJoint的Target的位置,以便形成滑鼠“拖拽”的效果。ccTouchEnded方法清理掉MouseJoint,這樣物體在滑鼠松開後便會回歸到自然受力的狀态(重力還有其他物體的力)。

好了,大功告成,測試一下效果:

使用b2MouseJoint實作滑鼠拖拽剛體的效果

哈哈,現在我們可以把盒子扔的滿天飛了~~

好了,例子就到這裡,如果有問題歡迎留言。