點擊交互的四種處理

1、概述


    遊戲也好,程序也好,只有能與用戶交互纔有意義。手機上的交互大致可以分爲兩部分:點擊和輸入。其中點擊更爲重要,幾乎是遊戲中全部的交互。在Cocos2d-x 3.0中,更改了dispatch機制。同時加入了兩種新的交互形式:listener 和touchEvent回調。加上先前版本中的點擊函數回調,與重寫layer層的touch消息響應,構成了一個相對完整的交互模式。先上一張Demo的圖:



2、四種點擊


1、函數回調

函數回調是最簡單的響應形式,一直以來被用於MenuItem中的點擊處理。在新版本中,此處發生了些小改變。我們可以看到在生成的程序中相關代碼是這樣的:

  1. // a selector callback  
  2. void menuCloseCallback(Object* pSender);  
  3.   
  4. auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",  
  5.                         CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));  
  6.   
  7. void HelloWorld::menuCloseCallback(Object* pSender)  
  8. {  
  9.     Director::getInstance()->end();  
  10.   
  11. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  12.     exit(0);  
  13. #endif  
  14. }  

    其中CC_CALLBACK_1宏是將函數與對象綁定在一起,1表示這個函數有一個參數。當點擊這個按鈕時,會調用這個回調函數。

    除了基於c++11的這個形式的改變,使用方法與先前相同。


此種方式已經被捨棄,可以使用第四種方法做替代。

   2、Layer的touch消息響應

     雖然重寫了底層的dispatch,但對這層的使用影響並不大。我們同樣需要重寫:

  1. //單點響應  
  2. virtual bool onTouchBegan(Touch* touch, Event  *event) override;  
  3. virtual void onTouchMoved(Touch* touch, Event  *event) override;  
  4. virtual void onTouchEnded(Touch* touch, Event  *event) override;  
  5. virtual void onTouchCancelled(Touch *touch, Event *event) override;  
  6.   
  7. //多點響應  
  8. virtual bool onTouchesBegan(Touch* touch, Event  *event) override;  
  9. virtual void onTouchesMoved(Touch* touch, Event  *event) override;  
  10. virtual void onTouchesEnded(Touch* touch, Event  *event) override;  
  11. virtual void onTouchesCancelled(Touch *touch, Event *event) override;  


    重寫這些函數來對layer的點擊做處理。當然,我們需要:

  1. setTouchEnabled(true)。  

 

   此外有個小改動。對於單點觸控響應,可以調用:

  

  1. //設置爲單點響應  
  2. setTouchMode(Touch::DispatchMode::ONE_BY_ONE);  
  3. //設置爲多點響應(默認)  
  4. setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);  

    進行設置,而不需要再用設置Delegate的方式來做了。


    3、TouchEvent響應

    這是新加入的響應方式。它主要是使用在UIWidget上的。可以將其看做是函數回調的一個擴展,爲更多的響應處理提供可能。使用方法大致是:

  1. //聲明  
  2. void touchButton(Object* object,TouchEventType type);  
  3.   
  4. //掛接到控件上  
  5. uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton));  
  6.   
  7. //實現  
  8. void HelloWorld::touchButton(Object* object,TouchEventType type)  
  9. {  
  10.     LabelTTF* label;  
  11.     switch (type)  
  12.     {  
  13.     case TouchEventType::TOUCH_EVENT_BEGAN:  
  14.         label = static_cast<LabelTTF*>(getChildByTag(11));  
  15.         label->setString("按下按鈕");  
  16.         break;  
  17.     case TouchEventType::TOUCH_EVENT_MOVED:  
  18.         label = static_cast<LabelTTF*>(getChildByTag(11));  
  19.         label->setString("按下按鈕移動");  
  20.         break;  
  21.     case TouchEventType::TOUCH_EVENT_ENDED:  
  22.         label = static_cast<LabelTTF*>(getChildByTag(11));  
  23.         label->setString("放開按鈕");  
  24.         break;  
  25.     case TouchEventType::TOUCH_EVENT_CANCELED:  
  26.         label = static_cast<LabelTTF*>(getChildByTag(11));  
  27.         label->setString("取消點擊");  
  28.         break;  
  29.     default:  
  30.         break;  
  31.     }  
  32. }  

    因爲所有的UIWidget都要添加到UILayer上,而UILayer通常作爲UI的Widget都會在最上層,所以可以“基本上”認爲這種使用方式會優先於其他方式處理點擊消息。因爲UILayer也會有層級的改變,比如它和MenuItem之間的關係。所以說“基本上”。


4、Listener消息響應方式

    這種實現也是新加入的。它更像是點擊的一個層次過濾器。點擊時,在listener隊裏中進行過濾。每一個listener檢查自己保存的touch消息響應是否會被觸發。一層一層過濾,最後在到Layer的touch消息響應。

    我覺得它的設計的初衷是爲任意sprite提供一套自己制定的點擊響應。但這樣的實現仍然要寫很多條件判斷,沒有能夠控件化。可能我的理解有些偏差,歡迎討論。

    它被設計成一個全局點擊響應控制。具體的用法大致是這樣:


  1. //auto dispatcher = EventDispatcher::getInstance();  
  2. //  auto myListener = EventListenerTouch::create(Touch::DispatchMode::ONE_BY_ONE);  
  3. auto dispatcher = Director::getInstance()->getEventDispatcher();  
  4. auto myListener = EventListenerTouchOneByOne::create();  
  5.   
  6. //如果不加入此句消息依舊會向下傳遞  
  7. myListener->setSwallowTouches(true);  
  8.   
  9. myListener->onTouchBegan = [=](Touch* touch,Event* event)  
  10. {  
  11.     //some check  
  12.     if (pass)  
  13.     {  
  14.         return true;  
  15.     }  
  16.     return false;  
  17. };  
  18.   
  19. myListener->onTouchMoved = [=](Touch* touch,Event* event)  
  20. {  
  21.     //do something  
  22. };  
  23.   
  24. myListener->onTouchEnded = [=](Touch* touch,Event* event)  
  25. {  
  26.     //do something  
  27. };  
  28.   
  29. dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1);  
  30. dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);  

    其原理是在dispatcher中檢查listener列表,例如myListener或加進來的其他listener。然後每個listener檢查自己中的Item看能否達到檢查條件,例如:mySprite1,mySprite2。然後執行相應的操作。但這樣的話,當控件很多的時候,每一次事件都進行這種雙鏈表的檢查操作不知會不會影響些性能?


3、實例

    光說不練假把式,於是就動手寫了一個上面的Demo:

    點擊背景區域可以移動整個場景,點擊藍色小方塊可以半透明移動它,點擊藍色按鈕可以更改文字,顯示狀態。點擊右下角按鈕退出程序。

    項目配置可參照: 

Cocos2d-x 3.0 開發(十六)cocos2dx-3.0beta版建立新項目並加載CocoStudio導出文件

4、總結

    根據不同的交互需要,選擇不同的實現方式,會更有利於我們的維護和擴展,相應例子可以在下面下載。


    Demo下載:http://download.csdn.net/detail/fansongy/6399291 不要資源分,覺得好勞煩點下 “頂” ~

   Demo For Beta2 下載:http://download.csdn.net/detail/fansongy/6892047


本篇博客出自阿修羅道,轉載請註明出處,禁止用於商業用途:http://blog.csdn.net/fansongy/article/details/12716671 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章