cocos2dx 多層觸摸機制

/*
* LsTouch.h
*/

#ifndef LSTOUCH_H_
#define LSTOUCH_H_

#include "cocos2d.h"

USING_NS_CC;

class LsTouchEvent;

/**
* 定義可觸摸元素,用於統一管理
*/
class LsTouch : public CCNode {
public:
    LsTouch();
    ~LsTouch();
    CREATE_FUNC(LsTouch);
    virtual bool init();

    // 設置顯示項
    void setDisplay(CCSprite* dis);

    void setEventId(int eventId);
    int getEventId();

    /// 常規判斷
    bool selfCheck(CCTouch* ccTouch, LsTouchEvent* lsTe);

private:
    // 判斷當前的元素是否被點擊
    bool containsCCTouchPoint(CCTouch* ccTouch);
    bool isParentAllVisible(LsTouchEvent* lsTe);

    // 用戶保存顯示精靈的 tag
    static const int TAG_DISPLAY = 100;
    int m_iEventId;

};

class LsTouchEvent {
public:
    LsTouchEvent();
    ~LsTouchEvent();

    void addLsTouch(LsTouch* touch, int eventId);

    void removeLsTouch(LsTouch* touch);

    bool sendTouchMessage(CCTouch* ccTouch);

    // 返回優先級較高的可觸摸對象
    LsTouch* getPriorityTouch(LsTouch* a, LsTouch* b);

    virtual void touchEventAction(LsTouch* touch) = 0;
private:
    CCArray* m_pLsTouches;
};

#endif /* LSTOUCH_H_ */
/*
* LsTouch.cpp
*
*/

#include "LsTouch.h"

LsTouch::LsTouch() {
    CCLog("LsTouch()");
    m_iEventId = 0;
}

LsTouch::~LsTouch() {
    CCLog("LsTouch().~()");
}

bool LsTouch::init() {

    if ( !Node::init() )
    {
        return false;
    }
    
    return true;
}

void LsTouch::setDisplay(CCSprite* dis) {
    // 設置以前先清除,沒有也無所謂
    removeChildByTag(TAG_DISPLAY, true);
    addChild(dis, 0, TAG_DISPLAY);
}

void LsTouch::setEventId(int eventId) {
    m_iEventId = eventId;
}

int LsTouch::getEventId() {
    return m_iEventId;
}

bool LsTouch::selfCheck(CCTouch* ccTouch, LsTouchEvent* lsTe) {
    bool bRef = false;
    // 可點擊項的檢測,可擴展
    do {
        // 是否經過點擊位置檢測
        CC_BREAK_IF(!containsCCTouchPoint(ccTouch));
        // 是否正在運行,排除可能存在已經從界面移除,可是並無釋放的可能
        CC_BREAK_IF(!isRunning());

        // 判斷是否隱藏
        CC_BREAK_IF(!isVisible());
        // 這裏可能還須要判斷內部顯示項目是否隱藏
        ///// 暫留
        // 不只判斷當前元素是否隱藏,還須要判斷在它之上的元素直到事件處理層,是否存在隱藏
        CC_BREAK_IF(!isParentAllVisible(lsTe));

        bRef = true;
    } while (0);
    return bRef;
}

bool LsTouch::containsCCTouchPoint(CCTouch* ccTouch) {
    // 得到顯示內容
    CCNode* dis = getChildByTag(TAG_DISPLAY);
    CCSprite* sprite = dynamic_cast<CCSprite*>(dis);
    CCPoint point = sprite->convertTouchToNodeSpaceAR(ccTouch);
    CCSize s = sprite->getTexture()->getContentSize();
    CCRect rect = CCRectMake(-s.width / 2, -s.height / 2, s.width, s.height);
    return rect.containsPoint(point);
}

bool LsTouch::isParentAllVisible(LsTouchEvent* lsTe) {
    bool bRef = true;
    // 向父類轉型,以便獲取地址比較對象,LsTouchEvent 的對象必須同時直接或者簡介繼承 CCNode
    CCNode* nLsTe = dynamic_cast<CCNode*>(lsTe);

    CCNode* parent = getParent();
    do {
        // 若是遍歷完畢,說明 LsTouch 再也不 LsTouchEvent 以內
        if (!parent) {
            bRef = false;
            break;
        }
        // 若是 LsTouch 在 LsTouchEvent 以內,返回 true
        // 注意:若是想讓LsTouchEvent 處理 不在其 CCNode 結構以內的元素,則取消此處判斷
        if (nLsTe == parent) {
            break;
        }
        if (!parent->isVisible()) {
            bRef = false;
            break;
        }
        parent = parent->getParent();
    } while (1);
    return bRef;
}

LsTouchEvent::LsTouchEvent() {
    CCLog("LsTouchEvent()");
    m_pLsTouches = CCArray::create();
    m_pLsTouches->retain();
}

LsTouchEvent::~LsTouchEvent() {
    CCLog("LsTouchEvent().~()");
    m_pLsTouches->release();
}

void LsTouchEvent::addLsTouch(LsTouch* touch, int eventId) {
    touch->setEventId(eventId);
    m_pLsTouches->addObject(touch);
}

void LsTouchEvent::removeLsTouch(LsTouch* touch) {
    m_pLsTouches->removeObject(touch, true);
}

bool LsTouchEvent::sendTouchMessage(CCTouch* ccTouch) {
    // 編寫判斷,集合中的哪一個元素級別高,就觸發哪個
    LsTouch* lsTouch = NULL;

    // 得到點擊的點
    CCObject* pObj = NULL;
    LsTouch* lt = NULL;
    CCARRAY_FOREACH(m_pLsTouches, pObj) {
        lt = dynamic_cast<LsTouch*>(pObj);
        if (lt) {
            if (lt->selfCheck(ccTouch, this)) {
                if (lsTouch == NULL)
                    lsTouch = lt;
                else
                    // 若是已存在符合條件元素,比較優先級
                    lsTouch = getPriorityTouch(lsTouch, lt);
            }
        }
    }
    // 比對最終只有一個元素觸發
    if (lsTouch){
        touchEventAction(lsTouch);
        return true;
    }
    return false;
}

LsTouch* LsTouchEvent::getPriorityTouch(LsTouch* a, LsTouch* b) {
    // 觸摸優先級經過 CCNode 樹判斷,也既是顯示層次級別等因素
    // 以當前元素爲「根」向父類轉型,以便獲取地址比較對象,LsTouchEvent 的對象必須同時直接或者簡介繼承 CCNode
    CCNode* nLsTe = dynamic_cast<CCNode*>(this);

    // 共同的分枝
    CCNode* allParent = NULL;
    // 尋找 a 與 b 共同的分枝
    CCNode* nAParent = a;
    CCNode* nBParent = b;
    CCNode* nAChild = NULL;
    CCNode* nBChild = NULL;
    do {
        nAChild = nAParent;
        nAParent = nAParent->getParent();
        if (!nAParent)
            break;

        nBParent = b;
        do {
            nBChild = nBParent;
            nBParent = nBParent->getParent();
            if (!nBParent)
                break;
            if (nAParent == nBParent) {
                allParent = nAParent;
                break;
            }
            if (nBParent == nLsTe) {
                break;
            }
        } while (1);
        if (allParent)
            break;
        if (nAParent == nLsTe) {
            break;
        }
    } while (1);

    // 此處只須要判斷 nAChild 和 nBChild 的優先級便可,默認返回 a
    if (!nAChild || !nBChild)
        return a;
    // 根據 ZOrder 判斷,若是 ZOrder同樣,根據索引位置判斷
    if ( nAChild->getZOrder() == nBChild->getZOrder() )
    {
        return (allParent->getChildren().getIndex(nAChild)) > (allParent->getChildren().getIndex(nBChild)) ? a : b;
    } 
    else
    {
        return nAChild->getZOrder() > nBChild->getZOrder() ? a : b;
    }
    


}

調用例子:html

1. 繼承
this

class HelloWorld : public cocos2d::Layer, public LsTouchEvent

2. 重寫 touchEventAction:spa

void HelloWorld::touchEventAction(LsTouch* touch)
{
    //touch->getEventId() 事件ID
    CCLog("touch event action id: %d", touch->getEventId());
}

3.調用code

    //建立精靈    
    auto sprint1 = Sprite::create("CloseNormal.png");
    //建立可觸控對象
    LsTouch* lt = LsTouch::create();
    //設置可觸控對象的位置
    lt->setPosition(origin.x, origin.y);
    //把精靈綁定到可操控對象上,把精靈加入帶可操控對象表
    lt->setDisplay(sprint1);
    //把可觸控對象加入到場景中
    this->addChild(lt);
    //把可觸控對象加入事件處理,當觸屏綁定精靈時調用touchEventAction
    //並傳達事件ID,根據區分ID 處理對應的事情
    this->addLsTouch(lt, 120);
    //建立佈景的 觸摸監聽
    auto listener1 = EventListenerTouchOneByOne::create();//建立一個觸摸監聽  
    listener1->setSwallowTouches(true);//設置是否想下傳遞觸摸

    listener1->onTouchBegan = [ = ](Touch* touch, Event* event){
        // 給事件類 發送消息
        sendTouchMessage(touch);
        return true;

     };
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

本文引用:http://blog.leafsoar.com/archives/2013/05-25.htmlorm

相關文章
相關標籤/搜索