zoukankan      html  css  js  c++  java
  • Cocos2d-x 3.0开发(三)点击交互的四种处理 .(转)

    1、概述

     
        游戏也好,程序也好,只有能与用户交互才有意义。手机上的交互大致可以分为两部分:点击和输入。其中点击更为重要,几乎是游戏中全部的交互。在Cocos2d-x 3.0中,更改了dispatch机制。同时加入了两种新的交互形式:listener 和touchEvent回调。加上先前版本中的点击函数回调,与重写layer层的touch消息响应,构成了一个相对完整的交互模式。先上一张Demo的图:
     


     

    2、四种点击

     

    1、函数回调

    函数回调是最简单的响应形式,一直以来被用于MenuItem中的点击处理。在新版本中,此处发生了些小改变。我们可以看到在生成的程序中相关代码是这样的:
     
    1. // a selector callback   
    2. void menuCloseCallback(Object* pSender);  
    3.   
    4. auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",  
    5.                         CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));  
    6.   
    7. void HelloWorld::menuCloseCallback(Object* pSender)  
    8. {  
    9.     Director::getInstance()->end();  
    10.   
    11. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)   
    12.     exit(0);  
    13. #endif   
    14. }  
    // a selector callback
    void menuCloseCallback(Object* pSender);
    
    auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",
                            CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    void HelloWorld::menuCloseCallback(Object* pSender)
    {
        Director::getInstance()->end();
    
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
    #endif
    }
    
     

        其中CC_CALLBACK_1宏是将函数与对象绑定在一起,1表示这个函数有一个参数。当点击这个按钮时,会调用这个回调函数。

        除了基于c++11的这个形式的改变,使用方法与先前相同。

     

       2、Layer的touch消息响应

        虽然重写了底层的dispatch,但对这层的使用影响并不大。我们同样需要重写:

    1. //单点响应   
    2. virtual bool onTouchBegan(Touch* touch, Event  *event) override;  
    3. virtual void onTouchMoved(Touch* touch, Event  *event) override;  
    4. virtual void onTouchEnded(Touch* touch, Event  *event) override;  
    5. virtual void onTouchCancelled(Touch *touch, Event *event) override;  
    6.   
    7. //多点响应   
    8. virtual bool onTouchesBegan(Touch* touch, Event  *event) override;  
    9. virtual void onTouchesMoved(Touch* touch, Event  *event) override;  
    10. virtual void onTouchesEnded(Touch* touch, Event  *event) override;  
    11. virtual void onTouchesCancelled(Touch *touch, Event *event) override;  
    //单点响应
    virtual bool onTouchBegan(Touch* touch, Event  *event) override;
    virtual void onTouchMoved(Touch* touch, Event  *event) override;
    virtual void onTouchEnded(Touch* touch, Event  *event) override;
    virtual void onTouchCancelled(Touch *touch, Event *event) override;
    
    //多点响应
    virtual bool onTouchesBegan(Touch* touch, Event  *event) override;
    virtual void onTouchesMoved(Touch* touch, Event  *event) override;
    virtual void onTouchesEnded(Touch* touch, Event  *event) override;
    virtual void onTouchesCancelled(Touch *touch, Event *event) override;

        重写这些函数来对layer的点击做处理。当然,我们需要:

    1. setTouchEnabled(true)。  
    setTouchEnabled(true)。

       此外有个小改动。对于单点触控响应,可以调用:

      

    1. //设置为单点响应   
    2. setTouchMode(Touch::DispatchMode::ONE_BY_ONE);  
    3. //设置为多点响应(默认)   
    4. setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);  
    //设置为单点响应
    setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
    //设置为多点响应(默认)
    setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);
    
    

        进行设置,而不需要再用设置Delegate的方式来做了。

        3、TouchEvent响应

        这是新加入的响应方式。它主要是使用在UIWidget上的。可以将其看做是函数回调的一个扩展,为更多的响应处理提供可能。使用方法大致是:
     
    1. //声明   
    2. void touchButton(Object* object,TouchEventType type);  
    3.   
    4. //挂接到控件上   
    5. uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton));  
    6.   
    7. //实现   
    8. void HelloWorld::touchButton(Object* object,TouchEventType type)  
    9. {  
    10.     LabelTTF* label;  
    11.     switch (type)  
    12.     {  
    13.     case TouchEventType::TOUCH_EVENT_BEGAN:  
    14.         label = static_cast<LabelTTF*>(getChildByTag(11));  
    15.         label->setString("按下按钮");  
    16.         break;  
    17.     case TouchEventType::TOUCH_EVENT_MOVED:  
    18.         label = static_cast<LabelTTF*>(getChildByTag(11));  
    19.         label->setString("按下按钮移动");  
    20.         break;  
    21.     case TouchEventType::TOUCH_EVENT_ENDED:  
    22.         label = static_cast<LabelTTF*>(getChildByTag(11));  
    23.         label->setString("放开按钮");  
    24.         break;  
    25.     case TouchEventType::TOUCH_EVENT_CANCELED:  
    26.         label = static_cast<LabelTTF*>(getChildByTag(11));  
    27.         label->setString("取消点击");  
    28.         break;  
    29.     default:  
    30.         break;  
    31.     }  
    32. }  
    //声明
    void touchButton(Object* object,TouchEventType type);
    
    //挂接到控件上
    uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton));
    
    //实现
    void HelloWorld::touchButton(Object* object,TouchEventType type)
    {
    	LabelTTF* label;
    	switch (type)
    	{
    	case TouchEventType::TOUCH_EVENT_BEGAN:
    		label = static_cast<LabelTTF*>(getChildByTag(11));
    		label->setString("按下按钮");
    		break;
    	case TouchEventType::TOUCH_EVENT_MOVED:
    		label = static_cast<LabelTTF*>(getChildByTag(11));
    		label->setString("按下按钮移动");
    		break;
    	case TouchEventType::TOUCH_EVENT_ENDED:
    		label = static_cast<LabelTTF*>(getChildByTag(11));
    		label->setString("放开按钮");
    		break;
    	case TouchEventType::TOUCH_EVENT_CANCELED:
    		label = static_cast<LabelTTF*>(getChildByTag(11));
    		label->setString("取消点击");
    		break;
    	default:
    		break;
    	}
    }
    

        因为所有的UIWidget都要添加到UILayer上,而UILayer通常都会在最上层,所以可以“基本上”认为这种使用方式会优先于其他方式处理点击消息。因为UILayer也会有层级的改变,比如它和MenuItem之间的关系。所以说“基本上”。

    4、Listener消息响应方式

        这种实现也是新加入的。它更像是点击的一个层次过滤器。点击时,在listener队里中进行过滤。每一个listener检查自己保存的touch消息响应是否会被触发。一层一层过滤,最后在到Layer的touch消息响应。

        我觉得它的设计的初衷是为任意sprite提供一套自己制定的点击响应。但这样的实现仍然要写很多条件判断,没有能够控件化。可能我的理解有些偏差,欢迎讨论。

        具体的用法大致是这样:

     

    1. auto dispatcher = EventDispatcher::getInstance();  
    2.     auto myListener = EventListenerTouch::create(Touch::DispatchMode::ONE_BY_ONE);  
    3.       
    4. //如果不加入此句消息依旧会向下传递   
    5. myListener->setSwallowTouches(true);  
    6.   
    7. myListener->onTouchBegan = [=](Touch* touch,Event* event)  
    8. {  
    9.     //some check   
    10.     if (pass)  
    11.     {  
    12.         return true;  
    13.     }  
    14.     return false;  
    15. };  
    16.   
    17. myListener->onTouchMoved = [=](Touch* touch,Event* event)  
    18. {  
    19.     //do something   
    20. };  
    21.   
    22. myListener->onTouchEnded = [=](Touch* touch,Event* event)  
    23. {  
    24.     //do something   
    25. };  
    26.   
    27. dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1);  
    28. dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);  
    auto dispatcher = EventDispatcher::getInstance();
    	auto myListener = EventListenerTouch::create(Touch::DispatchMode::ONE_BY_ONE);
    	
    //如果不加入此句消息依旧会向下传递
    myListener->setSwallowTouches(true);
    
    myListener->onTouchBegan = [=](Touch* touch,Event* event)
    {
        //some check
        if (pass)
        {
            return true;
        }
        return false;
    };
    
    myListener->onTouchMoved = [=](Touch* touch,Event* event)
    {
    	//do something
    };
    
    myListener->onTouchEnded = [=](Touch* touch,Event* event)
    {
    	//do something
    };
    
    dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1);
    dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);
    
    

        其原理是在dispatcher中检查listener列表,例如myListener或加进来的其他listener。然后每个listener检查自己中的Item看能否达到检查条件,例如:mySprite1,mySprite2。然后执行相应的操作。但这样的话,当控件很多的时候,每一次事件都进行这种双链表的检查操作不知会不会影响些性能?


    3、实例

        光说不练假把式,于是就动手写了一个上面的Demo:

        点击背景区域可以移动整个场景,点击蓝色小方块可以半透明移动它,点击蓝色按钮可以更改文字,显示状态。点击右下角按钮退出程序。


    4、总结

        根据不同的交互需要,选择不同的实现方式,会更有利于我们的维护和扩展,相应例子可以在下面下载。

     

        Demo下载:http://download.csdn.net/detail/fansongy/6399291 不要资源分,觉得好劳烦点下 “顶” ~

    本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途http://blog.csdn.net/fansongy/article/details/12716671 

  • 相关阅读:
    二维数组最大子数组算法
    寻找最大子数组
    最大值bug 调试
    多路电梯调度算法
    分析一个文本文件各个词出现的频率,并把频率最高的十个词打印出来。
    使用redis实现生产者消费者模式
    简单使用redis实现sso单点登录
    MongoDB批量导入及简单的性能优化
    mysql安装
    字符串操作性能优化
  • 原文地址:https://www.cnblogs.com/wodehao0808/p/3465427.html
Copyright © 2011-2022 走看看