cocos2dx 3.2 触摸

例子:   
 设置 menu优先级

void GameUtil::pack_setPriority2(Layer *layer, EventListenerTouchOneByOne *touchListener,int priority ,bool swallow)

{

     auto dispatcher = Director::getInstance()->getEventDispatcher();

       dispatcher->removeEventListenersForTarget(layer);

     touchListener->onTouchBegan = CC_CALLBACK_2(Layer::onTouchBegan,layer);

     touchListener->onTouchMoved = CC_CALLBACK_2(Layer::onTouchMoved,layer);

     touchListener->onTouchEnded = CC_CALLBACK_2(Layer::onTouchEnded,layer);

     touchListener->setSwallowTouches(swallow);

    //设置不想向下传递触摸  true是不想 默认为false

     dispatcher->addEventListenerWithFixedPriority(touchListener,priority);

    //下面的监听和layer绑定 layer释放连带监听 不需要手动   但是下面方法无法设置优先级

    //    dispatcher->addEventListenerWithSceneGraphPriority(touchListener,layer);

    // Sets listener's priority with fixed value. 这个函数只能设置fix priority  不能设置GraphPriority 所以不能用上一个方法

//    dispatcher->setPriority(touchListener,priority);

}


使用:

static EventListenerTouchOneByOne* listener; //可控的监听

//!触摸生效

void classXXX::setMyTouchEnable()

{

 listener = EventListenerTouchOneByOne::create();

 GameUtil::pack_setPriority2(this, listener,1 ,true);

}


//!触摸失效

void classXXX::setMyTouchDisable()

{  

if(listener)

     _eventDispatcher->removeEventListener(_touchListener);

}



前言

Cocos2d-X 3.0 引入了一个新的机制来响应用户事件。这篇文档将解释新的机制是如何工作的。

基础概念:

  • Event listeners 包含了你的事件处理代码
  • Event dispatcher 用于通知用户事件监听器
  • Event objects 包含事件的信息

为了响应事件, 你必须创建一个 EventListener 。一共有5种不同的 EventListener :

  • EventListenerTouch 响应触摸事件
  • EventListenerKeyboard 响应键盘事件
  • EventListenerAcceleration 响应加速计事件
  • EventListenMouse 响应鼠标事件
  • EventListenerCustom 响应自定义的事件

然后, 把你的事件处理代码添加到 Event listeners 中的合适的回调函数中 (例如, onTouchBegan 对应 EventListenerTouch监听器, onKeyPressed 对应 键盘事件监听器。

下一步,用 EventDispatcher 注册你的 EventListener 。

当事件发生时 (例如用户触摸了屏幕,按下了键盘的某个键), EventDispatcher 将分发 Event objects (例如EventTouchEventKeyboard) 给合适的 EventListeners 然后调用你的回调函数。 每一个事件对象都包含事件的相关信息 (例如事件发生的坐标)。

例子

在下面的例子中, 我们将在Scene中添加3个部分重叠的按钮。 每一个按钮都响应触摸按钮。

使用图片创建3个Sprite作为按钮

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

touchable_sprite

创建一个单点触摸事件监听器并且添加回调代码

(需要注意的是,在下面的例子中, 我们使用 C++11 lambda 表达式 实现回调。可以看到,在下面的键盘事件例子中,使用了不同的方式来书写,使用了 CC_CALLBACK_N 宏。

    //创建一个单点(one by one)触摸事件监听器(一次处理一个触摸)
    auto listener1 = EventListenerTouchOneByOne::create();
    // 当吞噬触摸(swallow touches)被设置为 true ,然后如果在 onTouchBegan 方法中返回 true ,那么触摸事件将会被吞噬,防止其他的监听器使用。
    listener1->setSwallowTouches(true);

    // 通过 lambda 表达式实现 onTouchBegan 回调方法的例子
    listener1->onTouchBegan = [](Touch* touch, Event* event){
        // event->getCurrentTarget() 返回监听器的 sceneGraphPriority node 。
        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());
        //Move the position of current button sprite
        //Move the position of current button sprite
        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);

_eventDispatcher 是一个 Node 的属性. 你可以使用它管理当前节点(Node)的所有事件的分发(这些节点可以是 Scene,Layer,Sprite 等)。

注意: 在上面的例子中, 我们在第二和第三个调用 addEventListenerWithSceneGraphPriority 使用 clone() 方法。这是因为每一个事件监听器只能被添加一次。addEventListenerWithSceneGraphPriority 和addEventListenerWithFixedPriority 方法在事件监听器中设置了一个注册标志,如果注册标志已经被设置了,就不能再次添加了。

还有一点是需要记住的: 如果你给一个节点添加 fixed priority 监听器, 当这个节点被移除了之后,你需要手动移除这个。然而,这种情况对于 scene graph priority 监听器来说,它是和节点捆绑的,当一个节点的析构函数被调用的时候,监听器会自动移除。

新的触摸机制

上面的过程和2.x版本的事件机制相比似乎看起来更加的复杂。在老版本中, 你需要继承一个委托( delegate )类,它定义了 onTouchBegan() 方法等。你的事件处理代码将会进入到这些委托方法。

新的事件机制从 delegate 中移除了事件处理逻辑,将这些逻辑移到了监听器中。但是,上面的逻辑实现了下方的方法: 1. 通过使用一个事件监听器, sprite 可以通过 SceneGraphPriority 被添加到事件分发器。当点击一个 sprite 按钮,相同绘制等级的回调函数将会被调用(换句话说,最表面的 sprite 将最先得到触摸事件)。 2. 处理事件逻辑的时候,当被触摸时根据每种情况采取相应的处理逻辑来显示点击效果(例如识别点击区域,为被点击的元素设置不同的透明度)。 3. 当listener1->setSwallowTouches(true) 被设置的时候,将根据 onTouchBegan 的返回值, 决定触摸事件的显示顺序是否应该被向后传递。

FixedPriority vs SceneGraphPriority

  • FixedPriority 是一个 int 值. 带有更小的 Priority 值事件监听器将先处理事件,更大的 Priority 值的将后处理。
  • SceneGraphPriority 是一个指向 Node 的指针. 拥有更高的 Z order 值的节点的监听器将比低 Z order 值的先接收到事件。(话句话说,Z order 越大就是被绘制在越表面)。这保证了触摸事件可以从前向后传递。

其他事件分发处理模块

除了触摸事件响应,下面的模块也使用了相同事件处理方法。

按键事件(Keyboard events)

除了键盘,它也可以是每一个有菜单的设备,他们可以使用相同的监听器来处理事件。

在上方的触摸事件中,我们使用了 lamdba 表达式设置回调函数。你也可以通过 CC_CALLBACK_N 宏来绑定已经存在的回调函数。如下:

    //初始化并绑定 
    auto listener = EventListenerKeyboard::create();
    listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
    listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    // 按键事件回调方法的实现原型
    void KeyboardTest::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
    {
        log("Key with keycode %d pressed", keyCode);
    }

    void KeyboardTest::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
    {
        log("Key with keycode %d released", keyCode);
    }   

加速计事件(Accelerometer events)

在使用加速计事件之前,你必须先在设备上将他们开启:

Device::setAccelerometerEnabled(true);

然后创建相应的监听器。

    auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(AccelerometerTest::onAcceleration, this));
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    // 加速计事件回调方法的实现原型
    void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
    {
        //  逻辑处理代码 
    }

鼠标事件(Mouse events)

鼠标点击事件的分发已经被添加到3.0版本了。它可以跨平台,并且丰富了游戏的用户体验。

如同所有的事件类型,首先我们需要创建事件监听器:

    _mouseListener = EventListenerMouse::create();
    _mouseListener->onMouseMove = CC_CALLBACK_1(MouseTest::onMouseMove, this);
    _mouseListener->onMouseUp = CC_CALLBACK_1(MouseTest::onMouseUp, this);
    _mouseListener->onMouseDown = CC_CALLBACK_1(MouseTest::onMouseDown, this);
    _mouseListener->onMouseScroll = CC_CALLBACK_1(MouseTest::onMouseScroll, this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);

然后一个接一个地实现每一个回调方法并绑定到监听器:

void MouseTest::onMouseDown(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Down detected, Key: ";
    str += tostr(e->getMouseButton());
    // ...
}

void MouseTest::onMouseUp(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Up detected, Key: ";
    str += tostr(e->getMouseButton());
    // ...
}

void MouseTest::onMouseMove(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "MousePosition X:";
    str = str + tostr(e->getCursorX()) + " Y:" + tostr(e->getCursorY());
    // ...
}

void MouseTest::onMouseScroll(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Scroll detected, X: ";
    str = str + tostr(e->getScrollX()) + " Y: " + tostr(e->getScrollY());
    // ...
}

自定义事件(Custom Event)

上面的事件类型是系统已经定义了的,而且事件(例如触摸屏幕,按键响应)是由系统自动触发的。另外,你可以制作你自己的 custom events ,它们可以不由系统触发,但是必须通过你的代码,如下:

    _listener = EventListenerCustom::create("game_custom_event1", [=](EventCustom* event){
        std::string str("Custom event 1 received, ");
        char* buf = static_cast<char*>(event->getUserData());
        str += buf;
        str += " times";
        statusLabel->setString(str.c_str());
    });

    _eventDispatcher->addEventListenerWithFixedPriority(_listener, 1);

上面自定义了一个事件监听器,并且带有一个响应方法。它被添加到了事件分发器里。那如何处理事件触发呢,往下看:

    static int count = 0;
    ++count;
    char* buf = new char[10];
    sprintf(buf, "%d", count);
    EventCustom event("game_custom_event1");
    event.setUserData(buf);
    _eventDispatcher->dispatchEvent(&event);
    CC_SAFE_DELETE_ARRAY(buf);

上面的例子创建了一个 EventCustom 对象,并且设置了他的 UserData。之后,它被 _eventDispatcher->dispatchEvent(&event); 手动的分发。它将触发之前定义好的事件处理方法。

移除事件监听器

一个已经添加的监听器可以通过下方的方法移除:

    _eventDispatcher->removeEventListener(listener);

使用下方的代码移除事件分发器的所有监听器:

    _eventDispatcher->removeAllEventListeners();

当使用 removeAll 的时候,这个节点的所有监听器都会被移除。推荐的做法是移除指定的监听器。

注意:在使用 removeAll 之后,菜单将不会响应,因为它也需要接受触摸事件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值