Cocos2dx 3.2 節點之間相互通信與設置觸摸吞噬的方法


  實際開發中,我們經常會遇到這樣的情況。我們有一個層layer1,這個層包含一個menu層,menu1層里又包含了一個節點按鈕button1。現在需要實現一個效果:點擊button1彈出一個對話框,這個對話框里也包含一個menu2和一個按鈕button2,點擊button2能夠關閉這個對話框。這個情況很普遍,在游戲ui中我們有大量的二級彈窗都需要用到這種效果(在這里我們不考慮直接在layer2里removefromparent,這樣就不能達成學習目的了)。我們可以用三種方法實現這個效果,分別是:

  1、通過代理中轉;

  2、通過parent指針獲得父節點;

  3、使用NotificationCenter消息管理器發送和接收消息;

  我們一個一個的來探究它們的實現方式。

一、通過代理中轉

  首先我們需要在layer1里新建一個代理類Delegate,這個代理類只含有一個public的純虛函數stop()。然后我們讓layer1繼承自Delegate,再在layer1中實現這個stop函數,具體內容先空着,等會再寫。接着layer2中加一條成員變量Delegate* example;然后我們回到layer1的按鈕button1的回調函數里在createlayer2之后,將layer1的this指針賦給layer2的example。之后在layer2的button2的回調里調用example的stop函數。最后我們在layer1的stop函數里將layer2的對象remove掉。文字還是很難理解,我們還是直接上代碼吧:

  這是layer1的頭文件:

 1 #ifndef __LAYER1_H__
 2 #define __LAYER1_H__
 3 
 4 #include "cocos2d.h"
 5 USING_NS_CC;
 6 class Delegate//代理類
 7 {
 8 public:
 9     virtual void stop() = 0;//純虛函數
10 };
11 class layer1 :public Scene,public Delegate
12 {
13 public:
14     static Scene* scene();
15     CREATE_FUNC(layer1);
16     bool init();
17     void stop();//重寫stop
18     void loadMenuItem();//加載按鈕
19     void func();//按鈕的回調
20     Layer* lay;//layer2的指針
21 };
22 
23 #endif

  這是layer1的cpp文件:

 1 #include "layer1.h"
 2 #include "layer2.h"
 3 Scene* layer1::scene()
 4 {
 5     Scene* scene1 = Scene::create();
 6     Layer* layer = Layer::create();
 7     scene1->addChild(layer);
 8     return scene1;
 9 }
10 bool layer1::init()
11 {
12     Scene::init();
13     loadMenuItem();
14     Size size = Director::getInstance()->getWinSize();
15     Sprite* sprite = Sprite::create("HelloWorld.png");//背景
16     sprite->setPosition(size.width / 2, size.height / 2);
17     addChild(sprite);
18     return true;
19 }
20 void layer1::loadMenuItem()
21 {
22     MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer1::func, this));//按鈕
23     item->setZOrder(100);
24     Menu* menu = Menu::create();
25     item->setPositionX(item->getPositionX() + 300);
26     menu->addChild(item);
27     addChild(menu,1);
28 }
29 void layer1::func()
30 {
31     layer2* layer = layer2::create();
32     layer->example = this;//將layer1的指針傳進去
33     lay = layer;
34     addChild(lay);
35 }
36 void layer1::stop()
37 {
38     lay->removeFromParent();//將layer2刪除釋放
39 }

  這是layer2的頭文件:

 1 #ifndef __LAYER2_H__
 2 #define __LAYER2_H__
 3 #include "cocos2d.h"
 4 #include "layer1.h"
 5 USING_NS_CC;
 6 class layer2:public Layer
 7 {
 8 public:
 9     CREATE_FUNC(layer2);
10     bool init();
11     void loadMenuItem();
12     Delegate* example;//存放layer1的指針

13    void func();
14 };

15 #endif

  這是layer2的cpp文件:

 1 #include "layer2.h"
 2 
 3 bool layer2::init()
 4 {
 5     Sprite* sprite = Sprite::create("background.png");
 6     Size size = Director::getInstance()->getWinSize();
 7     sprite->setPosition(size.width / 2, size.height / 2);
 8     addChild(sprite);
 9     loadMenuItem();
10     return true;
11 }
12 void layer2::loadMenuItem()
13 {
14     MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer2::func, this));
15     item->setZOrder(100);
16     item->setPositionY(item->getPositionY() + 20);
17     Menu* menu = Menu::create();
18     menu->addChild(item);
19     addChild(menu, 1);
20 }
21 void layer2::func()
22 {
23     example->stop();
24 }

  現在我們可以實現這個效果了,如下圖:

  但是我發現了一個致命的bug。因為layer2的背景層比較小,並沒有遮住layer1的button1按鈕。問題來了:在layer2層彈出以后,我們依然可以點擊layer1層的按鈕,之后在點擊button2就會崩潰。怎么解決這個bug呢?這就要說到3.x 版本的觸摸事件分發機制了。在2.x版本中,我們想要處理觸摸事件,需要重載相應的touch函數,那時為了處理這種bug,我們需要將一個層的優先級設到-130(因為menu也是一個層,而且優先級相當之高,有-128,數值越低優先級越高,為了不讓menu先處理觸摸事件,我們需要將攔截層的優先級設置的比menu高才行),然后在這個層的觸摸函數里決定如何調用其他層的觸摸函數,如此達到分發觸摸事件的目的。

  然而3.x版本對觸摸監聽做出了重大改動,所有的觸摸監聽統一由事件分發器_eventDispatcher注冊后處理。

  我的方法是這樣的:我的原始層是layer1,對話框是layer2,我在layer2里注冊了一個觸摸監聽,把它的優先級設為-130(這個觸摸監聽需要你自己在onExit函數里release),這樣在對話框彈出來之后,這個觸摸監

  聽就會攔截所有的觸摸信號,我在這個監聽里面調用layer2的menu的touch函數,注意,當menu的touchbegan返回false的時候,我們不執行menu的touchend函數。這樣就可以實現我們想要的效果了!

  接下來上代碼!

  因為layer1的代碼基本無改動,我就只貼layer2的代碼了。

  頭文件:

 1 #ifndef __LAYER2_H__
 2 #define __LAYER2_H__
 3 #include "cocos2d.h"
 4 #include "layer1.h"
 5 USING_NS_CC;
 6 class layer2:public Layer
 7 {
 8 public:
 9     CREATE_FUNC(layer2);
10     bool init();
11     void loadMenuItem();
12     Delegate* example;
13     void func();
14     Sprite* background;
15     EventListenerTouchOneByOne* _ev;//我們需要保存監聽事件,因為它不受cocos自動管理,我們需要手動釋放它
16     bool flag;
17     void onExit()
18     {
19         Layer::onExit();
20         _eventDispatcher->removeEventListener(_ev);//手動釋放
21     }
22 };
23 #endif

  這是cpp文件:

122 #include "layer2.h"
123 
124 bool layer2::init()
125 {
126     Sprite* sprite = Sprite::create("background.png");
127     Size size = Director::getInstance()->getWinSize();
128     sprite->setPosition(size.width / 2, size.height / 2);
129     background = sprite;
130     addChild(sprite);
131     loadMenuItem();
132     return true;
133 }
134 void layer2::loadMenuItem()
135 {
136     MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer2::func, this));
137     item->setPositionY(item->getPositionY() + 20);
138     Menu* menu = Menu::create();
139     menu->addChild(item);
140     addChild(menu, 1);
141     EventListenerTouchOneByOne* ev = EventListenerTouchOneByOne::create();//創建一個單點觸摸監聽事件
142     ev->setSwallowTouches(true);//這個函數用於設置觸摸吞噬,如果兩個精靈相重疊,那么觸摸信號只會由優先級最高的處理,不會向下傳遞
143     ev->onTouchBegan = [=](Touch* touch,Event* ev){
144         flag = menu->onTouchBegan(touch, ev);//保存ontouchbegan的返回值,因為當flag為false的時候我們不需要執行ontouchend
145         return true;
146     };
147     ev->onTouchEnded = [=](Touch* touch,Event* ev){
148         if(!flag)
149         {
150             return;
151         }
152         menu->onTouchEnded(touch, ev);//調用menu的touchend函數
153     };
154     _ev = ev;
155     _eventDispatcher->addEventListenerWithFixedPriority(ev, -130);//注冊並設置優先級
156 }
157 void layer2::func()
158 {
159     example->stop();
160 }

二、通過parent指針獲得父節點

   這個就很簡單了,直接強轉:

1 void layer2::func()
2 {
3     layer1* layer = (layer1*)getParent();
4     //example->stop();
5     layer->lay->removeFromParent();
6 }

三、使用NotificationCenter消息管理器發送和接收消息

   這個也非常簡單,首先你要在button2的回調里設置發送消息:

1 NotificationCenter::getInstance()->postNotification("xuan",NULL);

  接着在需要layer1的init函數里設置監聽:

1     NotificationCenter::getInstance()->addObserver(this,callfuncO_selector(layer1::stop),"xuan",NULL);

  在這里附上一篇博文的地址,詳細的解釋了NotificationCenter用法:http://blog.csdn.net/yangxuan0261/article/details/21793513

   好了,這篇博文到此結束!

 

 

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM