Lua游戏开发cocos2dx

Cocos2d-x 触摸节点

2016-07-09  本文已影响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方法。

上一篇下一篇

猜你喜欢

热点阅读