Cocos2d-x 触摸节点

作者: 码上说 | 来源:发表于2016-07-09 11:44 被阅读238次

    你是不是奇怪为什么 Node、Sprite、Layer等原生节点不能响应触摸事件,而 ImageView、Layout等GUI 控件就可以?
    恩,事实上我也不知道这份设定的原因。

    而实际上,我们经常需要原生节点可以响应触摸,为了实现目的,通常我们会这么做:在节点上盖上一层UILayout,让UIlayout去处理触摸事件。这么做的好处是处理简单;坏处是步骤繁琐,而且还需要管理一个无关紧要的节点。
    不!能!忍!那就只好自己来写一套了。

    我们看源码知道,GUI 控件是这么响应触摸事件的:

    void Widget::setTouchEnabled(bool enable)
    {
        if (enable == _touchEnabled)
        {
            return;
        }
        _touchEnabled = enable;
        if (_touchEnabled)
        {
            _touchListener = EventListenerTouchOneByOne::create();
            CC_SAFE_RETAIN(_touchListener);
            _touchListener->setSwallowTouches(true);
            _touchListener->onTouchBegan = CC_CALLBACK_2(Widget::onTouchBegan, this);
            _touchListener->onTouchMoved = CC_CALLBACK_2(Widget::onTouchMoved, this);
            _touchListener->onTouchEnded = CC_CALLBACK_2(Widget::onTouchEnded, this);
            _touchListener->onTouchCancelled = CC_CALLBACK_2(Widget::onTouchCancelled, this);
            _eventDispatcher->addEventListenerWithSceneGraphPriority(_touchListener, this);
        }
        else
        {
            _eventDispatcher->removeEventListener(_touchListener);
            CC_SAFE_RELEASE_NULL(_touchListener);
        }
    }
    

    从代码上看,给加上TouchOneByOne的事件监听就好了,是不是很简单?

    好,那我们就来模仿一下。
    为了秉持尽量不改动源码的宗旨,我们在lua层做修改。
    首先,我们需要定义一些会经常用到的基础的参数:_touchBegan_touchEnded_touchMoved分别代表触摸开始、触摸结束和触摸移动事件回调,_isTouchEnabled是触摸使能标识,_touchListener是触摸事件监听句柄 :

    local TouchNode = cc.Node
    TouchNode._touchBegan = nil
    TouchNode._touchEnded = nil
    TouchNode._touchMoved = nil
    TouchNode._isTouchEnabled = nil
    TouchNode._touchListener = nil
    

    接着,我们开始添加触摸事件监听:

    function TouchNode:enableTouchEvent()
        self:setTouchEnabled(true)
        self:onListenTouchEvent()
    end
    
    function TouchNode:disableTouchEvent()
        self:setTouchEnabled(false)
    end
    
    function TouchNode:isTouchEnabled()
        return self._isTouchEnabled
    end
    
    function TouchNode:setTouchEnabled(var)
        if var == self._isTouchEnabled then
            return 
        end
        self._isTouchEnabled = var and true or false
        return self._isTouchEnabled and self:onListenTouchEvent() or self:unListenTouchEvent()
    end
    
    function TouchNode:onListenTouchBegan(callback)
        if type(callback) == 'function' then
            self._touchBegan = callback
        end
    end
    
    function TouchNode:onListenTouchEnded(callback)
        if type(callback) == 'function' then
            self._touchEnded = callback
        end
    end
    
    function TouchNode:onListenTouchMoved(callback)
        if type(callback) == 'function' then
            self._touchMoved = callback
        end
    end
    
    -- 监听触摸事件响应
    function TouchNode:onListenTouchEvent()
        local function onTouchBegan(touch, event)
            local isFocus = isTouchFocusNode(touch, self)
            if isFocus then
                if self._touchBegan then
                    print('onListenTouchBegan')
                    self._touchBegan(self, ccui.TouchEventType.began)
                end
            end
            return isFocus
        end
        local function onTouchEnded(touch, event)
            local isFocus = isTouchFocusNode(touch, self)
            if isFocus then
                if self._touchEnded then
                    print('onListenTouchEnded')
                    self._touchEnded(self, ccui.TouchEventType.ended)
                end
            end
        end
        local function onTouchMoved(touch, event)
            if self._touchMoved then
                print('onListenTouchMoved')
                self._touchMoved(self, ccui.TouchEventType.moved)
            end
        end
    
        local listener = cc.EventListenerTouchOneByOne:create()
        listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
        listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED)
        listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED)
        self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, self)
        self._touchListener = listener
    end
    
    -- 设置是否吞噬响应
    function TouchNode:setSwallowTouches(var)
        if not self._touchListener then return end
        var = var and true or false
        self._touchListener:setSwallowTouches(false)
    end
    
    -- 取消监听
    function TouchNode:unListenTouchEvent()
        if not self._touchListener then return end
        self:getEventDispatcher():removeEventListener(self._touchListener)
        self._touchListener = nil
    end
    

    注意,isTouchFocusNode 还没有实现,你可能奇怪这是什么鬼?从字面上看,这是一个判断触摸点是否在节点上的方法。那为什么需要这个判断?因为如果不判断,触摸点满屏幕都是,我们无法确定触摸点是否被节点正确接收,因此这步判断是非常有必要的。

    最后,我们就来实现isTouchFocusNode

    function isTouchFocusNode(touch, node)
        local touchP = touch:getLocation()
        local bound  = node:getBoundingBox()
        local point  = node:convertTouchToNodeSpaceAR(touch)
        local anchor = node:getAnchorPoint()
        if (point.x >= -bound.width * anchor.x) and (point.x <= bound.width * (1-anchor.x)) and
           (point.y >= -bound.height * anchor.y) and (point.y <= bound.height * (1-anchor.y)) then
            print('Focus ...')
            return true
        end
        return false
    end
    

    注意:由于有些Node的BoundingBox为0,因此并不能接收到触摸事件,这时候需要根据具体情况调整节点的ContentSize。

    PS:
    源码中确定控件的触摸响应区域的具体实现与以上确定节点的实现是不一样的,有兴趣的可以看看 UIWidget的 hitTest方法。

    相关文章

      网友评论

        本文标题:Cocos2d-x 触摸节点

        本文链接:https://www.haomeiwen.com/subject/wnbrjttx.html