知易遊戲開發教程cocos2d-x移植版004

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/03/25/2416890.html


我們知道cocos2d-x是cocos2d-iphone項目的C++移植版本,它擁有跨平臺的特性。同時cocos2d-x與cocos2d-iphone保持着高度地同步,這也就從根本上限制住它是一個爲手機、平板等設備量身定做的遊戲引擎。而對Win32等平臺的支持,僅僅是爲了方便開發與調試。
如果你正準備開發PC版的遊戲,使用那種專爲PC設計的引擎纔是你正確的選擇。雖然在一篇介紹cocos2d-x的文章裏出現這樣的句子有些扎眼,但我想有些事情還是講明白的好,任何人的時間都不應該被浪費。

如果你也像我一樣是從PC端開發加入到cocos2d-x中的,那麼有很多概念是需要注意的。
比如,在PC上我們經常使用的鼠標在手機和平板上變成了手指頭。這當然不單單是介質的轉變,發生變化的還包括使用習慣。

消失的鼠標經過狀態
大家都知道,在PC上,一個按鈕通常包含4種狀態——普通、鼠標經過、鼠標按下、禁用,而在手機和平板上,一個按鈕通常只有3種狀態——普通、按下、禁用。
這是很容易理解的。
第一,不管鼠標放在什麼地方,它終究是脫離不了顯示器屏幕的,而讓一個人時刻把手指按在觸摸屏上,要困難得多。
第二,鼠標是有按鍵的,而人手沒有按鍵,想實現移動而脫離點擊是有難度的。
所以在手機和平板上,傳統意義的鼠標經過狀態幾乎不存在。

神奇的多點觸摸
對於手機和平板用戶來說,多點觸屏稀鬆平常。雖然PC也有支持多點輸入的外設,但支持這一特性的軟件卻不常見。對於用慣了PC的人來說,還是鼠標的單點輸入更容易接受。

所謂習慣嘛,就是習以爲常,時間久了,也就是那麼回事兒了。今天我們就來認識認識這個Touch(觸摸)。

cocos2d-x的觸摸事件處理

在cocos2d-x中,觸摸是一種重要的交互手段。雖然我們之前沒有明確指出這個觸摸事件,但是我們一刻也沒有離開它。每一個Menu的觸發,其實就是一次觸摸。

CCStandardTouchDelegate

準確來說,我們與Touch有過一面之緣。在第2篇中,我們實現了一個點擊屏幕後,飛船移動過去的功能。

複製代碼
1 void GameLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
2 {
3 CCTouch *touch = (CCTouch *)pTouches->anyObject();
4 // 獲得觸摸點座標
5 CCPoint location = touch->locationInView(touch->view());
6 CCPoint convertedLocation = CCDirector::sharedDirector()->convertToGL(location);
7 // 讓飛船在1秒鐘內移動過去
8 flight->runAction(CCMoveTo::actionWithDuration(1.0f, ccp(convertedLocation.x, convertedLocation.y)));
9 }
複製代碼

但是,想要上面的代碼生效,還必須在初始化中開啓觸摸事件。

複製代碼
1 bool GameLayer::init(void)
2 {
3 ...
4 // accept touch now!
5 setIsTouchEnabled(true);
6 ...
7 }
複製代碼

跟進去看看開啓觸摸事件的實現。

複製代碼
 1 void CCLayer::setIsTouchEnabled(bool enabled)
2 {
3 if (m_bIsTouchEnabled != enabled)
4 {
5 m_bIsTouchEnabled = enabled;
6 if (m_bIsRunning)
7 {
8 if (enabled)
9 {
10 this->registerWithTouchDispatcher();
11 }
12 else
13 {
14 // have problems?
15 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
16 }
17 }
18 }
19 }
20
21 void CCLayer::registerWithTouchDispatcher()
22 {
23 CCTouchDispatcher::sharedDispatcher()->addStandardDelegate(this,0);
24 }
複製代碼

上面的代碼通過CCTouchDispatcher添加標準代理實現開啓觸摸的功能。

所謂標準代理就是指你可以接收到所有的消息——開始(Began)、移動(Moved)、結束(Ended)、取消(Cancelled)。觸摸點的信息是由CCSet表示一個集合。

與高權限相隨的是高靈活性,而要獲得高靈活性的代價就是你必須自己來編寫額外的代碼。

靈活性太高,反而不知從何開始,乾脆就不講了,以後有實際需要的時候再說。不過有一點必須提一下,標準代理類型與蘋果的CocoaTouch框架基本一致,所以CocoaTouch的大部分方案在cocos2d-x中也是可行的。

CCTargetedTouchDelegate

相較標準代理來說,目標代理的功能自然是弱一些,但使用起來要簡便得多。

使用目標代理有兩個顯著的好處:
1.你不用跟CCSet打交道,調度程序會替你做好拆分,每次使用時你會得到單一的Touch事件。
2.你可以通過讓ccTouchBegan返回true來“認領”一個觸摸事件。被“認領”的觸摸事件只會被分發給“認領”它的代理對象。這樣你就可以從多點觸摸的檢測苦海中解脫出來。

我們來實戰演練一下。假設我們接到一個任務,要求在屏幕上顯示一個精靈,這個精靈是一個循環播放的序列幀動畫。當我們按在精靈上的時候可以拖動它,如果點在精靈外面,那就不能拖動。

首先分析下我們接到的任務。顯示一個循環播放的序列幀動畫精靈,這個沒有難點,我們之前已經做過多次了。按在精靈上的時候可以拖動它,按在外面就不能拖動,這一條如果使用標準代理對象實現,必然要添加大量的判斷和狀態,顯然目標代理是我們應該優先考慮的。

因此,我們需要的是一個符合目標代理的精靈,所以這是一個多重繼承的類,它有2個父類——CCSprite和CCTargetedTouchDelegate。

複製代碼
1 class KillingLight : public CCSprite, public CCTargetedTouchDelegate
2 {
3 public:
4 KillingLight(void);
5 virtual ~KillingLight(void);
6 }
複製代碼

要接收觸摸事件必須先進行註冊,當不再需要的時候要進行反註冊。我們重寫CCNode的onEnter和onExit函數來完成觸摸事件的註冊與反註冊。

複製代碼
 1 void KillingLight::onEnter(void)
2 {
3 CCSprite::onEnter();
4 // 我們希望獨佔被“認領”的觸摸事件,所以第3個參數傳入true
5 CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
6 }
7
8 void KillingLight::onExit(void)
9 {
10 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
11 CCSprite::onExit();
12 }
複製代碼

在目標代理對象中,只有先“認領”觸摸事件,才能使用。所以我們要重寫CCTargetedTouchDelegate的ccTouchBegan函數。

因爲任務要求只有點在精靈上面時才能拖動,所以我們需要增加判斷觸摸點是否在精靈上的判斷。

複製代碼
 1 CCRect KillingLight::rect(void)
2 {
3 // 後面的比較是以錨點爲標準的
4 const CCSize& s = getTextureRect().size;
5 const CCPoint& p = this->getAnchorPointInPixels();
6 return CCRectMake(-p.x, -p.y, s.width, s.height);
7 }
8
9 bool KillingLight::containsTouchLocation(CCTouch *touch)
10 {
11 return CCRect::CCRectContainsPoint(rect(), convertTouchToNodeSpaceAR(touch));
12 }
13
14 bool KillingLight::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
15 {
16 if (! containsTouchLocation(pTouch))
17 return false;
18
19 return true;
20 }
複製代碼

接着,我們重寫CCTargetedTouchDelegate的ccTouchMoved函數,來實現拖動功能。

複製代碼
1 void KillingLight::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
2 {
3 CCPoint touchPoint = pTouch->locationInView(pTouch->view());
4 touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);
5 setPosition(touchPoint);
6 }
複製代碼

爲了便於使用,我們再添加一個靜態成員函數KillingLightWithBatchNode來創建KillingLight對象。

複製代碼
 1 KillingLight* KillingLight::KillingLightWithBatchNode(cocos2d::CCSpriteBatchNode *batchNode, const cocos2d::CCRect& rect)
2 {
3 KillingLight *pobSprite = new KillingLight();
4 if (pobSprite && pobSprite->initWithBatchNode(batchNode, rect))
5 {
6 pobSprite->autorelease();
7 return pobSprite;
8 }
9 CC_SAFE_DELETE(pobSprite);
10 return NULL;
11 }
複製代碼

這樣,一個支持點擊拖動的精靈類就完成了,你可以像使用CCSprite那樣使用它。

如果你希望讓這個類再完美一些,比如只響應第一個按上的觸摸事件以免多點搶一個精靈等等,你可以參考TouchesTest中的Paddle類。

代碼下載:
http://files.cnblogs.com/cocos2d-x/ZYG004.rar

在處理觸摸事件的時候,最需要注意的是座標轉換。這包括很多層面,是屏幕座標系還是GL座標系,是世界座標還是局部座標,還有座標是相對哪個點計算的等等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章