[置顶] 【amazing cocos2d-x 3.0之五】新的触摸机制

我们将从源码级来分析cocos2d-x 3.0新的触摸机制。

运行引擎本身的TestCpp之后,我们可以看到Touchable Sprite Test,如下图,我们将从这里开始分析。

[置顶] 【amazing cocos2d-x 3.0之五】新的触摸机制_第1张图片

这个例子实现在一个界面中添加三个按钮,三个按钮互相遮挡,并且能够触发触摸事件。


1. 创建三个精灵,作为三个按钮的显示图片

    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);

2. 创建一个单点触摸事件监听器,在监听器中完成逻辑处理

    // 创建一个事件监听器 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);
        }
    };

3. 添加事件监听器到事件分发器

    // 添加监听器
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);

_eventDispatcher是Node的属性,通过它管理当前节点(如 场景、层、精灵等)的所有事件分发情况。但是它本身是一个单例模式的引用,在CCNode构造函数中,通过

Director::getInstance()->getEventDispatcher()获取,有了这个属性,我们能更加方便的调用。


注意:这里当我们再次使用listener1的时候,需要使用clone()方法创建一个新的克隆,因为在使用addEventListenerWithSceneGraphPriority或者addEventListenerWithFixedPriority方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。另外,有一点非常重要,FixedPriority listener添加完成之后需要手动remove,而SceneGraphPriority listener是跟node绑定的,在node的析构函数中会被移除。


4. 总结:新的触摸机制

对于触摸机制,2.2中继承一个delegate,里面定义了onTouchBegan等方法,然后在里面判断点击的元素,进行逻辑处理。而3.0新的触摸机制将事件处理逻辑独立出来,封装到一个Listener中,而以上的逻辑实现了以下功能:

(1)通过添加事件监听器,将精灵以显示优先级(SceneGraphPriority)添加到事件分发器。这意味着,当我们点击精灵按钮时,根据屏幕显示的“遮盖”实际情况,进行有序的函数回调。

(2)在事件逻辑处理时,根据各种条件处理触摸后的逻辑,如点击范围判断,设置被点击元素为不同的透明度,达到点击的效果。

(3)因为设置了listener1->setSwallowTouches(true);并且在onTouchBegan中做相应的判断,以决定其返回值是false还是true,用来处理触摸事件是否依据显示的顺序关系向后传递。

你可能感兴趣的:(cocos2d-x3.0,触摸机制)