cstriker1407的笔记本

好记性不如铅笔头

C && C++, cocos2dx, 编程

cocos2dx学习笔记:cocos2dx3.x版本触摸事件响应简单笔记

最近学习了下cocos2dx3.x版本的事件响应和分发逻辑,由于3.x版本作者个人用的比较少,结果是看的一头雾水。这里简单的笔记下大概的代码流程吧,以后用的多了理解加深了在随时更新吧。

先看写基础的类的定义。

CCEventTouch.h/cpp:

触摸事件类,继承了Event。用来管理触摸中的触摸点等。

#define TOUCH_PERF_DEBUG 1
class EventTouch : public Event
{
public:
    static const int MAX_TOUCHES = 5;//最多保存5个触摸点
    
    enum class EventCode
    {
        BEGAN,
        MOVED,
        ENDED,
        CANCELLED
    };//触摸点的类型


    EventTouch();


    inline EventCode getEventCode() const { return _eventCode; };
    inline const std::vector<Touch*>& getTouches() const { return _touches; };


#if TOUCH_PERF_DEBUG
    void setEventCode(EventCode eventCode) { _eventCode = eventCode; };
    void setTouches(const std::vector<Touch*>& touches) { _touches = touches; };
#endif
    
private:
    EventCode _eventCode;
    std::vector<Touch*> _touches;


    friend class GLViewProtocol;
    //将GLViewProtocol设置为友元类,由于各个平台实现不同,因此获取触摸点的方式不同,这里就不笔记了。
};
======
EventTouch::EventTouch()
: Event(Type::TOUCH)
{//调用构造函数时,申请特定大小的vector,用来存储触摸点
    _touches.reserve(MAX_TOUCHES);
}

CCEventListenerTouch.h/cpp:

触摸事件响应类,继承自EventListener。用来管理各个回调函数。

class EventListenerTouchOneByOne : public EventListener
{
public:
    static const std::string LISTENER_ID;
    
    static EventListenerTouchOneByOne* create();
    
    virtual ~EventListenerTouchOneByOne();
    
    void setSwallowTouches(bool needSwallow);
    bool isSwallowTouches();
    
    /// Overrides
    virtual EventListenerTouchOneByOne* clone() override;
    virtual bool checkAvailable() override;
    //

public:
    std::function<bool(Touch*, Event*)> onTouchBegan;
    std::function<void(Touch*, Event*)> onTouchMoved;
    std::function<void(Touch*, Event*)> onTouchEnded;
    std::function<void(Touch*, Event*)> onTouchCancelled;
    
private:
    EventListenerTouchOneByOne();
    bool init();
    
    std::vector<Touch*> _claimedTouches;
    bool _needSwallow;
    
    friend class EventDispatcher;
};


class EventListenerTouchAllAtOnce : public EventListener
{
public:
    static const std::string LISTENER_ID;
    
    static EventListenerTouchAllAtOnce* create();
    virtual ~EventListenerTouchAllAtOnce();
    
    /// Overrides
    virtual EventListenerTouchAllAtOnce* clone() override;
    virtual bool checkAvailable() override;
    //
public:
    std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;
    std::function<void(const std::vector<Touch*>&, Event*)> onTouchesMoved;
    std::function<void(const std::vector<Touch*>&, Event*)> onTouchesEnded;
    std::function<void(const std::vector<Touch*>&, Event*)> onTouchesCancelled;
    
private:
    EventListenerTouchAllAtOnce();
    bool init();
private:
    
    friend class EventDispatcher;
};

EventDispatcher事件分发代码:

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 there aren't any touch listeners, return directly.
    if (nullptr == oneByOneListeners && nullptr == allAtOnceListeners)
        return;
    
    bool isNeedsMutableSet = (oneByOneListeners && allAtOnceListeners);
    
    const std::vector<Touch*>& originalTouches = event->getTouches();
    	
//把传入的触摸事件拷贝一份,因为处理特定目标响应时可能会吞噬掉部分触摸事件。这里拷贝一份用来实现吞噬,原数据就不会被更改了。    	
    std::vector<Touch*> mutableTouches(originalTouches.size());
    std::copy(originalTouches.begin(), originalTouches.end(), mutableTouches.begin());

    //
    // process the target handlers 1st
    //
    //先处理特定目标响应(OneByOne)
    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的target设为传入listener的node,非常重要。这样graphic级别的listener回调函数就可以获取到自己的node了
                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时传入了第一个触摸点,返回是否处理
                        isClaimed = listener->onTouchBegan(*touchesIter, event);
                        if (isClaimed && listener->_isRegistered)
                        {
                            listener->_claimedTouches.push_back(*touchesIter);
                            //将这个起始触摸点放到vector中。用来判断该listener是否开始处理触摸了。个人认为用_claimedTouches来作为判断条件,而且这个成员变量的用途也很奇怪~~
                        }
                    }
                }
                else if (listener->_claimedTouches.size() > 0
                         && ((removedIter = std::find(listener->_claimedTouches.begin(), listener->_claimedTouches.end(), *touchesIter)) != listener->_claimedTouches.end()))
                {
                	//如果传入的点是MOVE型而且listener接受了响应(符合该if条件),那么就说明这些MOVE点也是被处理的。
                	//这样isClaimed会被设置成true。以便于后面判断是否需要Swallow。
                    isClaimed = true;
                    
                    switch (eventCode)
                    {
                        case EventTouch::EventCode::MOVED:
                            if (listener->onTouchMoved)
                            {
                                listener->onTouchMoved(*touchesIter, event);
                            }
                            break;
                        case EventTouch::EventCode::ENDED://当传入的触摸点的类型是ENDED时,调用onTouchEnded
                            if (listener->onTouchEnded)
                            {
                                listener->onTouchEnded(*touchesIter, event);
                            }
                            if (listener->_isRegistered)
                            {//结束后把_claimedTouches中的元素删掉。
                                listener->_claimedTouches.erase(removedIter);
                            }
                            break;
                        case EventTouch::EventCode::CANCELLED://和ENDED差不多
                            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(), "");
                
                //如果listener设置为Swallow,那么经过它处理的所有的点都要被删掉。
                //这里采取的措施是通过for循环,处理一个点,删除一个点。
                if (isClaimed && listener->_isRegistered && listener->_needSwallow)
                {
                    if (isNeedsMutableSet)
                    {
                        mutableTouchesIter = mutableTouches.erase(mutableTouchesIter);
                        isSwallowed = true;
                    }
                    return true;
                }
                
                return false;
            };
            
            //调用函数表达式onTouchEvent
            dispatchEventToListeners(oneByOneListeners, onTouchEvent);
            if (event->isStopped())
            {
                return;
            }
            
            //mutableTouches是拷贝自originalTouches的,因此初始时数据是相同的。
            //所以如果没有Swallow,mutableTouchesIter也要随着循环增加而增加。
            //保证在程序执行时,mutableTouchesIter和mutableTouchesIter指向的内容是对应的。
            if (!isSwallowed)
                ++mutableTouchesIter;
        }
    }
    
    //
    // process standard handlers 2nd
    //
    //如果还有剩下的触摸点没有被吞噬,那么一次丢给标准触摸响应(AllAtOnce)
    if (allAtOnceListeners && mutableTouches.size() > 0)
    {
        
        auto onTouchesEvent = [&](EventListener* l) -> bool{
            EventListenerTouchAllAtOnce* listener = static_cast<EventListenerTouchAllAtOnce*>(l);
            // Skip if the listener was removed.
            if (!listener->_isRegistered)
                return false;
            
            event->setCurrentTarget(listener->_node);
            //标准触摸响应就比较简单了,直接根据事件的类型调用响应的对应的回调函数即可。
            switch (event->getEventCode())
            {
                case EventTouch::EventCode::BEGAN:
                    if (listener->onTouchesBegan)
                    {
                        listener->onTouchesBegan(mutableTouches, event);
                    }
                    break;
                case EventTouch::EventCode::MOVED:
                    if (listener->onTouchesMoved)
                    {
                        listener->onTouchesMoved(mutableTouches, event);
                    }
                    break;
                case EventTouch::EventCode::ENDED:
                    if (listener->onTouchesEnded)
                    {
                        listener->onTouchesEnded(mutableTouches, event);
                    }
                    break;
                case EventTouch::EventCode::CANCELLED:
                    if (listener->onTouchesCancelled)
                    {
                        listener->onTouchesCancelled(mutableTouches, event);
                    }
                    break;
                default:
                    CCASSERT(false, "The eventcode is invalid.");
                    break;
            }
            
            // If the event was stopped, return directly.
            if (event->isStopped())
            {
                updateListeners(event);
                return true;
            }
            
            return false;
        };
        
        dispatchEventToListeners(allAtOnceListeners, onTouchesEvent);
        if (event->isStopped())
        {
            return;
        }
    }
    
    updateListeners(event);
}

 

Leave a Reply

5 × 3 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

Theme by Anders Norén

苏ICP备16032087号