Cocos2dx Touch事件原理剖析

简介: Cocos2dx Touch事件原理剖析

问题表现

当多点触摸和单点触摸的操作区域有重叠时,如果单点触摸事件设置为吞噬,那么多点触摸无法正常响应,例如:

网络异常,图片无法展示
|

上图中红色区域的多点触摸是无法正常响应的

代码分析

  • 鼠标点击事件的源头:组装touch事件,并派发出去
void GLViewImpl::onGLFWMouseCallBack(GLFWwindow* /*window*/, int button, int action, int /*modify*/)
{
     this->handleTouchesBegin(1, &id, &_mouseX, &_mouseY);
}
void GLView::handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[])
{
    EventTouch touchEvent;
    touchEvent._eventCode = EventTouch::EventCode::BEGAN;
    auto dispatcher = Director::getInstance()->getEventDispatcher();
    dispatcher->dispatchEvent(&touchEvent);
}
复制代码
  • 具体的派发过程:
void EventDispatcher::dispatchEvent(Event* event)
{
  if (event->getType() == Event::Type::TOUCH)
    {
        dispatchTouchEvent(static_cast<EventTouch*>(event));
        return;
    }
}
void EventDispatcher::dispatchTouchEvent(EventTouch* event)
{
    // 排序
    sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);
    sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
    auto oneByOneListeners = getListeners(EventListenerTouchOneByOne::LISTENER_ID);
    auto allAtOnceListeners = getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
    // 先处理单点
    if (oneByOneListeners)
    {
        for(auto& touches: originalTouches){
            dispatchTouchEventToListeners(listeners, onTouchEvent)
        } 
    }
    // 再处理多点,逻辑同上,注意mutableTouches的size
     if (allAtOnceListeners && mutableTouches.size() > 0)
    {
        // ...
    }
}
复制代码
  • dispatchTouchEventToListeners派发事件时,会将listener划分为3个批次进行循环执行
  1. 固定优先级小于0的
  2. 图形优先级
  3. 固定优先级大于0的

每个批次一旦执行失败onEvent,就会跳过这个批次的后续listener

void EventDispatcher::dispatchTouchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
    // 细节逻辑:固定优先级的会按照优先级依次排序,找到小于0的index
    for(){
        if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
        {
            shouldStopPropagation = true;
            break;
        }
    } 
}
复制代码
  • 最关键的就是这个onEvent逻辑了,也就是onTouchEvent:
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)
        {
            // touchBegan返回true,就会将这个touch保存在claimed里面,当touchMove、touchEnd的时候,就能检索到
            // 这也解释了touchBegan返回true,后续的move、end才能正常
            isClaimed = listener->onTouchBegan(touches, event);
            if (isClaimed && listener->_isRegistered)
            {
                listener->_claimedTouches.push_back(touches);
            }
        }
    }
    else if (listener->_claimedTouches.size() > 0
             && ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), touches)) != listener->_claimedTouches.end()))
    {
        isClaimed = true;
        switch (eventCode)
        {
            case EventTouch::EventCode::MOVED:
                if (listener->onTouchMoved)
                {
                    listener->onTouchMoved(touches, event);
                }
                break;
            case EventTouch::EventCode::ENDED:
                if (listener->onTouchEnded)
                {
                    listener->onTouchEnded(touches, event);
                }
                if (listener->_isRegistered)
                {
                    listener->_claimedTouches.erase(removedIter);
                }
                break;
            case EventTouch::EventCode::CANCELLED:
                if (listener->onTouchCancelled)
                {
                    listener->onTouchCancelled(touches, event);
                }
                if (listener->_isRegistered)
                {
                    listener->_claimedTouches.erase(removedIter);
                }
                break;
            default:
                CCASSERT(false, "The eventcode is invalid.");
                break;
        }
    }
    // If the event was stopped, return directly.
    // 可以在touch回调里面设置停止冒泡: event->stopPropagation()
    if (event->isStopped())
    {
        updateListeners(event);
        return true;
    }
    CCASSERT(touches->getID() == (*mutableTouchesIter)->getID(), "touches ID should be equal to mutableTouchesIter's ID.");
    // 当前的listener吞噬触摸点
    if (isClaimed && listener->_isRegistered && listener->_needSwallow) 
    {
        if (isNeedsMutableSet) // 当单点、多点 触摸同时存在
        {
            // 会将当前的touch移除,会影响后续的多点触摸的生效
            mutableTouchesIter = mutableTouches.erase(mutableTouchesIter);
            isSwallowed = true;
        }
        return true;
    }
    return false;
};
复制代码

影响范围

网络异常,图片无法展示
|



目录
相关文章
|
编解码 前端开发 算法
|
前端开发 图形学
|
前端开发 图形学 数据安全/隐私保护
|
Dart 开发者
Flutter 手势原理
Flutter 手势原理
245 0
Flutter 手势原理
|
vr&ar 图形学
【Unity3D 灵巧小知识点】☀️ | Unity UGUI组件Scroll View禁止 左右 或 上下 滑动
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。 也可以简单把 Unity 理解为一个游戏引擎,可以用来专业制作游戏!
【Unity3D 灵巧小知识点】☀️ | Unity UGUI组件Scroll View禁止 左右 或 上下 滑动
|
Android开发
Flutter 119: 图解简易 ACEFrameAnimated 帧动画
0 基础学习 Flutter,第一百一十九步:尝试简单帧动画效果!
365 0
Flutter 119: 图解简易 ACEFrameAnimated 帧动画
|
安全 数据挖掘 Android开发
Flutter 134: 图解动画小插曲之 SVGA 动画
0 基础学习 Flutter,第一百三十四步:探索新的动画 SVGA 方式!
754 0
Flutter 134: 图解动画小插曲之 SVGA 动画
|
API 容器
Flutter 51: 图解动画小插曲之 Flare 动画
0 基础学习 Flutter,第五十一步:学习一下 Flare 动画!
1889 0