Cocos2d-x 3.0 触摸事件处理机制

在本篇文章中,我们一testCpp里面的例子为准介绍一下其新特性

\

首先:创建三个精灵

auto sprite1 = Sprite::create("Images/CyanSquare.png");
sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));
addChild(sprite1, 10); //其中 10 表示 zOreder
     
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); //注意 sprite3 是添加到 sprite2 上的
创建一个单点触摸事件监听器,处理触摸事件逻辑

// Make sprite1 touchable
auto listener1 = EventListenerTouchOneByOne::create();//创建一个触摸监听
listener1->setSwallowTouches(true); //设置是否想下传递触摸
 
//通过 lambda 表达式 直接实现触摸事件的回掉方法
listener1->onTouchBegan = [](Touch* touch, Event* event){
    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);
    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);
①其中的触摸监听类型为:EventListenerTouchOneByOne 表示的是单点触摸;而EventListenerTouchAllAtOnce 表示的就是多点触摸。
class EventListenerTouchOneByOne : public EventListener
{
public:
    static const std::string LISTENER_ID;
     
    static EventListenerTouchOneByOne* create();
     
    virtual ~EventListenerTouchOneByOne();
     
    void setSwallowTouches(bool needSwallow);
     
    /// Overrides
    virtual EventListenerTouchOneByOne* clone() override;
    virtual bool checkAvailable() override;
    //
 
public:
    std::function<bool(touch*, event*)=""> onTouchBegan;
    std::function<void(touch*, event*)=""> onTouchMoved;
    std::function<void(touch*, event*)=""> onTouchEnded;
    std::function<void(touch*, event*)=""> onTouchCancelled;
     
private:
    EventListenerTouchOneByOne();
    bool init();
     
    std::vector<touch*> _claimedTouches;
    bool _needSwallow;
     
    friend class EventDispatcher;
};
 
 
class EventListenerTouchAllAtOnce : public EventListener
{
public:
    static const std::string LISTENER_ID;
     
    static EventListenerTouchAllAtOnce* create();
    virtual ~EventListenerTouchAllAtOnce();
     
    /// Overrides
    virtual EventListenerTouchAllAtOnce* clone() override;
    virtual bool checkAvailable() override;
    //
public:
    std::function<void(const std::vector<touch*="">&, Event*)> onTouchesBegan;
    std::function<void(const std::vector<touch*="">&, Event*)> onTouchesMoved;
    std::function<void(const std::vector<touch*="">&, Event*)> onTouchesEnded;
    std::function<void(const std::vector<touch*="">&, Event*)> onTouchesCancelled;
     
private:
    EventListenerTouchAllAtOnce();
    bool init();
private:
     
    friend class EventDispatcher;
};


看起来很是熟悉吧,和cocos2dx 2.x 版本中的 target touch 和 standard touch 差不多吧!只是使用的形式不太一样罢了。还有在3.0版本中,不需要注册触摸事件代理delegate了。


② _eventDispatcher

事件监听器包含以下几种:

  • 触摸事件 (EventListenerTouch)
  • 键盘响应事件 (EventListenerKeyboard)
  • 加速记录事件 (EventListenerAcceleration)
  • 鼠标响应事件 (EventListenerMouse)
  • 自定义事件 (EventListenerCustom)

    以上事件监听器统一由 _eventDispatcher 来进行管理。

    _eventDispatcher 是 Node 的属性,通过它管理当前节点(如 场景 、层、精灵等 )的所有事件分发情况。但是它本身是一个单例模式值的引用,在 Node 构造函数中,通过 "Director::getInstance()->getEventDispatcher();" 获取,有了这个属性,我们能更为方便的调用。

有两种方式将 事件监听器 listener1 添加到 事件调度器_eventDispatcher 中:
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
看看这两种方式的实现代码:

void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
    CCASSERT(listener && node, "Invalid parameters.");
    CCASSERT(!listener->isRegistered(), "The listener has been registered.");
     
    if (!listener->checkAvailable())
        return;
     
    listener->setSceneGraphPriority(node);
    listener->setFixedPriority(0);
    listener->setRegistered(true);
     
    addEventListener(listener);
}
 
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
    CCASSERT(listener, "Invalid parameters.");
    CCASSERT(!listener->isRegistered(), "The listener has been registered.");
    CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");
     
    if (!listener->checkAvailable())
        return;
     
    listener->setSceneGraphPriority(nullptr);
    listener->setFixedPriority(fixedPriority);
    listener->setRegistered(true);
    listener->setPaused(false);
 
    addEventListener(listener);
}
从中我们可以知道: 其中的 addEventListenerWithSceneGraphPriority 的事件监听器优先级是 0 ;而且在 addEventListenerWithFixedPriority 中的事件监听器的优先级不可以设置为 0,因为这个是保留给 SceneGraphPriority 使用的。

注意:(1) 这里当我们再次使用 listener1 的时候,需要使用 clone() 方法创建一个新的克隆,因为在使用 addEventListenerWithSceneGraphPriority 或者 addEventListenerWithFixedPriority 方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。

看看clone()方法的代码:

EventListenerTouchOneByOne* EventListenerTouchOneByOne::clone()
{
    auto ret = new EventListenerTouchOneByOne();
    if (ret && ret->init())
    {
        ret->autorelease();
         
        ret->onTouchBegan = onTouchBegan;
        ret->onTouchMoved = onTouchMoved;
        ret->onTouchEnded = onTouchEnded;
        ret->onTouchCancelled = onTouchCancelled;
         
        ret->_claimedTouches = _claimedTouches;
        ret->_needSwallow = _needSwallow;
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}
(2)另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟node绑定的,在node的析构函数中会被移除。
_eventDispatcher->cleanTarget(this);
   CC_SAFE_RELEASE(_eventDispatcher);

二、例子2

在上面的例子中,使用的是 addEventListenerWithSceneGraphPriority 添加触摸监听器,也就是单点触摸。其结点的触摸优先级都是相同的 0 。那么上层的结点 是比 下层的结点 先处理触摸事件的。

下面看看如何使用 addEventListenerWithFixedPriority 自定义结点的触摸优先级

\

1)首先自定义精灵,其中可以设置精灵接受触摸的优先级

<pre name="code" class="cpp">class TouchableSpriteWithFixedPriority : public Sprite
{
public:
    CREATE_FUNC(TouchableSpriteWithFixedPriority);
    TouchableSpriteWithFixedPriority() : _listener(nullptr) , _fixedPriority(0) , _useNodePriority(false) { }
    void setPriority(int fixedPriority) { _fixedPriority = fixedPriority; _useNodePriority = false; };
    void setPriorityWithThis(bool useNodePriority) { _useNodePriority = useNodePriority; _fixedPriority = true; }
    void onEnter()
    {
        Sprite::onEnter();
        auto listener = EventListenerTouchOneByOne::create();
        listener->setSwallowTouches(true);
        listener->onTouchBegan = [=](Touch* touch, Event* event){ Point locationInNode = this->convertToNodeSpace(touch->getLocation());
            Size s = this->getContentSize();
            Rect rect = Rect(0, 0, s.width, s.height);
            if (rect.containsPoint(locationInNode))
            {
                this->setColor(Color3B::RED);
                return true;
            }
            return false;
        };
        listener->onTouchMoved = [=](Touch* touch, Event* event)
        {
            //this->setPosition(this->getPosition() + touch->getDelta());
        };
        listener->onTouchEnded = [=](Touch* touch, Event* event){ this->setColor(Color3B::WHITE); };
        if (_useNodePriority)
        {
            _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
        }
        else {
            
            _eventDispatcher->addEventListenerWithFixedPriority(listener, _fixedPriority);
        }
        _listener = listener;
    }
    void onExit()
    {
        _eventDispatcher->removeEventListener(_listener); Sprite::onExit();
    }
private:
    EventListener* _listener;
    int _fixedPriority;
    bool _useNodePriority;
};
(2)分别创建三个精灵,可以自定义设置每一个精灵的触摸优先级。 注意:优先级值小的,接受触摸优先。
    auto sprite1 = TouchableSpriteWithFixedPriority::create();
    sprite1->setTexture("Images/CyanSquare.png");
    sprite1->setPriority(30);
    sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 40));
    addChild(sprite1, 10);
     
    auto sprite2 = TouchableSpriteWithFixedPriority::create();
    sprite2->setTexture("Images/MagentaSquare.png");
    sprite2->setPriority(20);
    sprite2->setPosition(origin+Point(size.width/2, size.height/2));
    addChild(sprite2, 20);
     
    auto sprite3 = TouchableSpriteWithFixedPriority::create();
    sprite3->setTexture("Images/YellowSquare.png");
    sprite3->setPriority(10);
    sprite3->setPosition(Point(0, 0));
    sprite2->addChild(sprite3, 1);
三、删除触摸监听器的方法:
/** Remove a listener
  *  @param listener The specified event listener which needs to be removed.
  */
 void removeEventListener(EventListener* listener);
 
 /** Removes all listeners with the same event listener type */
 void removeEventListeners(EventListener::Type listenerType);

 


前者只是删除某一个事件监听器,而后者是删除某一类事件监听器(使用了 clone 克隆)







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值