我们将从源码级来分析cocos2d-x 3.0新的触摸机制。
运行引擎本身的TestCpp之后,我们可以看到Touchable Sprite Test,如下图,我们将从这里开始分析。
这个例子实现在一个界面中添加三个按钮,三个按钮互相遮挡,并且能够触发触摸事件。
Point origin = Director::getInstance()->getVisibleOrigin(); Size size = Director::getInstance()->getVisibleSize(); auto sprite1 = Sprite::create("Images/CyanSquare.png"); sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80)); addChild(sprite1, 10); 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);
// 创建一个事件监听器 OneByOne 为单点触摸 auto listener1 = EventListenerTouchOneByOne::create(); // 设置是否吞没事件,在 onTouchBegan 方法返回 true 时吞没 listener1->setSwallowTouches(true); // 使用 lambda 实现 onTouchBegan 事件回调函数 listener1->onTouchBegan = [](Touch* touch, Event* event){ // 获取事件所绑定的 target 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); // 重新设置 ZOrder,显示的前后顺序将会改变 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);
Director::getInstance()->getEventDispatcher()获取,有了这个属性,我们能更加方便的调用。
注意:这里当我们再次使用listener1的时候,需要使用clone()方法创建一个新的克隆,因为在使用addEventListenerWithSceneGraphPriority或者addEventListenerWithFixedPriority方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。另外,有一点非常重要,FixedPriority listener添加完成之后需要手动remove,而SceneGraphPriority listener是跟node绑定的,在node的析构函数中会被移除。
对于触摸机制,2.2中继承一个delegate,里面定义了onTouchBegan等方法,然后在里面判断点击的元素,进行逻辑处理。而3.0新的触摸机制将事件处理逻辑独立出来,封装到一个Listener中,而以上的逻辑实现了以下功能:
(1)通过添加事件监听器,将精灵以显示优先级(SceneGraphPriority)添加到事件分发器。这意味着,当我们点击精灵按钮时,根据屏幕显示的“遮盖”实际情况,进行有序的函数回调。
(2)在事件逻辑处理时,根据各种条件处理触摸后的逻辑,如点击范围判断,设置被点击元素为不同的透明度,达到点击的效果。
(3)因为设置了listener1->setSwallowTouches(true);并且在onTouchBegan中做相应的判断,以决定其返回值是false还是true,用来处理触摸事件是否依据显示的顺序关系向后传递。