2016年4月7日 星期四

20160407_Cocos2d-x 3.0 新特性體驗(3)觸摸事件處理機制

在官方的文檔中:點擊打開鏈接  這篇文章有對新的事件分發機制的介紹。
下面,我將通過引擎中自帶的sample來探索一下這個新的觸摸事件處理機制。
註:例子來自Test cpp/NewEventDispatcherTest
一、例子1
(1)創建三個精靈
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. auto sprite1 = Sprite::create("Images/CyanSquare.png");  
  2.     sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));  
  3.     addChild(sprite1, 10); //其中 10 表示 zOreder  
  4.       
  5.     auto sprite2 = Sprite::create("Images/MagentaSquare.png");  
  6.     sprite2->setPosition(origin+Point(size.width/2, size.height/2));  
  7.     addChild(sprite2, 20);  
  8.       
  9.     auto sprite3 = Sprite::create("Images/YellowSquare.png");  
  10.     sprite3->setPosition(Point(0, 0));  
  11.     sprite2->addChild(sprite3, 1); //注意 sprite3 是添加到 sprite2 上的  

(2)創建一個單點觸摸事件監聽器,處理觸摸事件邏輯
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. // Make sprite1 touchable  
  2. auto listener1 = EventListenerTouchOneByOne::create();//創建一個觸摸監聽  
  3. listener1->setSwallowTouches(true); //設置是否想下傳遞觸摸  
  4.   
  5. //通過 lambda 表達式 直接實現觸摸事件的回掉方法  
  6. listener1->onTouchBegan = [](Touch* touch, Event* event){  
  7.     auto target = static_cast<Sprite*>(event->getCurrentTarget());  
  8.       
  9.     Point locationInNode = target->convertToNodeSpace(touch->getLocation());  
  10.     Size s = target->getContentSize();  
  11.     Rect rect = Rect(0, 0, s.width, s.height);  
  12.       
  13.     if (rect.containsPoint(locationInNode))  
  14.     {  
  15.         log("sprite began... x = %f, y = %f", locationInNode.x, locationInNode.y);  
  16.         target->setOpacity(180);  
  17.         return true;  
  18.     }  
  19.     return false;  
  20. };  
  21.   
  22. listener1->onTouchMoved = [](Touch* touch, Event* event){  
  23.     auto target = static_cast<Sprite*>(event->getCurrentTarget());  
  24.     target->setPosition(target->getPosition() + touch->getDelta());  
  25. };  
  26.   
  27. listener1->onTouchEnded = [=](Touch* touch, Event* event){  
  28.     auto target = static_cast<Sprite*>(event->getCurrentTarget());  
  29.     log("sprite onTouchesEnded.. ");  
  30.     target->setOpacity(255);  
  31.     if (target == sprite2)  
  32.     {  
  33.         sprite1->setZOrder(100);  
  34.     }  
  35.     else if(target == sprite1)  
  36.     {  
  37.         sprite1->setZOrder(0);  
  38.     }  
  39. };  
  40.   
  41. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);  
  42. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);  
  43. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);  

①其中的觸摸監聽類型為:EventListenerTouchOneByOne 表示的是單點觸摸;而EventListenerTouchAllAtOnce 表示的就是多點觸摸。
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. class EventListenerTouchOneByOne : public EventListener  
  2. {  
  3. public:  
  4.     static const std::string LISTENER_ID;  
  5.       
  6.     static EventListenerTouchOneByOne* create();  
  7.       
  8.     virtual ~EventListenerTouchOneByOne();  
  9.       
  10.     void setSwallowTouches(bool needSwallow);  
  11.       
  12.     /// Overrides  
  13.     virtual EventListenerTouchOneByOne* clone() override;  
  14.     virtual bool checkAvailable() override;  
  15.     //  
  16.   
  17. public:  
  18.     std::function<bool(Touch*, Event*)> onTouchBegan;  
  19.     std::function<void(Touch*, Event*)> onTouchMoved;  
  20.     std::function<void(Touch*, Event*)> onTouchEnded;  
  21.     std::function<void(Touch*, Event*)> onTouchCancelled;  
  22.       
  23. private:  
  24.     EventListenerTouchOneByOne();  
  25.     bool init();  
  26.       
  27.     std::vector<Touch*> _claimedTouches;  
  28.     bool _needSwallow;  
  29.       
  30.     friend class EventDispatcher;  
  31. };  
  32.   
  33.   
  34. class EventListenerTouchAllAtOnce : public EventListener  
  35. {  
  36. public:  
  37.     static const std::string LISTENER_ID;  
  38.       
  39.     static EventListenerTouchAllAtOnce* create();  
  40.     virtual ~EventListenerTouchAllAtOnce();  
  41.       
  42.     /// Overrides  
  43.     virtual EventListenerTouchAllAtOnce* clone() override;  
  44.     virtual bool checkAvailable() override;  
  45.     //  
  46. public:  
  47.     std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;  
  48.     std::function<void(const std::vector<Touch*>&, Event*)> onTouchesMoved;  
  49.     std::function<void(const std::vector<Touch*>&, Event*)> onTouchesEnded;  
  50.     std::function<void(const std::vector<Touch*>&, Event*)> onTouchesCancelled;  
  51.       
  52. private:  
  53.     EventListenerTouchAllAtOnce();  
  54.     bool init();  
  55. private:  
  56.       
  57.     friend class EventDispatcher;  
  58. };  
看起來很是熟悉吧,和cocos2dx 2.x 版本中的 target touch 和 standard touch 差不多吧!只是使用的形式不太一樣罷了。還有在3.0版本中,不需要註冊觸摸事件代理delegate了。

 _eventDispatcher
事件監聽器包含以下幾種:
  • 觸摸事件 (EventListenerTouch)
  • 鍵盤響應事件 (EventListenerKeyboard)
  • 加速記錄事件 (EventListenerAcceleration)
  • 鼠標響應事件 (EventListenerMouse)
  • 自定義事件 (EventListenerCustom)
以上事件監聽器統一由 _eventDispatcher 來進行管理。
_eventDispatcher 是 Node 的屬性,通過它管理當前節點(如 場景 、層、精靈等 )的所有事件分發情況。但是它本身是一個單例模式值的引用,在 Node 構造函數中,通過 "Director::getInstance()->getEventDispatcher();" 獲取,有了這個屬性,我們能更為方便的調用。
有兩種方式將 事件監聽器 listener1 添加到 事件調度器_eventDispatcher 中:
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)  
  2. void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)  
看看這兩種方式的實現代碼:
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)  
  2. {  
  3.     CCASSERT(listener && node, "Invalid parameters.");  
  4.     CCASSERT(!listener->isRegistered(), "The listener has been registered.");  
  5.       
  6.     if (!listener->checkAvailable())  
  7.         return;  
  8.       
  9.     listener->setSceneGraphPriority(node);  
  10.     listener->setFixedPriority(0);  
  11.     listener->setRegistered(true);  
  12.       
  13.     addEventListener(listener);  
  14. }  
  15.   
  16. void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)  
  17. {  
  18.     CCASSERT(listener, "Invalid parameters.");  
  19.     CCASSERT(!listener->isRegistered(), "The listener has been registered.");  
  20.     CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");  
  21.       
  22.     if (!listener->checkAvailable())  
  23.         return;  
  24.       
  25.     listener->setSceneGraphPriority(nullptr);  
  26.     listener->setFixedPriority(fixedPriority);  
  27.     listener->setRegistered(true);  
  28.     listener->setPaused(false);  
  29.   
  30.     addEventListener(listener);  
  31. }  

從中我們可以知道: 其中的 addEventListenerWithSceneGraphPriority 的事件監聽器優先級是 0 ;而且在 addEventListenerWithFixedPriority 中的事件監聽器的優先級不可以設置為 0,因為這個是保留給 SceneGraphPriority 使用的。
注意:(1) 這裡當我們再次使用 listener1 的時候,需要使用 clone() 方法創建一個新的克隆,因為在使用 addEventListenerWithSceneGraphPriority 或者 addEventListenerWithFixedPriority 方法時,會對當前使用的事件監聽器添加一個已註冊的標記,這使得它不能夠被添加多次。
看看clone()方法的代碼:
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()  
  2. {  
  3.     auto ret = new EventListenerTouchOneByOne();  
  4.     if (ret && ret->init())  
  5.     {  
  6.         ret->autorelease();  
  7.           
  8.         ret->onTouchBegan = onTouchBegan;  
  9.         ret->onTouchMoved = onTouchMoved;  
  10.         ret->onTouchEnded = onTouchEnded;  
  11.         ret->onTouchCancelled = onTouchCancelled;  
  12.           
  13.         ret->_claimedTouches = _claimedTouches;  
  14.         ret->_needSwallow = _needSwallow;  
  15.     }  
  16.     else  
  17.     {  
  18.         CC_SAFE_DELETE(ret);  
  19.     }  
  20.     return ret;  
  21. }  

(2)另外,有一點非常重要,FixedPriority listener添加完之後需要手動remove,而SceneGraphPriority listener是跟node綁定的,在node的析構函數中會被移除。
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. _eventDispatcher->cleanTarget(this);  
  2.    CC_SAFE_RELEASE(_eventDispatcher);  

二、例子2
在上面的例子中,使用的是 addEventListenerWithSceneGraphPriority 添加觸摸監聽器,也就是單點觸摸。其結點的觸摸優先級都是相同的 0 。那麼上層的結點 是比 下層的結點 先處理觸摸事件的。
下面看看如何使用 addEventListenerWithFixedPriority 自定義結點的觸摸優先級。
(1)首先自定義精靈,其中可以設置精靈接受觸摸的優先級。
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. class TouchableSpriteWithFixedPriority : public Sprite  
  2. {  
  3. public:  
  4.   
  5.     CREATE_FUNC(TouchableSpriteWithFixedPriority);  
  6.       
  7.     TouchableSpriteWithFixedPriority()  
  8.     : _listener(nullptr)  
  9.     , _fixedPriority(0)  
  10.     , _useNodePriority(false)  
  11.     {  
  12.     }  
  13.       
  14.     void setPriority(int fixedPriority) { _fixedPriority = fixedPriority; _useNodePriority = false; };  
  15.     void setPriorityWithThis(bool useNodePriority) { _useNodePriority = useNodePriority; _fixedPriority = true; }  
  16.       
  17.     void onEnter() override  
  18.     {  
  19.         Sprite::onEnter();  
  20.           
  21.         auto listener = EventListenerTouchOneByOne::create();  
  22.         listener->setSwallowTouches(true);  
  23.           
  24.         listener->onTouchBegan = [=](Touch* touch, Event* event){  
  25.               
  26.             Point locationInNode = this->convertToNodeSpace(touch->getLocation());  
  27.             Size s = this->getContentSize();  
  28.             Rect rect = Rect(0, 0, s.width, s.height);  
  29.               
  30.             if (rect.containsPoint(locationInNode))  
  31.             {  
  32.                 this->setColor(Color3B::RED);  
  33.                 return true;  
  34.             }  
  35.             return false;  
  36.         };  
  37.           
  38.         listener->onTouchMoved = [=](Touch* touch, Event* event){  
  39.             //this->setPosition(this->getPosition() + touch->getDelta());  
  40.         };  
  41.           
  42.         listener->onTouchEnded = [=](Touch* touch, Event* event){  
  43.             this->setColor(Color3B::WHITE);  
  44.         };  
  45.           
  46.         if (_useNodePriority)  
  47.         {  
  48.             _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);  
  49.         }  
  50.         else  
  51.         {  
  52.             _eventDispatcher->addEventListenerWithFixedPriority(listener, _fixedPriority);  
  53.         }  
  54.         _listener = listener;  
  55.     }  
  56.       
  57.     void onExit() override  
  58.     {  
  59.         _eventDispatcher->removeEventListener(_listener);  
  60.           
  61.         Sprite::onExit();  
  62.     }  
  63.   
  64. private:  
  65.     EventListener* _listener;  
  66.     int _fixedPriority;  
  67.     bool _useNodePriority;  
  68. };  

(2)分別創建三個精靈,可以自定義設置每一個精靈的觸摸優先級。注意:優先級值小的,接受觸摸優先。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. auto sprite1 = TouchableSpriteWithFixedPriority::create();  
  2.     sprite1->setTexture("Images/CyanSquare.png");  
  3.     sprite1->setPriority(30);  
  4.     sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 40));  
  5.     addChild(sprite1, 10);  
  6.       
  7.     auto sprite2 = TouchableSpriteWithFixedPriority::create();  
  8.     sprite2->setTexture("Images/MagentaSquare.png");  
  9.     sprite2->setPriority(20);  
  10.     sprite2->setPosition(origin+Point(size.width/2, size.height/2));  
  11.     addChild(sprite2, 20);  
  12.       
  13.     auto sprite3 = TouchableSpriteWithFixedPriority::create();  
  14.     sprite3->setTexture("Images/YellowSquare.png");  
  15.     sprite3->setPriority(10);  
  16.     sprite3->setPosition(Point(0, 0));  
  17.     sprite2->addChild(sprite3, 1);  


三、刪除觸摸監聽器的方法:
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /** Remove a listener 
  2.   *  @param listener The specified event listener which needs to be removed. 
  3.   */  
  4.  void removeEventListener(EventListener* listener);  
  5.   
  6.  /** Removes all listeners with the same event listener type */  
  7.  void removeEventListeners(EventListener::Type listenerType);  
前者只是刪除某一個事件監聽器,而後者是刪除某一類事件監聽器(使用了 clone 克隆)

沒有留言:

張貼留言

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...