Cocos2dx触摸管理

用cocos2dx好长时间了,刚接触的时候觉得触摸好烦,总要要单独处理某些触摸的权限.所以自己试着封装了一个触摸管理的机制吧,现在整理一下吧.

简单介绍:

1.WPLayer 继承于 cocos2d::Layer 记录的所有Menu,ScrollView的触摸开关.
2.WPScene 继承于 cocos2d::Scene 将WPLayer以栈的形式展现在WPScene上. 每当push新的WPLayer时,关闭WPScene中的top WPLayer所有触摸. 每当pop WPLayer时,先关闭要被pop的WPLayer的触摸,pop之后开启WPScene中的Top WPLayer的触摸.
3.WPMenu 继承于 cocos2d::Menu 创建时可以选择Menu的触摸等级.方便在ScrollView中点击,并不影响ScrollView的滑动.
4.下面贴下代码吧 🙂
5.I’m so sorry for my bad English.

#include <iostream> 
#include "cocos2d.h"
#include "extensions/cocos-ext.h"
#include "WPLayer.h"

USING_NS_CC;
USING_NS_CC_EXT;

/*
 *
 * all scene should public WPScene
 * in order to switch WPLayer
 * manager the touch
 *
 */

class WPScene : public Scene
{
    
public:
    ~WPScene();
    virtual bool init();

    CREATE_FUNC(WPScene);
    //
    // push WPLayer
    //
    void pushLayer(WPLayer * layer);
    //
    // pop WPLayer
    //
    void popLayer();
    void popLayer(WPLayer * layer);
    //
    // replace top WPLayer
    //
    void replaceLayer(WPLayer * layer);
    //
    // pop all layer and push new wplayer
    //
    void resetRootLayer(WPLayer * layer);
    //
    // get top WPLayer 
    //
    WPLayer * getTopLayer();

protected:
    
private:
    __Array *_layerArray;
};


/*************************   WPScene.cpp   *************************/
#include "WPScene.h"

WPScene::~WPScene()
{
    CC_SAFE_RELEASE(_layerArray);
}

bool WPScene::init()
{
    if (!CCScene::init()) {
        return false;
    }
    _layerArray = __Array::create();
    _layerArray -> retain();
    return true;
}


void WPScene::pushLayer(WPLayer * layer)
{
    size_t zOrder = _layerArray -> count();
    if (_layerArray -> count()) {
        WPLayer * lastLayer = (WPLayer *)_layerArray -> getLastObject();
        lastLayer -> closeAllTouch();
        zOrder = lastLayer -> getLocalZOrder() + 1;
    }
    _layerArray -> addObject(layer);
    layer -> openAllTouch();
    this -> addChild(layer, (int)zOrder, (int)zOrder);
}

void WPScene::popLayer()
{
    WPLayer * layer = (WPLayer *)_layerArray -> getLastObject();
    if (layer -> getParent() == this) {
        layer -> closeAllTouch();
        this -> removeChild(layer, true);
    }
    _layerArray -> removeLastObject();
    if (_layerArray -> count()) {
        WPLayer * lastLayer = (WPLayer *)_layerArray -> getLastObject();
        lastLayer -> openAllTouch();
    }
}

void WPScene::popLayer(WPLayer * layer)
{
    if (_layerArray -> containsObject(layer)) {
        if (layer -> getParent() == this) {
            layer -> closeAllTouch();
            this -> removeChild(layer, true);
        }
        _layerArray -> removeObject(layer);
    }
    if (_layerArray -> count()) {
        WPLayer * lastLayer = (WPLayer *)_layerArray -> getLastObject();
        lastLayer -> openAllTouch();
    }
}

void WPScene::replaceLayer(WPLayer * layer)
{
    WPLayer * last = (WPLayer *)_layerArray -> getLastObject();
    if (last) {
        last -> retain();
    }
    this -> popLayer();
    this -> pushLayer(layer);
    if (last) {
        last -> release();
    }
}

void WPScene::resetRootLayer(WPLayer * layer)
{
    while (_layerArray -> count()) {
        WPLayer * layer = (WPLayer *)_layerArray -> getLastObject();
        _layerArray -> removeLastObject();
        this -> removeChild(layer, true);
    }
    pushLayer(layer);
}

WPLayer * WPScene::getTopLayer()
{
    if (_layerArray -> count()) {
        return (WPLayer *)_layerArray -> getLastObject();
    }
    return NULL;
}


/*
 *
 * all Layer need public WPLayer in order to manager touch
 *
 *
 */

class WPLayer : public Layer {
    
public:
    CREATE_FUNC(WPLayer);
    virtual bool init();
    ~WPLayer();
    WPLayer();
    //
    //  push or pop menu into touch stack
    //
    void pushMenu(Menu *menu);
    void popMenu(Menu *menu);
    //
    // push or pop ScrollView into touch stack
    //
    void pushScrollView(ScrollView * scrollView);
    void popScrollView(ScrollView * scrollView);
    //
    // replace the Running WPScene 's top WPLayer
    //
    virtual void replaceLayer(WPLayer *layer);
    //
    // push the WPLayer
    //
    virtual void pushWPLayer(WPLayer *layer);
    //
    // pop top WPLayer
    //
    virtual void popWPLayer();
    //
    // pop the WPLayer in WPScene stack
    //
    virtual void popWPLayer(WPLayer * layer);
    //
    // close all touch for this layer
    //
    virtual void closeAllTouch();
    //
    // open all touch for this layer
    //
    virtual void openAllTouch();
    //
    // get Top WPLayer
    //
    WPLayer * getTopLayer();
    //
    // pop all WPLayer and push new WPLayer;
    //
    void resetWPLayer(WPLayer *layer);
    //
    // set the Layer touch event is open or not
    //
    void setOpenOnTouch(bool can,int priority = 1);
    void setCantOnTouch();
    //
    // and key event for android or wp8
    //
    void setCanOnKeyboardTouch(bool can);
    
    
protected:
    
    bool _isOpenTouch;
    int _touchPriority;
    
    virtual bool onTouchBegan(Touch* touch, Event* event){return true;};
    virtual void onTouchEnded(Touch* touch, Event* event){};
    virtual void onTouchCancelled(Touch* touch, Event* event){};
    virtual void onTouchMoved(Touch* touch, Event* event){};
    
    virtual void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event){};
    virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event){};
    
    void addWPTouchOneByOne(int priority);
    void removeWPTouchOneByOne();
    
    
    EventListenerTouchOneByOne *_wpTouchListener;
    EventListenerKeyboard      *_wpKeyboardListener;
    __Array *_layerArray;
    __Array *_scrollViewArray;
    
    void setTouchEnable(bool touch);
};

/***********************   WPLayer.cpp   ************************/
#include "WPLayer.h"
#include "WPScene.h"


WPLayer::~WPLayer()
{
    CC_SAFE_RELEASE(_layerArray);
    CC_SAFE_RELEASE(_scrollViewArray);
    removeWPTouchOneByOne();
    setCanOnKeyboardTouch(false);
}

WPLayer::WPLayer()
:_wpTouchListener(nullptr)
,_layerArray(nullptr)
,_scrollViewArray(nullptr)
,_wpKeyboardListener(nullptr)
,_isOpenTouch(false)
{
    
}

bool WPLayer::init()
{
    if (!Layer::init()) {
        return false;
    }
    _layerArray = __Array::create();
    _layerArray -> retain();
    
    _scrollViewArray = __Array::create();
    _scrollViewArray -> retain();
    
    
    return true;
}


void WPLayer::pushMenu(Menu *menu)
{
    if (!_layerArray -> containsObject(menu)) {
        _layerArray -> addObject(menu);
    }
}
void WPLayer::popMenu(cocos2d::Menu *menu)
{
    if (_layerArray -> containsObject(menu)) {
        _layerArray -> removeObject(menu);
    }
}

void WPLayer::pushScrollView(ScrollView * scrollView)
{
    if (!_scrollViewArray -> containsObject(scrollView)) {
        _scrollViewArray -> addObject(scrollView);
    }
}

void WPLayer::popScrollView(ScrollView * scrollView)
{
    if (_layerArray -> containsObject(scrollView)) {
        _layerArray -> removeObject(scrollView);
    }
}


void WPLayer::pushWPLayer(WPLayer *layer)
{
    WPScene * scene = (WPScene *)this -> getParent();
    if (scene) {
        bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
        CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
        scene -> pushLayer(layer);
    }
}

void WPLayer::popWPLayer()
{
    WPScene * scene = (WPScene *)this -> getParent();
    if (scene) {
        bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
        CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
        scene -> popLayer();
    }
}

void WPLayer::popWPLayer(WPLayer * layer)
{
    WPScene * scene = (WPScene *)this -> getParent();
    if (scene) {
        bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
        CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
        scene -> popLayer(layer);
    }
}

void WPLayer::replaceLayer(WPLayer *layer)
{
    WPScene * scene = (WPScene *)this -> getParent();
    if (scene) {
        bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
        CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
        scene -> replaceLayer(layer);
    }
}

void WPLayer::closeAllTouch()
{
    setTouchEnable(false);
}
void WPLayer::openAllTouch()
{
    setTouchEnable(true);
}

WPLayer * WPLayer::getTopLayer()
{
    WPScene * scene = (WPScene *)getParent();
    if (!scene) {
        return NULL;
    }
    bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
    CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
    return scene -> getTopLayer();
}

void WPLayer::setTouchEnable(bool touch)
{
    for (int i = 0; i < _layerArray -> count(); i++) {
        Menu * menu = (Menu *) _layerArray -> getObjectAtIndex(i);
        menu -> setEnabled(touch);
    }
    for (int i = 0 ; i < _scrollViewArray -> count(); i++) {
        ScrollView * scrollView = (ScrollView *)_scrollViewArray -> getObjectAtIndex(i);
        scrollView -> setTouchEnabled(touch);
    }
    if (touch) {
        if (_isOpenTouch) {
            addWPTouchOneByOne(_touchPriority);
        }
        else {
            removeWPTouchOneByOne();
        }
    }
    else {
        removeWPTouchOneByOne();
    }
}

void WPLayer::resetWPLayer(WPLayer *layer)
{
    WPScene * scene = (WPScene *)getParent();
    if (!scene) {
        CCASSERT(false,"WPLayer 's parent isn't a WPScene");
    }
    bool isWPScene = dynamic_cast<WPScene*>(scene) != nullptr;
    CCASSERT(isWPScene,"WPLayer 's parent isn't a WPScene");
    scene -> resetRootLayer(layer);
}

void WPLayer::setOpenOnTouch(bool can,int priority)
{
    if (can) {
        addWPTouchOneByOne(priority);
        _isOpenTouch = can;
        _touchPriority = priority;
    }
    else {
        removeWPTouchOneByOne();
    }
}
void WPLayer::setCantOnTouch()
{
    _isOpenTouch = false;
    setOpenOnTouch(false);
}

void WPLayer::addWPTouchOneByOne(int priority)
{
    if (!_wpTouchListener) {
        _wpTouchListener = EventListenerTouchOneByOne::create();
        _wpTouchListener->setSwallowTouches(true);
        _wpTouchListener->onTouchBegan = CC_CALLBACK_2(WPLayer::onTouchBegan, this);
        _wpTouchListener->onTouchMoved = CC_CALLBACK_2(WPLayer::onTouchMoved, this);
        _wpTouchListener->onTouchEnded = CC_CALLBACK_2(WPLayer::onTouchEnded, this);
        _wpTouchListener->onTouchCancelled = CC_CALLBACK_2(WPLayer::onTouchCancelled, this);
        _eventDispatcher -> addEventListenerWithFixedPriority(_wpTouchListener, priority);
    }
}

void WPLayer::removeWPTouchOneByOne()
{
    if (_wpTouchListener) {
        _eventDispatcher -> removeEventListener(_wpTouchListener);
        _wpTouchListener = nullptr;
    }
}

void WPLayer::setCanOnKeyboardTouch(bool can)
{
    if (can) {
        if (!_wpKeyboardListener) {
            _wpKeyboardListener = EventListenerKeyboard::create();
            _wpKeyboardListener->onKeyPressed = CC_CALLBACK_2(WPLayer::onKeyPressed, this);
            _wpKeyboardListener->onKeyReleased = CC_CALLBACK_2(WPLayer::onKeyReleased, this);
            
            _eventDispatcher->addEventListenerWithSceneGraphPriority(_wpKeyboardListener, this);
        }
    }
    else if(_wpKeyboardListener){
        _eventDispatcher -> removeEventListener(_wpKeyboardListener);
        _wpKeyboardListener = nullptr;
    }
}

#include <iostream>
#include "cocos2d.h"
using namespace cocos2d;

class WPMenu : public Menu  {
    
public:
    ~WPMenu();
    //
    //   sett the menu scroll type for when touch scroll cancel the item event
    //
    enum MenuScrollType{
        kMenuScrollNone,
        kMenuScrollHorizontal,
        kMenuScrollVertical,
    };
    //
    // create WPMenu  scroll type and touch priority
    //
    static WPMenu * create(MenuScrollType type,int pro = 2);
protected:
    virtual bool init();
    bool initWithArray(const Vector<MenuItem*>& arrayOfItems);
    
    virtual void onEnter();
    virtual void onExit();
    
    virtual bool onTouchBegan(Touch* touch, Event* event);
    virtual void onTouchEnded(Touch* touch, Event* event);
    virtual void onTouchCancelled(Touch* touch, Event* event);
    virtual void onTouchMoved(Touch* touch, Event* event);
    
    Point _startPoint;
    bool _isMoving;
    MenuScrollType _scrollType;
    
    EventListenerTouchOneByOne *_listener;
    
    int _priority;
};

/*************************   WPMenu.cpp   ********************/

#include "WPMenu.h"

#define kDISTANCE_SEQ      40

WPMenu::~WPMenu()
{
    _eventDispatcher -> removeEventListener(_listener);
}


WPMenu * WPMenu::create(MenuScrollType type,int priority)
{
    WPMenu * pRet = new WPMenu;
    pRet -> _priority = priority;
    pRet -> _scrollType = type;
    if (pRet && pRet -> init()) {
        pRet -> autorelease();
    }
    else {
        CC_SAFE_DELETE(pRet);
    }
    return pRet;
}

bool WPMenu::init()
{
    _isMoving = false;
    return initWithArray(Vector<MenuItem*>());
}

bool WPMenu::initWithArray(const Vector<MenuItem*>& arrayOfItems)
{
    if (Layer::init())
    {
        _enabled = true;
        // menu in the center of the screen
        Size s = Director::getInstance()->getWinSize();
        
        this->ignoreAnchorPointForPosition(true);
        
        setAnchorPoint(Point(0.5f, 0.5f));
        this->setContentSize(s);
        
        setPosition(Point(s.width/2, s.height/2));
        
        int z=0;
        
        for (auto& item : arrayOfItems)
        {
            this->addChild(item, z);
            z++;
        }
        
        _selectedItem = nullptr;
        _state = Menu::State::WAITING;
        
        // enable cascade color and opacity on menus
        setCascadeColorEnabled(true);
        setCascadeOpacityEnabled(true);
        
        
        _listener = EventListenerTouchOneByOne::create();
        _listener->setSwallowTouches(true);
        
        _listener->onTouchBegan = CC_CALLBACK_2(Menu::onTouchBegan, this);
        _listener->onTouchMoved = CC_CALLBACK_2(Menu::onTouchMoved, this);
        _listener->onTouchEnded = CC_CALLBACK_2(Menu::onTouchEnded, this);
        _listener->onTouchCancelled = CC_CALLBACK_2(Menu::onTouchCancelled, this);
        _eventDispatcher -> addEventListenerWithFixedPriority(_listener, _priority);

        return true;
    }
    return false;
}

void WPMenu::onEnter()
{
    Menu::onEnter();
}

void WPMenu::onExit()
{
    Menu::onExit();
}

bool WPMenu::onTouchBegan(Touch* touch, Event* event)
{
    bool ans = Menu::onTouchBegan(touch, event);
    if (ans) {
        _startPoint = touch->getLocationInView();
    }
    _isMoving = false;
    return ans;
}

void WPMenu::onTouchEnded(Touch* touch, Event* event)
{
    this -> retain();
    if (!_isMoving) {
        Menu::onTouchEnded(touch,event);
    }
    else {
        if (_selectedItem)
        {
            _selectedItem->unselected();
        }
        _state = Menu::State::WAITING;
    }
    _isMoving = false;
    this -> release();
}

void WPMenu::onTouchCancelled(Touch* touch, Event* event)
{
    Menu::onTouchCancelled(touch,event);
}

void WPMenu::onTouchMoved(Touch* touch, Event* event)
{
    if (!_isMoving) {
        float dis = 0;
        Point endPoint = touch -> getLocationInView();
        if (_scrollType == kMenuScrollNone) {
            dis = (endPoint.getDistanceSq(_startPoint));
        }
        else if (_scrollType == kMenuScrollHorizontal){
            dis = fabs(endPoint.x - _startPoint.x);
        }
        else if (_scrollType == kMenuScrollVertical){
            dis = fabs(endPoint.y - _startPoint.y);

        }
        
        if (dis > kDISTANCE_SEQ) {
            _isMoving = true;
            if (_selectedItem)
            {
                _selectedItem->unselected();
            }
        }
        else {
            Menu::onTouchMoved(touch,event);
        }
    }
}