http://blog.csdn.net/crayondeng/article/details/18795451
在cocos2d-x 2.x版本中,相信大家都抱怨過其中的觸摸機制;在3.0版本中,採用了全新的觸摸事件處理機制。
在官方的文檔中:點擊打開鏈接 這篇文章有對新的事件分發機制的介紹。
下面,我將通過引擎中自帶的sample來探索一下這個新的觸摸事件處理機制。
註:例子來自Test cpp/NewEventDispatcherTest
一、例子1
(1)創建三個精靈
- auto sprite1 = Sprite::create("Images/CyanSquare.png");
- sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));
- addChild(sprite1, 10); //其中 10 表示 zOreder
- auto sprite2 = Sprite::create("Images/MagentaSquare.png");
- sprite2->setPosition(origin+Point(size.width/2, size.height/2));
- addChild(sprite2, 20);
- auto sprite3 = Sprite::create("Images/YellowSquare.png");
- sprite3->setPosition(Point(0, 0));
- sprite2->addChild(sprite3, 1); //注意 sprite3 是添加到 sprite2 上的
(2)創建一個單點觸摸事件監聽器,處理觸摸事件邏輯
- // Make sprite1 touchable
- auto listener1 = EventListenerTouchOneByOne::create();//創建一個觸摸監聽
- listener1->setSwallowTouches(true); //設置是否想下傳遞觸摸
- //通過 lambda 表達式 直接實現觸摸事件的回掉方法
- listener1->onTouchBegan = [](Touch* touch, Event* event){
- auto target = static_cast<Sprite*>(event->getCurrentTarget());
- Point locationInNode = target->convertToNodeSpace(touch->getLocation());
- Size s = target->getContentSize();
- Rect rect = Rect(0, 0, s.width, s.height);
- if (rect.containsPoint(locationInNode))
- {
- log("sprite began... x = %f, y = %f", locationInNode.x, locationInNode.y);
- target->setOpacity(180);
- return true;
- }
- return false;
- };
- listener1->onTouchMoved = [](Touch* touch, Event* event){
- auto target = static_cast<Sprite*>(event->getCurrentTarget());
- target->setPosition(target->getPosition() + touch->getDelta());
- };
- listener1->onTouchEnded = [=](Touch* touch, Event* event){
- auto target = static_cast<Sprite*>(event->getCurrentTarget());
- log("sprite onTouchesEnded.. ");
- target->setOpacity(255);
- if (target == sprite2)
- {
- sprite1->setZOrder(100);
- }
- else if(target == sprite1)
- {
- sprite1->setZOrder(0);
- }
- };
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);
①其中的觸摸監聽類型為:EventListenerTouchOneByOne 表示的是單點觸摸;而EventListenerTouchAllAtOnce 表示的就是多點觸摸。
- class EventListenerTouchOneByOne : public EventListener
- {
- public:
- static const std::string LISTENER_ID;
- static EventListenerTouchOneByOne* create();
- virtual ~EventListenerTouchOneByOne();
- void setSwallowTouches(bool needSwallow);
- /// 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
事件監聽器包含以下幾種:
- 觸摸事件 (EventListenerTouch)
- 鍵盤響應事件 (EventListenerKeyboard)
- 加速記錄事件 (EventListenerAcceleration)
- 鼠標響應事件 (EventListenerMouse)
- 自定義事件 (EventListenerCustom)
以上事件監聽器統一由
_eventDispatcher
來進行管理。
_eventDispatcher 是 Node 的屬性,通過它管理當前節點(如 場景 、層、精靈等 )的所有事件分發情況。但是它本身是一個單例模式值的引用,在 Node 構造函數中,通過 "Director::getInstance()->getEventDispatcher();" 獲取,有了這個屬性,我們能更為方便的調用。
有兩種方式將 事件監聽器 listener1 添加到 事件調度器_eventDispatcher 中:
- void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
- void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
- void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
- {
- CCASSERT(listener && node, "Invalid parameters.");
- CCASSERT(!listener->isRegistered(), "The listener has been registered.");
- if (!listener->checkAvailable())
- return;
- listener->setSceneGraphPriority(node);
- listener->setFixedPriority(0);
- listener->setRegistered(true);
- addEventListener(listener);
- }
- void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
- {
- CCASSERT(listener, "Invalid parameters.");
- CCASSERT(!listener->isRegistered(), "The listener has been registered.");
- CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");
- if (!listener->checkAvailable())
- return;
- listener->setSceneGraphPriority(nullptr);
- listener->setFixedPriority(fixedPriority);
- listener->setRegistered(true);
- listener->setPaused(false);
- addEventListener(listener);
- }
從中我們可以知道: 其中的 addEventListenerWithSceneGraphPriority 的事件監聽器優先級是 0 ;而且在 addEventListenerWithFixedPriority 中的事件監聽器的優先級不可以設置為 0,因為這個是保留給 SceneGraphPriority 使用的。
注意:(1) 這裡當我們再次使用 listener1 的時候,需要使用
clone()
方法創建一個新的克隆,因為在使用 addEventListenerWithSceneGraphPriority
或者 addEventListenerWithFixedPriority
方法時,會對當前使用的事件監聽器添加一個已註冊的標記,這使得它不能夠被添加多次。
看看clone()方法的代碼:
- EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()
- {
- auto ret = new EventListenerTouchOneByOne();
- if (ret && ret->init())
- {
- ret->autorelease();
- ret->onTouchBegan = onTouchBegan;
- ret->onTouchMoved = onTouchMoved;
- ret->onTouchEnded = onTouchEnded;
- ret->onTouchCancelled = onTouchCancelled;
- ret->_claimedTouches = _claimedTouches;
- ret->_needSwallow = _needSwallow;
- }
- else
- {
- CC_SAFE_DELETE(ret);
- }
- return ret;
- }
(2)另外,有一點非常重要,FixedPriority listener添加完之後需要手動remove,而SceneGraphPriority listener是跟node綁定的,在node的析構函數中會被移除。
- _eventDispatcher->cleanTarget(this);
- CC_SAFE_RELEASE(_eventDispatcher);
二、例子2
在上面的例子中,使用的是 addEventListenerWithSceneGraphPriority 添加觸摸監聽器,也就是單點觸摸。其結點的觸摸優先級都是相同的 0 。那麼上層的結點 是比 下層的結點 先處理觸摸事件的。
下面看看如何使用 addEventListenerWithFixedPriority 自定義結點的觸摸優先級。
- class TouchableSpriteWithFixedPriority : public Sprite
- {
- public:
- CREATE_FUNC(TouchableSpriteWithFixedPriority);
- TouchableSpriteWithFixedPriority()
- : _listener(nullptr)
- , _fixedPriority(0)
- , _useNodePriority(false)
- {
- }
- void setPriority(int fixedPriority) { _fixedPriority = fixedPriority; _useNodePriority = false; };
- void setPriorityWithThis(bool useNodePriority) { _useNodePriority = useNodePriority; _fixedPriority = true; }
- void onEnter() override
- {
- Sprite::onEnter();
- auto listener = EventListenerTouchOneByOne::create();
- listener->setSwallowTouches(true);
- listener->onTouchBegan = [=](Touch* touch, Event* event){
- Point locationInNode = this->convertToNodeSpace(touch->getLocation());
- Size s = this->getContentSize();
- Rect rect = Rect(0, 0, s.width, s.height);
- if (rect.containsPoint(locationInNode))
- {
- this->setColor(Color3B::RED);
- return true;
- }
- return false;
- };
- listener->onTouchMoved = [=](Touch* touch, Event* event){
- //this->setPosition(this->getPosition() + touch->getDelta());
- };
- listener->onTouchEnded = [=](Touch* touch, Event* event){
- this->setColor(Color3B::WHITE);
- };
- if (_useNodePriority)
- {
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
- }
- else
- {
- _eventDispatcher->addEventListenerWithFixedPriority(listener, _fixedPriority);
- }
- _listener = listener;
- }
- void onExit() override
- {
- _eventDispatcher->removeEventListener(_listener);
- Sprite::onExit();
- }
- private:
- EventListener* _listener;
- int _fixedPriority;
- bool _useNodePriority;
- };
(2)分別創建三個精靈,可以自定義設置每一個精靈的觸摸優先級。注意:優先級值小的,接受觸摸優先。
- auto sprite1 = TouchableSpriteWithFixedPriority::create();
- sprite1->setTexture("Images/CyanSquare.png");
- sprite1->setPriority(30);
- sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 40));
- addChild(sprite1, 10);
- auto sprite2 = TouchableSpriteWithFixedPriority::create();
- sprite2->setTexture("Images/MagentaSquare.png");
- sprite2->setPriority(20);
- sprite2->setPosition(origin+Point(size.width/2, size.height/2));
- addChild(sprite2, 20);
- auto sprite3 = TouchableSpriteWithFixedPriority::create();
- sprite3->setTexture("Images/YellowSquare.png");
- sprite3->setPriority(10);
- sprite3->setPosition(Point(0, 0));
- sprite2->addChild(sprite3, 1);
- /** Remove a listener
- * @param listener The specified event listener which needs to be removed.
- */
- void removeEventListener(EventListener* listener);
- /** Removes all listeners with the same event listener type */
- void removeEventListeners(EventListener::Type listenerType);
前者只是刪除某一個事件監聽器,而後者是刪除某一類事件監聽器(使用了 clone 克隆)
沒有留言:
張貼留言