我们工作室需要用cocos2dx来写项目,不过之前的分工有些不是很明确,每个人都使用cocos2dx中的点击事件封装了好几个按钮。所以造成了,游戏的基础框架中出现了N种风格的按钮,于是问题就来了:由于我们使用的cocos2dx中的触摸事件是单点,也就是说非手势的触摸事件。不过cocos2dx在ios和安卓平台上对于单点的响应有些不一致。ios的单点触摸事件的响应机制是,如果屏幕上两个地方同时响应触摸事件,如果一个地方先响应触摸事件,另一个地方的触摸事件就不会再向下分发下去,这样就避免了两个事件都会响应。而安卓中的触摸点击事件分发就显得比较蛋疼了,是一个接一个的分发,最终造成的结果就是如果你用手同时点击两个按钮(尽管同时这种事情在cpu处理看来基本是不可能的),会把这两次触摸事件依次分发下去,这样,如果两次点击事件触发加载两个不同的场景的情况下,就显得比较混乱,所以我们需要做到在两个按钮同时点击的时候肯定要屏蔽到后点击到的按钮的点击事件。
经过一些思索,决定统一写一个基类来搞定这个问题。
#include <stdio.h>
#include "cocos2d.h"
#include "WWCompatibleClasses.h"
class WWButton:public cocos2d::Node
{
public:
WWButton();
~WWButton();
virtual bool init();
virtual bool init(wawa::WWNode* pTouchNode);
virtual void onEnter();
virtual void onExit();
virtual bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event);
virtual void onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *unused_event);
virtual void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event);
virtual void onTouchCancelled(cocos2d::Touch *touch, cocos2d::Event *unused_event);
virtual void touchActive(cocos2d::Touch* touch){};
void setEnabled(bool en);
void setTouchSwallow(bool enable);
protected:
CC_SYNTHESIZE(wawa::WWNode*, m_pTouchNode, TouchNode);
CC_SYNTHESIZE(bool, m_hadCancel, HadCancel);
private:
bool containTouch(cocos2d::Touch* touch);
private:
bool m_bEnable;
cocos2d::Vec2 m_beginPoint;
bool m_swallow;
//cocos2d::Vec2 m_movePoint;
};
#endif /* defined(__helloworld__WWButton__) */
此类继承了node,所以肯定可以使用最基本的cocos2d的一些UI功能,cpp文件中实现了触摸事件的分发,而这个分发中,我们用一个变量来控制所有触摸事件的接收。也就是当一个按钮被点击的时候,设置一个状态,这个状态属于全局的一个变量,把此状态置为false,然后其他按钮在执行到touch事件的时候会先经过这个状态判断,当状态为false的时候,就touchbegan就会返回false,这个touch事件就无法执行到
onTouchEnded方法,所以这个按钮就不会响应具体的点击事件,因为具体的点击事件都写在
onTouchEnded方法里边.具体实现代码如下:
#include "WWButton.h"
#include "ButtonManager.h"
#include "../WWMacros.h"
USING_NS_CC;
USING_NS_WW;
namespace ButtonArgument
{
const static float g_moveMax = 10;
}
WWButton::WWButton():
m_pTouchNode(nullptr),
m_bEnable(true),
m_swallow(true),
m_hadCancel(false)
{
}
WWButton::~WWButton()
{
}
bool WWButton::init()
{
if (!Node::init())
{
return false;
}
return true;
}
bool WWButton::init(WWNode *pTouchNode)
{
if (!Node::init())
{
return false;
}
this->setCascadeColorEnabled(true);
this->setTouchNode(pTouchNode);
return true;
}
void WWButton::onEnter()
{
Node::onEnter();
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(m_swallow);
listener->onTouchBegan = CC_CALLBACK_2(WWButton::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(WWButton::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(WWButton::onTouchEnded, this);
listener->onTouchCancelled = CC_CALLBACK_2(WWButton::onTouchCancelled, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
}
void WWButton::onExit()
{
Node::onExit();
}
bool WWButton::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
if (!m_bEnable)
{
return false;
}
if (!ButtonManager::getInstance()->getIsClickEnable())
{
return false;
}
if (!containTouch(touch))
{
return false;
}
ButtonManager::getInstance()->setIsClickEnable(false);
this->setColor(Color3B::GRAY);
m_beginPoint = touch->getLocation();
return true;
}
void WWButton::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
if (!m_swallow)
{
auto movePoint = touch->getLocation();
if (abs(movePoint.x-m_beginPoint.x)>ButtonArgument::g_moveMax||abs(movePoint.y-m_beginPoint.y)>ButtonArgument::g_moveMax)
{
onTouchCancelled(touch, unused_event);
}
}
}
void WWButton::onTouchCancelled(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
this->setColor(Color3B::WHITE);
ButtonManager::getInstance()->setIsClickEnable(true);
m_hadCancel = true;
}
void WWButton::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
this->setColor(Color3B::WHITE);
ButtonManager::getInstance()->setIsClickEnable(true);
CCLOG("WWButton::onTouchEnded -------------");
if (!m_hadCancel)
{
touchActive(touch);
}
m_hadCancel = false;
}
bool WWButton::containTouch(Touch* touch)
{
if (m_pTouchNode == nullptr)
{
CCLOG("ErrorInfo::WWButton::containTouch touchNode is nullptr");
return false;
}
WWPoint touchLocation = touch->getLocation();
WWPoint local = m_pTouchNode->convertToNodeSpace(touchLocation);
WWRect r = m_pTouchNode->getBoundingBox();
r.origin = Vec2::ZERO;
return r.containsPoint(local);
}
void WWButton::setEnabled(bool en)
{
m_bEnable = en;
if (m_bEnable)
{
this->setColor(Color3B::WHITE);
}
else
{
this->setColor(Color3B::GRAY);
}
}
void WWButton::setTouchSwallow(bool enable)
{
m_swallow = enable;
Director::getInstance()->getEventDispatcher()->removeEventListenersForTarget(this);
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(enable);
listener->onTouchBegan = CC_CALLBACK_2(WWButton::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(WWButton::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(WWButton::onTouchEnded, this);
listener->onTouchCancelled = CC_CALLBACK_2(WWButton::onTouchCancelled, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
}
其中ButtonManager是个单例类,存储了这个按钮的状态,也许有人会有疑问,简单的写一个static 变量不就可以了嘛,干嘛还要用单例仅仅为了存储一个变量,额,这就是一个历史问题了,当初我的单例里边是这样的
#ifndef __helloworld__ButtonManager__
#define __helloworld__ButtonManager__
#include <stdio.h>
#include "cocos2d.h"
#include "WWButton.h"
const static std::string g_btnCreateNotafication = "buttonCreate";
const static std::string g_buttonClickNotafication = "buttonClick";
const static std::string g_buttonReset = "buttonReset";
const static std::string g_buttonRemove = "button_Remove";
const static std::string g_sceneChange = "scene_change";
class ButtonManager;
static ButtonManager* m_instance = nullptr;
class ButtonManager
{
public:
ButtonManager();
~ButtonManager();
static ButtonManager* getInstance()
{
if (!m_instance)
{
m_instance = new ButtonManager();
}
return m_instance;
}
static void release()
{
if (m_instance)
{
delete m_instance;
m_instance = nullptr;
}
}
void beginListen();
private:
CC_SYNTHESIZE(bool, m_bIsClickEnable, IsClickEnable);
private:
std::vector<WWButton*> m_allButtons;
};
#endif /* defined(__helloworld__ButtonManager__) */
#include "ButtonManager.h"
USING_NS_CC;
#include "WWTouchSprite.h"
#include "WWTwoPicBtn.h"
ButtonManager::ButtonManager():
m_bIsClickEnable(true)
{
}
ButtonManager::~ButtonManager()
{
}
void ButtonManager::beginListen()
{
// /*******监听按钮的创建********************************************/
// Director::getInstance()->getEventDispatcher()->addCustomEventListener(g_btnCreateNotafication, [&](EventCustom* event)
//{
// auto btn = (WWButton*)event->getUserData();
// if (btn)
// {
// btn->setBtnIndex((int)m_allButtons.size());
// m_allButtons.push_back(btn);
// CCLOG("the m_allButton size=%ld",m_allButtons.size());
// CCLOG("the beginListen() btn idx=%ld",btn->getBtnIndex());
// }
//});
//
//
//
// /*******监听按钮点击事件(touchbegan)********************************************/
// Director::getInstance()->getEventDispatcher()->addCustomEventListener(g_buttonClickNotafication, [&](EventCustom* event)
//{
// auto btn = (WWButton*)event->getUserData();
// CCLOG("the btn indx =%ld",btn->getBtnIndex());
// if (btn)
// {
// for (auto obj:m_allButtons)
// {
// CCLOG("the every obj inx=%ld",obj->getBtnIndex());
// if (btn->getBtnIndex()!=obj->getBtnIndex())
// {
// CCLOG("obj index%ldd",obj->getBtnIndex());
// obj->setTouchEnable(false);
// }
// }
// }
//});
//
//
//
// /*******所有按钮置回可点状态********************************************/
// Director::getInstance()->getEventDispatcher()->addCustomEventListener(g_buttonReset,[&](EventCustom* event)
// {
// for (auto obj:m_allButtons)
// {
// obj->setTouchEnable(true);
// }
// });
//
//
//
// /*******按钮移除通知监听********************************************/
// Director::getInstance()->getEventDispatcher()->addCustomEventListener(g_buttonRemove,[&](EventCustom* event)
//{
// auto btn = (WWButton*)event->getUserData();
// if (btn)
// {
// std::vector<WWButton*>::iterator i = m_allButtons.begin();
// while (i!=m_allButtons.end())
// {
// WWButton* obj = *i;
// if (obj->getBtnIndex()==btn->getBtnIndex())
// {
// m_allButtons.erase(i);
// break;
// }
// i++;
// }
// }
//});
}
也就是说,一开始我把所有的按钮单独存储了一个状态,每次按钮点击的时候就会发送通知,将所有的按钮状态均置为fasle,不过后来幡然悔悟,用个全局变量就解决所有问题了,然后开始敲脑袋觉得智商捉急啊,所以把一些多此一举的代码贴上,以表耻辱。(不过还是觉得自己之前的想法也是有那么一点用处的,万一之后扩展,需要每次改变一些按钮的状态,这个按钮管理还是用得着的嘛。诸位莫要拍砖)
所以综上所述。cocos2dx有一点封装的还是不够好,就是在按钮点击方面,需要创建之后加入一个Menu里边,而如果一个界面出现多个层次的按钮,就需要多个Menu,而多个Menu之间再去做互斥又是多么蛋疼的事情啊,所以还是封装一个按钮来做互斥比较好一些。(今天喝了点白酒+龙舌兰+伏特加)半醉半朦胧,若有语焉不详之处,或者愚蠢的地方,欢迎留言拍砖。