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;
    }
    


}

调用例子:

1. 继承

class HelloWorld : public cocos2d::Layer, public LsTouchEvent

2. 重写 touchEventAction:

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

3.调用

    //创建精灵    
    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.html

你可能感兴趣的:(cocos2dx 多层触摸机制)