要在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)];
}
添加完成後試運作一下:
這裡我們添加了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,這樣物體在滑鼠松開後便會回歸到自然受力的狀态(重力還有其他物體的力)。
好了,大功告成,測試一下效果:
哈哈,現在我們可以把盒子扔的滿天飛了~~
好了,例子就到這裡,如果有問題歡迎留言。