cocos2dx 3.x 自学笔记 <二> cocos2dx 中注册触摸事件touchEvent 2.x 与 3.x

转载请注明来自_鞋男blog :http://www.jb51.cc/article/p-ayjqnrmk-bbq.html

cocos2.2.5还在编译依赖库,还是先看3.2的吧

环境:win8 +vs2013

时间有限还是先说说这个把

#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MInor__ >= 1)))
    #define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
#elif _MSC_VER >= 1400 //vs 2005 or higher
    #define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated) 
#else
    #define CC_DEPRECATED_ATTRIBUTE
#endif 
看不懂也没关系,主要是提醒大家但凡函数加了
CC_DEPRECATED_ATTRIBUTE
表示废弃了的意思,也只能在3.x中才能看到,想跟下去看看怎么实现,水平有限,跟不了。谁知道告诉我一声,先谢了。

但是有些函数没有加这个修饰,但是在编译的过程中也提示废弃,以后再深入研究。

    CC_DEPRECATED_ATTRIBUTE virtual bool ccTouchBegan(Touch *pTouch,Event *pEvent) final {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent); return false;};
    CC_DEPRECATED_ATTRIBUTE virtual void ccTouchMoved(Touch *pTouch,Event *pEvent) final {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}
    CC_DEPRECATED_ATTRIBUTE virtual void ccTouchEnded(Touch *pTouch,Event *pEvent) final {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}
    CC_DEPRECATED_ATTRIBUTE virtual void ccTouchCancelled(Touch *pTouch,Event *pEvent) final {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}
    
    CC_DEPRECATED_ATTRIBUTE virtual void cctouchesBegan(__Set *ptouches,Event *pEvent) final {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
    CC_DEPRECATED_ATTRIBUTE virtual void cctouchesMoved(__Set *ptouches,Event *pEvent) final {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
    CC_DEPRECATED_ATTRIBUTE virtual void cctouchesEnded(__Set *ptouches,Event *pEvent) final {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
    CC_DEPRECATED_ATTRIBUTE virtual void cctouchesCancelled(__Set *ptouches,Event *pEvent) final {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
被废弃了,为什么被废弃了(还是可以用吧),下面会讲的
    virtual bool onTouchBegan(Touch *touch,Event *unused_event); 
    virtual void onTouchMoved(Touch *touch,Event *unused_event); 
    virtual void onTouchEnded(Touch *touch,Event *unused_event); 
    virtual void onTouchCancelled(Touch *touch,Event *unused_event);

    virtual void ontouchesBegan(const std::vector<Touch*>& touches,Event *unused_event);
    virtual void ontouchesMoved(const std::vector<Touch*>& touches,Event *unused_event);
    virtual void ontouchesEnded(const std::vector<Touch*>& touches,Event *unused_event);
    virtual void ontouchesCancelled(const std::vector<Touch*>&touches,Event *unused_event);

注册监听:

 auto listener = EventListenerTouchOneByOne::create();
 listener->onTouchBegan = CC_CALLBACK_2(MainLayer::onTouchBegan,this);
 listener->onTouchEnded = CC_CALLBACK_2(MainLayer::onTouchEnded,this);
 listener->onTouchMoved = CC_CALLBACK_2(MainLayer::onTouchMove,this);//其实这里也可以用lambda去写
   listener->onTouchMoved = [](const std::vector<Touch*>&touches,Event *unused_event)->bool{ /* 函数体*/}
 _eventdispatcher->addEventListenerWithSceneGraPHPriority(listener,this);//认优先级0 ,
创建listener对象,根据listenerID映射listeners容器,将listener对象存入容器中
void Eventdispatcher::addEventListener(EventListener* listener)
{
    if (_indispatch == 0)
    {
        forceAddEventListener(listener);
    }
    else
    {
        _toAddedListeners.push_back(listener);
    }

    listener->retain();
}

之后,在_nodeListenersMap中建立node(target)与关联的listener的映射

void Eventdispatcher::associateNodeAndEventListener(Node* node,EventListener* listener)
{
    std::vector<EventListener*>* listeners = nullptr;
    auto found = _nodeListenersMap.find(node);
    if (found != _nodeListenersMap.end())
    {
        listeners = found->second;
    }
    else
    {
        listeners = new std::vector<EventListener*>();
        _nodeListenersMap.insert(std::make_pair(node,listeners));
    }
    
    listeners->push_back(listener);
}

一个
_eventdispatcher->addEventListenerWithFixedPriority(listener,Priority);//
我这里发现了个问题 
void Eventdispatcher::addEventListenerWithFixedPriority(EventListener* listener,int fixedPriority)
{
 CCASSERT(listener,"Invalid parameters.");
 CCASSERT(!listener->isRegistered(),"The listener has been registered.");
 CCASSERT(fixedPriority != 0,"0 priority is forbidden for fixed priority since it's used for scene graph based priority.");
 
 if (!listener->checkAvailable())
 return;
 
 listener->setAssociatednode(nullptr);
 listener->setFixedPriority(fixedPriority);
 listener->setRegistered(true);
 listener->setPaused(false);

 addEventListener(listener);
}//我 TM的超级郁闷 ,设置非0吧,assert了,设置〇吧,没有node ,甭的更厉害
 //无奈之下注释了第三行,OK了 唉只能说 被白fuck了一个小时,还能说了什么呢
此不绑定target,所以需要手动释放
_eventdispatcher->removeEventListener(listener),此处没有根据nonde

第三

_eventdispatcher->addCustomEventListener("custom",[](EventCustom*)->void{ log("a event");});
EventListenerCustom* Eventdispatcher::addCustomEventListener(const std::string &eventName,const std::function<void(EventCustom*)>& callback)
{
 EventListenerCustom *listener = EventListenerCustom::create(eventName,callback);//在这里创建listener
 addEventListenerWithFixedPriority(listener,1);
 return listener;
}
测试了下,编译过了,但是点击没有反应,知道这个意思就好了以后在看了


现在看看事件分发:

glview->pollEvents();

。。。。。。//物理事件传递

class GLFWEventHandler
{
public:
    static void onGLFWError(int errorID,const char* errorDesc)
    {
        if (_view)
            _view->onGLFWError(errorID,errorDesc);
    }

    static void onGLFWMouseCallBack(GLFWwindow* window,int button,int action,int modify)
    {
        if (_view)
            _view->onGLFWMouseCallBack(window,button,action,modify);
    }

    static void onGLFWMouseMoveCallBack(GLFWwindow* window,double x,double y)
    {
        if (_view)
            _view->onGLFWMouseMoveCallBack(window,x,y);
    }

    static void onGLFWMouseScrollCallback(GLFWwindow* window,double y)
    {
        if (_view)
            _view->onGLFWMouseScrollCallback(window,y);
    }

    static void onGLFWKeyCallback(GLFWwindow* window,int key,int scancode,int mods)
    {
        if (_view)
            _view->onGLFWKeyCallback(window,key,scancode,mods);
    }

    static void onGLFWCharCallback(GLFWwindow* window,unsigned int character)
    {
        if (_view)
            _view->onGLFWCharCallback(window,character);
    }

    static void onGLFWWindowPosCallback(GLFWwindow* windows,int x,int y)
    {
        if (_view)
            _view->onGLFWWindowPosCallback(windows,y);
    }

    static void onGLFWframebuffersize(GLFWwindow* window,int w,int h)
    {
        if (_view)
            _view->onGLFWframebuffersize(window,w,h);
    }

    static void onGLFWWindowSizefunCallback(GLFWwindow *window,int width,int height)
    {
        if (_view)
            _view->onGLFWWindowSizefunCallback(window,width,height);
    }

    static void setGLView(GLView* view)
    {
        _view = view;
    }

private:
    static GLView* _view;
};
我是用鼠标点的响应了鼠标的那个函数调用,根据鼠标事件调用
void GLViewProtocol::handletouchesEnd(int num,intptr_t ids[],float xs[],float ys[])
{
 handletouchesOfEndOrCancel(EventTouch::EventCode::ENDED,num,ids,xs,ys);
}
接下来才到我们之前注册的事件TouchEvent

void Eventdispatcher::dispatchEvent(Event* event){

void Eventdispatcher::dispatchTouchEvent(EventTouch* event){

这里面干的是

对事件排序,按优先级。

 auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID);
 auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);

得到各个TouchMode的Listeners:

    auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID);
    auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
就是各种超找匹配找出是否有注册的Event:
    if (oneByOneListeners)
    {
        auto mutabletouchesIter = mutabletouches.begin();
        auto touchesIter = originaltouches.begin();
        
        for (; touchesIter != originaltouches.end(); ++touchesIter)
        {
            bool isSwallowed = false;

            auto onTouchEvent = [&](EventListener* l) -> bool { // Return true to break
                EventListenerTouchOneByOne* listener = static_cast<EventListenerTouchOneByOne*>(l);
                
                // Skip if the listener was removed.
                if (!listener->_isRegistered)
                    return false;
             
                event->setCurrentTarget(listener->_node);
                
                bool isClaimed = false;
                std::vector<Touch*>::iterator removedIter;
                
                EventTouch::EventCode eventCode = event->getEventCode();
                
                if (eventCode == EventTouch::EventCode::BEGAN)
                {
                    if (listener->onTouchBegan)
                    {
                        isClaimed = listener->onTouchBegan(*touchesIter,event);
                        if (isClaimed && listener->_isRegistered)
                        {
                            listener->_claimedtouches.push_back(*touchesIter);
                        }
                    }
                }
                else if (listener->_claimedtouches.size() > 0
                         && ((removedIter = std::find(listener->_claimedtouches.begin(),listener->_claimedtouches.end(),*touchesIter)) != listener->_claimedtouches.end()))
                {
                    isClaimed = true;
                    
                    switch (eventCode)
                    {
                        case EventTouch::EventCode::MOVED:
                            if (listener->onTouchMoved)
                            {
                                listener->onTouchMoved(*touchesIter,event);
                            }
                            break;
                        case EventTouch::EventCode::ENDED:
                            if (listener->onTouchEnded)
                            {
                                listener->onTouchEnded(*touchesIter,event);
                            }
                            if (listener->_isRegistered)
                            {
                                listener->_claimedtouches.erase(removedIter);
                            }
                            break;
                        case EventTouch::EventCode::CANCELLED:
                            if (listener->onTouchCancelled)
                            {
                                listener->onTouchCancelled(*touchesIter,event);
                            }
                            if (listener->_isRegistered)
                            {
                                listener->_claimedtouches.erase(removedIter);
                            }
                            break;
                        default:
                            CCASSERT(false,"The eventcode is invalid.");
                            break;
                    }
                }
                
                // If the event was stopped,return directly.
                if (event->isstopped())
                {
                    updateListeners(event);
                    return true;
                }
                
                CCASSERT((*touchesIter)->getID() == (*mutabletouchesIter)->getID(),"");
                
                if (isClaimed && listener->_isRegistered && listener->_needSwallow)
                {
                    if (isNeedsMutableSet)
                    {
                        mutabletouchesIter = mutabletouches.erase(mutabletouchesIter);
                        isSwallowed = true;
                    }
                    return true;
                }
                
                return false;
            };
            
            //
            dispatchEventToListeners(oneByOneListeners,onTouchEvent);//主要在这里 这里用了lambda,执行循序要搞清楚了
            if (event->isstopped())
            {
                return;
            }
            
            if (!isSwallowed)
                ++mutabletouchesIter;
        }
    }
void Eventdispatcher::dispatchEventToListeners(EventListenerVector* listeners,const std::function<bool(EventListener*)>& onEvent)
这个函数告诉我们了若匹配到了不想下分发了

然后进入那个lambda函数里执行注册过的touch系列函数了。执行完了还会在下一个循环删除这一事件



现在讲讲ccs 2.2.x ,一直在发布新版本,ccs现在完善了不少,我们就拿最新的来将好了,ccs 3.0x之前的版本大家都熟悉了,不熟悉,网上教程一大把,我这里略微补充下

类:CCTouchdispatcher

void CCTouchdispatcher::addStandardDelegate(CCTouchDelegate *pDelegate,int nPriority)
{    
    CCTouchHandler *pHandler = CCStandardTouchHandler::handlerWithDelegate(pDelegate,nPriority);
    if (! m_bLocked)
    {
        forceAddHandler(pHandler,m_pStandardHandlers);
    }
    else
    {
        /* If pHandler is contained in m_pHandlersToRemove,if so remove it from m_pHandlersToRemove and return.
         * Refer issue #752(cocos2d-x)
         */
        if (ccCArrayContainsValue(m_pHandlersToRemove,pDelegate))
        {
            ccCArrayRemoveValue(m_pHandlersToRemove,pDelegate);
            return;
        }

        m_pHandlersToAdd->addobject(pHandler);
        m_bToAdd = true;
    }
}

我们经常重写void cclayer::registerWithTouchdispatcher(),然后调用上面的注册函数

void cclayer::registerWithTouchdispatcher()
{
    CCTouchdispatcher* pdispatcher = CCDirector::sharedDirector()->getTouchdispatcher();

    // Using LuaBindings
    if (m_pScriptTouchHandlerEntry)
    {
	    if (m_pScriptTouchHandlerEntry->isMultitouches())
	    {
	       pdispatcher->addStandardDelegate(this,0);
	       LUALOG("[LUA] Add multi-touches event handler: %d",m_pScriptTouchHandlerEntry->getHandler());
	    }
	    else
	    {
	       pdispatcher->addTargetedDelegate(this,m_pScriptTouchHandlerEntry->getPriority(),m_pScriptTouchHandlerEntry->getSwallowstouches());
	       LUALOG("[LUA] Add touch event handler: %d",m_pScriptTouchHandlerEntry->getHandler());
	    }
    }
    else
    {
        if( m_etouchMode == kCCtouchesAllAtOnce ) {
            pdispatcher->addStandardDelegate(this,0);
        } else {
            pdispatcher->addTargetedDelegate(this,m_nTouchPriority,true);
        }
    }
}

从上面代码可以看出如果我们改变m_etouchMode就可以修改注册模式,完成这个使命的函数cclayer::setTouchMode( ... )

但是一定要注意调用的顺序,我们都知道在layer切换的时候先是init(),然后onEnter(),onEnterTransitionDidFinish()

在CCDirect类中有这样的一代码可以看出

        m_pRunningScene->onEnter();
        m_pRunningScene->onEnterTransitionDidFinish();

我们都知道registerWithTouchdispatch()是在cclayer的onEnter里面调用,我们只需init()或者重写onEnter(){ setTouchMode(...),cclayer::onEnter();},OK!

还有一点:

void CCTouchdispatcher::addStandardDelegate(CCTouchDelegate *pDelegate,pDelegate);
            return;
        }

        m_pHandlersToAdd->addobject(pHandler);
        m_bToAdd = true;
    }
}
从参数类型看,
class CC_DLL CCTouchDelegate
{
public:

    CCTouchDelegate() {}

    virtual ~CCTouchDelegate()
    {
    }

    virtual bool ccTouchBegan(CCTouch *pTouch,CCEvent *pEvent) {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent); return false;};
    // optional

    virtual void ccTouchMoved(CCTouch *pTouch,CCEvent *pEvent) {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}
    virtual void ccTouchEnded(CCTouch *pTouch,CCEvent *pEvent) {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}
    virtual void ccTouchCancelled(CCTouch *pTouch,CCEvent *pEvent) {CC_UNUSED_ParaM(pTouch); CC_UNUSED_ParaM(pEvent);}

    // optional
     virtual void cctouchesBegan(CCSet *ptouches,CCEvent *pEvent) {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
     virtual void cctouchesMoved(CCSet *ptouches,CCEvent *pEvent) {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
     virtual void cctouchesEnded(CCSet *ptouches,CCEvent *pEvent) {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}
     virtual void cctouchesCancelled(CCSet *ptouches,CCEvent *pEvent) {CC_UNUSED_ParaM(ptouches); CC_UNUSED_ParaM(pEvent);}

};
我们已经知道他们想干什么事情了。

protected:
     CCArray* m_pTargetedHandlers;
     CCArray* m_pStandardHandlers;

    bool m_bLocked;
...
     CCArray* m_pHandlersToAdd;
    struct _ccCArray *m_pHandlersToRemove;
...

从CCTouchdispatcher的数据结构来看,主要是两个容器CCArray* m_pTargetedHandlers; CCArray* m_pStandardHandlers;m_bLocked
m_pHandlersToAdd;m_pHandlersToRemove主要是同步安全机制


注册好了就是分发了,主要算法都在 CCTouchdispatcher::touches(cocos2d::CCSet * ptouches,cocos2d::CCEvent * pEvent,unsigned int uIndex) ,分类处理

            switch (sHelper.m_type)
            {
            case CCTOUCHBEGAN:
                pHandler->getDelegate()->cctouchesBegan(pMutabletouches,pEvent);
                break;
            case CCTOUCHMOVED:
                pHandler->getDelegate()->cctouchesMoved(pMutabletouches,pEvent);
                break;
            case CCTOUCHENDED:
                pHandler->getDelegate()->cctouchesEnded(pMutabletouches,pEvent);
                break;
            case CCTOUCHCANCELLED:
                pHandler->getDelegate()->cctouchesCancelled(pMutabletouches,pEvent);
                break;
            }
暂时就写到这了,若有错误,还请留言指出,谢谢

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...