cocos2D-x 3.x学习笔记【二】 事件机制入门

来源:互联网 发布:淘宝小也旗舰店靠谱吗 编辑:程序博客网 时间:2024/05/22 01:29
cocos2Dx v3.x 有了不同于以往版本的事件机制,个人认为,相比起以往来说,这样的事件机制无疑更简洁和有效率,因为,我是从AS3转过来的,哈哈。
和AS3的事件机制有着很多相似。

本博文基于以下环境:

Cocos2D-x v3.2

vc++ 2012

win7

=============================正文开始=====================================


事件机制包括两部分,第一部分是 事件监听器,第二部分是 事件分发器。
监听器起到监听事件的触发,实现触发后的逻辑;将事件监听器注册到事件分发器后,分发器负责在适当时候分发事件类型,然后调用相应类型的监听器。
事件监听器的类型如图:
inherent
  • 触摸事件 (EventListenerTouch)
  • 键盘响应事件 (EventListenerKeyboard)
  • 鼠标响应事件 (EventListenerMouse)
  • 自定义事件 (EventListenerCustom)
  • 加速记录事件 (EventListenerAcceleration)

_eventDispatcher的工作由三部分组成:

  • 事件分发器 EventDispatcher
  • 事件类型 EventTouch, EventKeyboard 等
  • 事件监听器 EventListenerTouch, EventListenerKeyboard 等。
以上来自官网的资料,下面是我测试所得。

cocos2Dx v3.x 事件分发的特点:
事件是 由下至上的深度,先后再前的广度 触发事件监听器来调用函数的。
举例:当两个 sprite 被包含 在一个 layer 中的时候,手指按下 ,事件首先触发的是 两个sprite的onTouchBegan,然后才是 layer 的;而这两个sprite,前调用 后面addChild 的 sprite ,再调用前面添加的sprite,其他函数等同。所以称为,由下至上,即先调用子节点,再调用父节点;先后再前,后面添加到层次中的先被调用,前面添加的随后才调用。
以上两种情况在没有修改 优先级 的情况 和 没有设置 setSwallowTouches 为true 的情况下起效。
这里有些特性需要把玩一下,十分有趣。

那么,开始看看怎么写代码吧。

首先,获得一个事件监听器的实例。
通过两个静态方法获得,
1、EventListenerTouchOneByOne::create();这个方法获得单点触碰监听,常用于按钮。
2、EventListenerTouch::create();获得多点触碰监听,不记录。

以下代码为 单点触碰 的官方文档图。
第一个为 手指按下时 调用,第二个为 手指移动时 调用,第三个为 手指松开 时调用,第四个为 手势中断 时调用。


代码分析,其中一部分源码来自:cpp-tests   newEventDispatcherTest.cpp
   auto sprite1 = Sprite::create("about.png");//创建精灵1,随意放入图片   sprite1->setAnchorPoint(Vec2(0.5,0.5));   sprite1->setPosition(Vec2(origin.x + visibleSize.width/2  , origin.y  +150 ));   this->addChild(sprite1);   auto listener1 = EventListenerTouchOneByOne::create();//获取 单点触碰 事件监听器。   listener1->setSwallowTouches(false);// 设置是否为吞没事件,在 onTouchBegan 方法返回 true 时吞没,其他节点将不会被触发事件   listener1->onTouchBegan = [](Touch* touch , Event* event ) {      auto target = static_cast<Sprite*>(event->getCurrentTarget()); //获取当前的节点,即注册了该事件的节点。                Vec2 locationInNode = target->convertToNodeSpace(touch->getLocation());//相对于 当前节点左下角原点 的触碰坐标,x y可能为负        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);  // 设置目标节点透明度为 180,满值255.            return true;        }        return false;    };//添加触碰结束逻辑函数      listener1->onTouchEnded = [](Touch* touch , Event* event) {    log( "sprite1 is on touch ended ");   };   listener1->onTouchMoved = [](Touch* touch , Event* event ) {   log( "sprite1 is on touch moved" );   };   auto listener2 = EventListenerTouchOneByOne::create();   listener2->onTouchBegan = [](Touch* touch , Event* event ) {   log("layer is on touch");   return true;   };   listener2->onTouchMoved = [](Touch* touch , Event* event ) {   log("layer is on touch move" );   };   listener2 -> onTouchEnded = [](Touch* touch , Event* event ) {   log( "layer is on touch end ");   };   _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1 , sprite1 );//给sprite1注册监听   _eventDispatcher->addEventListenerWithSceneGraphPriority(listener2 ,this );//给当前层注册监听,sprite1为该层的子节点
这些方法我调用了log,可以在调试的时候看一下控制台输出。

如图,可以看到,子节点 sprite1 的函数总是先于 layer 被调用(没有截图完整):

注意要点:
第一点:cocos2d-x v3.x的 触碰事件监听器,虽然注册的时候是以特定精灵或者层次等进行注册,但是触碰屏幕任意位置都可以触发事件。
 所以,需要在onTouchBegan中,对触发范围进行判断。
第二点:除了 onTouchBegan外,其他事件都不需要返回 bool。要注意的是,如果 onTouchBegan的返回是 false 的话,后面将不再调用该目标的 其他函数。也就是说,上面的例子中,sprite的 onTouchBegan 方法返回false,表示按下手势失效,那么即便后面操作也处于触发范围,也不会再调用其他的触发函数如 onTouchEnded。毕竟判断了你没有按下屏幕,那么后面的操作也就无从开始。
第三点:这里当我们再次使用 listener1 的时候,需要使用 clone() 方法创建一个新的克隆,因为在使用addEventListenerWithSceneGraphPriority 或者 addEventListenerWithFixedPriority 方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟node绑定的,在node的析构函数中会被移除。
第四点:setSwallowTouches,作用是让子节点接收到手指按下的事件之后不再继续触发其他的事件监听器。比如上面例子,此方法设置为 true。那么,在 sprite1 的 onTouchBegan 返回true之后,控制台输出是这样子,可以看到layer的事件监听器没有被触发:

 

第五点: 点击判断是分离的。一般意义上的点击,是按下手指和松开手指为一个完整的流程。事件机制里分别对应两个逻辑函数,onTouchBegan 和 onTouchEnded。所以想要对点击作一个监听的话,把点击逻辑放在在onTouchBegan里是有错漏的,也许用户是误点按钮,他会在按下以后手指划出按钮范围以避免发生点击(我想很多人都这么做过大笑),故而点击逻辑应该写在 ended里面。


如何移除监听:

我们可以通过以下方法移除一个已经被添加了的监听器。

_eventDispatcher->removeEventListener(listener);

也可以使用如下方法,移除当前事件分发器中所有监听器。

_eventDispatcher->removeAllEventListeners();

当使用removeAll的时候,此节点的所有的监听将被移除,推荐使用 指定删除的方式。removeAll之后菜单也不能响应。因为它也需要接受触摸事件。

建议:当想为某一个特定的 sprite 设置触碰事件监听的时候(比如当做按钮),应当将事件监听器的setSwallowTouches方法设定为true,并在此事件监听器的 onTouchBegan 方法中,对触碰范围进行判断,如果在该 sprite 的范围内,则返回true。这样的话,事件分发器将不再对其他的事件监听器进行分发操作,即优化了效率,也提高了操作的安全性。


完毕,关于键盘、鼠标以及加速记录事件可以查看官方资料。

明天研究将研究 事件优先级 和 自定义事件。

以上是今天研究所得,有所缺漏,望能指正。

0 0
原创粉丝点击