您的位置:首页 > 移动开发 > Cocos引擎

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

2014-10-22 12:32 639 查看
转载请注明来自_鞋男blog :/article/7618209.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 x, double y)
{
if (_view)
_view->onGLFWMouseScrollCallback(window, x, y);
}

static void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (_view)
_view->onGLFWKeyCallback(window, key, scancode, action, 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, x, 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, 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;
}
}
从参数类型看,
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;
}


暂时就写到这了,若有错误,还请留言指出,谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: