cocos2d-x自定義可點選/可處理事件/可接受觸屏訊息的Sprite

Norton-Linux核心研究發表於2020-04-07

本篇大部分摘自http://blog.csdn.net/onerain88/article/details/7550009(寫的好清楚)

一般經常用到的觸屏的情況有兩種:一種是Layer統一接收觸屏訊息,然後由程式根據需要分發給不同位置的精靈;另一種情況是自定義一個可以接收觸屏訊息的Sprite,比如類似於Button功能的Sprite,這就需要在定義Sprite的時候就要定義好觸屏所觸發的操作!

1.Layer接收觸屏訊息(請參考原文)

總結下就是:在layer中的sprite,他們可能會處於同一層,那麼,最重要的就是怎麼把事件傳遞給希望處理事件的sprite。方法就是通過優先順序去“接收事件”,如果不處理,ccTouchBegin的返回值就設定為false,如果處理,就設定為true,表示攔截此訊息。這樣,事件就不會往更低優先順序的sprite傳遞了。

2.自定義可以接收觸屏訊息的Sprite

我比較感興趣的是自定義Sprite。

首先要先繼承CCSprite或者其父類,以滿足精靈形狀,位置等資訊的要求,另外還需要繼承觸屏事件委託CCTargetedTouchDelegate,CCTargetedTouchDelegate中定義了接收觸屏資訊的回撥虛擬函式,而這些虛擬函式,正是我們需要覆寫的部分,程式碼如下

class TouchableSprite: public CCSprite, public CCTargetedTouchDelegate  
{  
      
public:  
    TouchableSprite();  
    virtual ~TouchableSprite();  
      
    static TouchableSprite *touchSpriteWithFile(const char *file);  
      
    bool initWithFile(const char *file);  
      
    virtual void onEnter();  
    virtual void onExit();  
      
    CCRect rect();  
    bool containsTouchLocation(CCTouch *touch);  
      
    virtual bool ccTouchBegan(CCTouch *touch, CCEvent *event);  
    virtual void ccTouchMoved(CCTouch *touch, CCEvent *event);  
    virtual void ccTouchEnded(CCTouch *touch, CCEvent *event);  
  
};  

重點在於判斷自定義Sprite是否被點選,這時就需要得到精靈所在的矩形了,這時又有兩種判斷方式

(1)得到觸屏所在位置,然後根據精靈所在位置的矩形區域和觸屏的點判斷是否包含,如果包含,則說明觸控到了Sprite。這裡寫了一個得到精靈當前所在矩形的方法

CCRect TouchableSprite::rect()  
{  
    CCSize size = getContentSize();  
    CCPoint pos = getPosition();  
  
    return CCRectMake(pos.x - size.width / 2, pos.y - size.height / 2, size.width, size.height);  }
然後在每次點選的時候都需要將當前觸屏位置轉換為GL座標的位置,然後和Sprite所在矩形做包含判斷

bool TouchableSprite::containsTouchLocation(cocos2d::CCTouch *touch)  
{  
    CCPoint touchPoint = touch->locationInView(touch->view());         //這裡由於版本更新,改為touch->getLoactionInView()
    touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);  
      
    return CCRect::CCRectContainsPoint(rect(), touchPoint);  
}
轉載作者補充:

可以使用getLocation()取代上面那兩句程式碼,cocos2d-x相關原始碼如下:

// returns the current touch location in screen coordinates
CCPoint CCTouch::getLocationInView() const 
{ 
    return m_point; 
}
// returns the current touch location in OpenGL coordinates
CCPoint CCTouch::getLocation() const
{ 
    return CCDirector::sharedDirector()->convertToGL(m_point); 
}
這裡要提下座標系統,getLocationInView獲取的是螢幕座標,不同裝置座標系統不同,例如ios是左上角為原點。而cocos2d-x是左下角為原點。所以在cocos2d-x中做運算時候,要轉為GL座標系,即cocos2d-x預設的“可視螢幕的座標系統”。

(2)其實cocos2d為我們提供了一種相對簡單的方法,但是原理類似,呼叫CCNode中定義的convertTouchToNodeSpaceAR()方法,將觸屏點轉化為相對於結點的相對座標

CCRect TouchableSprite::rect()  
{  
    CCSize size = getTexture()->getContentSize();  
  
    return CCRectMake(-size.width / 2, -size.height / 2, size.width, size.height);  
}
bool TouchableSprite::containsTouchLocation(cocos2d::CCTouch *touch)  
{  
    return CCRect::CCRectContainsPoint(rect(), convertTouchToNodeSpaceAR(touch));  
}

關於事件處理還可以參考本文

http://blog.linguofeng.com/archive/2012/09/12/cocos2d-x-touch.html

可以借鑑該文點選事件處理方法

void MyLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) {
    // 單點
    CCTouch *pTouch = (CCTouch*)(pTouches->anyObject());

    // 所有點
    for(CCSetIterator iterTouch = pTouches->begin(); iterTouch != pTouches->end(); iterTouch++) {
        CCTouch *pCurTouch =  (CCTouch*)(*iterTouch);
    }

    // 獲取點在檢視中的座標(左上角為原點)
    CCPoint touchLocation = pTouch->getLocationInView();
    // 把點的座標轉換成OpenGL座標(左下角為原點)
    touchLocation = CCDirector::sharedDirector()->convertToGL(touchLocation);
    // 把OpenGL的座標轉換成CCLayer的座標
    CCPoint local = convertToNodeSpace(touchLocation)
    // 大小為100x100,座標為(0, 0)的矩形
    CCRect * rect = CCRectMake(0, 0, 100, 100);
    // 判斷該座標是否在rect矩形內
    bool flag = rect.containsPoint(local)
    if(flag) {
        // 回撥
    } else {
        // 不執行
    }
}

其它處理方法,可以借鑑CCTableView

http://blog.csdn.net/xzongyuan/article/details/9177421

觸屏傳遞順序

另外需要主要的是

virtual bool ccTouchBegan(CCTouch *touch, CCEvent *event);

方法,其返回值是對此觸屏訊息有影響的,簡單來說,如果返回false,表示不處理ccTouchMoved(),ccTouchEnded(),ccTouchCanceld()方法,而交由後續接收觸屏訊息的物件處理;如果返回true,表示會處理ccTouchMoved(),ccTouchEnded(),ccTouchCanceld()方法,並且消耗掉此觸屏訊息。


最後,要在進入時候新增觸屏介面

void TouchableSprite::onEnter(){
	CCSprite::onEnter();
	CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true);
}

void TouchableSprite::onExit(){
	CCSprite::onExit();
	CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}



相關文章