实际开发中,我们经常会遇到这样的情况。我们有一个层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的头文件:

 #ifndef __LAYER1_H__
#define __LAYER1_H__ #include "cocos2d.h"
USING_NS_CC;
class Delegate//代理类
{
public:
virtual void stop() = ;//纯虚函数
};
class layer1 :public Scene,public Delegate
{
public:
static Scene* scene();
CREATE_FUNC(layer1);
bool init();
void stop();//重写stop
void loadMenuItem();//加载按钮
void func();//按钮的回调
Layer* lay;//layer2的指针
}; #endif

  这是layer1的cpp文件:

 #include "layer1.h"
#include "layer2.h"
Scene* layer1::scene()
{
Scene* scene1 = Scene::create();
Layer* layer = Layer::create();
scene1->addChild(layer);
return scene1;
}
bool layer1::init()
{
Scene::init();
loadMenuItem();
Size size = Director::getInstance()->getWinSize();
Sprite* sprite = Sprite::create("HelloWorld.png");//背景
sprite->setPosition(size.width / , size.height / );
addChild(sprite);
return true;
}
void layer1::loadMenuItem()
{
MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer1::func, this));//按钮
item->setZOrder();
Menu* menu = Menu::create();
item->setPositionX(item->getPositionX() + );
menu->addChild(item);
addChild(menu,);
}
void layer1::func()
{
layer2* layer = layer2::create();
layer->example = this;//将layer1的指针传进去
lay = layer;
addChild(lay);
}
void layer1::stop()
{
lay->removeFromParent();//将layer2删除释放
}

  这是layer2的头文件:

 #ifndef __LAYER2_H__
#define __LAYER2_H__
#include "cocos2d.h"
#include "layer1.h"
USING_NS_CC;
class layer2:public Layer
{
public:
CREATE_FUNC(layer2);
bool init();
void loadMenuItem();
Delegate* example;//存放layer1的指针    void func();
}; #endif

  这是layer2的cpp文件:

 #include "layer2.h"

 bool layer2::init()
{
Sprite* sprite = Sprite::create("background.png");
Size size = Director::getInstance()->getWinSize();
sprite->setPosition(size.width / , size.height / );
addChild(sprite);
loadMenuItem();
return true;
}
void layer2::loadMenuItem()
{
MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer2::func, this));
item->setZOrder();
item->setPositionY(item->getPositionY() + );
Menu* menu = Menu::create();
menu->addChild(item);
addChild(menu, );
}
void layer2::func()
{
example->stop();
}

  现在我们可以实现这个效果了,如下图:

  但是我发现了一个致命的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的代码了。

  头文件:

 #ifndef __LAYER2_H__
#define __LAYER2_H__
#include "cocos2d.h"
#include "layer1.h"
USING_NS_CC;
class layer2:public Layer
{
public:
CREATE_FUNC(layer2);
bool init();
void loadMenuItem();
Delegate* example;
void func();
Sprite* background;
EventListenerTouchOneByOne* _ev;//我们需要保存监听事件,因为它不受cocos自动管理,我们需要手动释放它
bool flag;
void onExit()
{
Layer::onExit();
_eventDispatcher->removeEventListener(_ev);//手动释放
}
};
#endif

  这是cpp文件:

 #include "layer2.h"

 bool layer2::init()
{
Sprite* sprite = Sprite::create("background.png");
Size size = Director::getInstance()->getWinSize();
sprite->setPosition(size.width / , size.height / );
background = sprite;
addChild(sprite);
loadMenuItem();
return true;
}
void layer2::loadMenuItem()
{
MenuItem* item = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_0(layer2::func, this));
item->setPositionY(item->getPositionY() + );
Menu* menu = Menu::create();
menu->addChild(item);
addChild(menu, );
EventListenerTouchOneByOne* ev = EventListenerTouchOneByOne::create();//创建一个单点触摸监听事件
ev->setSwallowTouches(true);//这个函数用于设置触摸吞噬,如果两个精灵相重叠,那么触摸信号只会由优先级最高的处理,不会向下传递
ev->onTouchBegan = [=](Touch* touch,Event* ev){
flag = menu->onTouchBegan(touch, ev);//保存ontouchbegan的返回值,因为当flag为false的时候我们不需要执行ontouchend
return true;
};
ev->onTouchEnded = [=](Touch* touch,Event* ev){
if(!flag)
{
return;
}
menu->onTouchEnded(touch, ev);//调用menu的touchend函数
};
_ev = ev;
_eventDispatcher->addEventListenerWithFixedPriority(ev, -);//注册并设置优先级
}
void layer2::func()
{
example->stop();
}

二、通过parent指针获得父节点

  这个就很简单了,直接强转:

 void layer2::func()
{
layer1* layer = (layer1*)getParent();
//example->stop();
layer->lay->removeFromParent();
}

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

  这个也非常简单,首先你要在button2的回调里设置发送消息:

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

  接着在需要layer1的init函数里设置监听:

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

  在这里附上一篇博文的地址,详细的解释了NotificationCenter用法:http://blog.csdn.net/yangxuan0261/article/details/21793513

  好了,这篇博文到此结束!

  

Cocos2dx 3.2 节点之间相互通信与设置触摸吞噬的方法的更多相关文章

  1. 不同vlan之间相互通信

    不同VLAN之间相互通信的两种方式 (单臂路由.三层交换) 试验目的: 1.通过单臂路由实现不同VLAN之间的通信 2.通过三层交换路由功能实现不同VLAN之间的通信   网络拓扑图: 1.单臂路由实 ...

  2. Redis 哨兵节点之间相互自动发现机制(自动重写哨兵节点的配置文件)

    Redis的哨兵机制中,如果是多哨兵模式,哨兵节点之间也是可以相互感知的,各种搜索之后出来的是千篇一律的一个基础配置文件,在配置当前哨兵节点的配置文件中,并没有配置其他哨兵节点的任何信息.如下是一个哨 ...

  3. android中四大组件之间相互通信

    好久没有写有关android有关的博客了,今天主要来谈一谈android中四大组件.首先,接触android的人,都应该知道android中有四大组件,activity,service,broadca ...

  4. 【转】不同VLAN之间相互通信及VTP、STP、EtherChannel概念

    厘清最后一个概念. 转了网上两个相关帖子: http://www.net130.com/CMS/Pub/Tech/tech_zh/2009_03_12_97386_3.htm http://blog. ...

  5. 不同VLAN之间相互通信的两种方式

    (单臂路由.三层交换) 试验环境:东郊二楼第三机房 试验设备:Catalyst 2950-24(SW3) Cisco 2611(R2) Catalyst 3750 SERIES (带两个SD接口,S8 ...

  6. 基于ssh的多节点之间互信通信的实现

    实现条件:node1:192.168.176.6 主机名称是node1.magedu.com: node2:192.168.176.6 主机名称是node1.magedu.com: 实现目的:在节点n ...

  7. docker images之间相互通信 link

    同一个host上的两个container 首先启动一个nginx. container起名叫netease_nginx docker run --detach --name netease_nginx ...

  8. android和javascript之间相互通信实例分析

    1.  AndroidManifest.xml中必须使用许可 "android.permission.INTERNET", 否则会出Web page not available错误 ...

  9. vue组件父子之间相互通信案例

随机推荐

  1. UpdateLayeredWindow是炫效果的关键

    自绘——是的,输入框每个字都自己绘制,计算行宽,行高,模拟光标闪烁,处理输入法的各种事件,以及选中,拖动等功能. 支持支持一下,实际上无句柄的,就是多行富文本编辑比较麻烦,其他的,都不复杂.很容易实现 ...

  2. 转:Mysql读写分离实现的三种方式

    1 程序修改mysql操作类可以参考PHP实现的Mysql读写分离,阿权开始的本项目,以php程序解决此需求.优点:直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配缺点:自 ...

  3. 中国linux论坛

    linux在国内经过十多年的发展,已慢慢走向成熟.昔日如雨后春笋般成长的linux网站,现在已出现了明显的两极分化.一部分已成长壮大,公司化运作,一部分面临域名出售或关闭的境地.  以笔者经验,以下十 ...

  4. Unity 小地图制作插件NJG MiniMap的简单使用

    unity版本:4.5.1 NJG MiniMap版本:1.5 参考链接:http://blog.csdn.net/wuming22222/article/details/37526659,作者:CS ...

  5. [FreeProxy]FreeProxy代理服务器端软件介绍 之 sock 5

    首先在FreeProxy上创建一个sock5 service 然后在Client 设置使用sock5

  6. Nightmare(搜索)

    http://acm.hdu.edu.cn/showproblem.php?pid=1072 /* 题意: 迷宫内有入口和出口 在6分钟结束后炸弹会爆炸,但是迷宫内有重置炸弹的装置,可以重置炸弹的时间 ...

  7. javax.el.PropertyNotFoundException错误

    在J2EE项目的开发过程中,遇到了这个问题,报错如下: 错误原因为在我model里的Person类里定义了一个Name属性,但是读取属性的getter方法的,一般按照属性首字母小写来处理,所以把Nam ...

  8. 组合数学:容斥原理(HDU1976)

    ●容斥原理所研究的问题是与若干有限集的交.并或差有关的计数. ●在实际中, 有时要计算具有某种性质的元素个数. 例: 某单位举办一个外语培训班, 开设英语, 法语两门课.设U为该单位所有人集合, A, ...

  9. [转载]Python兵器谱

    转载自:http://www.52nlp.cn/python-网页爬虫-文本处理-科学计算-机器学习-数据挖掘 曾经因为NLTK的缘故开始学习Python,之后渐渐成为我工作中的第一辅助脚本语言,虽然 ...

  10. MyBatis(4):动态SQL

    什么是动态SQL MyBatis的一个强大特性之一通常是它的动态SQL能力.如果你有使用JDBC或其他相似框架的经验,你就明白条件串联SQL字符串在一起是多么地痛苦,确保不能忘了空格或者在列表的最后的 ...