1主要思路
用Image类的initWithImageFile()方法去初始化Image对象,在一开始时创建一次,用一个bool数组保存每个像素点是否透明度为0的信息。每次触发点击事件时,就根据这个数组的值来判断点击是否有效。IrregularButton类继承自Button类。
2详细设计
2.1成员变量
CC_SYNTHESIZE(int,m_iBtnID,iBtnID); int normalImageWidth_; int normalImageHeight_; bool* normalTransparent_;
m_iBtnID:按钮ID。normalImageWidth_:图片的宽度。normalImageHeight_:图片的高度。normalTransparent_:实际上是一个bool型的数组,用于存放每个像素点是否透明度为0,为0值为true。
2.2Create函数
IrregularButton的静态创建函数create,覆盖父类create函数。
IrregularButton* IrregularButton::create(const std::string& normalImage,const std::string& selectedImage,const std::string& disableImage,TextureResType texType)
{
IrregularButton* btn = new IrregularButton;
if (btn && btn->init(normalImage,selectedImage,disableImage,texType)) {
btn->autorelease();
return btn;
}
CC_SAFE_DELETE(btn);
return nullptr;
}
2.3init函数
bool IrregularButton::init(const std::string &normalImage,TextureResType texType)
{
bool ret = true;
do {
if (!Button::init(normalImage,texType)) {
ret = false;
break;
}
} while (0);
loadnormalTransparentInfo(normalImage);
auto listener1 = EventListenerTouchOneByOne::create(); listener1->setSwallowtouches(true);
listener1->onTouchBegan = [=](Touch* touch,Event* event){
Point locationInNode = convertToWorldspace(touch->getLocation());
hitTest(locationInNode);
return true;
};
listener1->onTouchMoved = [=](Touch* touch,Event* event){
};
listener1->onTouchEnded = [=](Touch* touch,Event* event){
};
_eventdispatcher->addEventListenerWithSceneGraPHPriority(listener1,this);
return ret;
}
这里做以下几件事:1.先调用父类init函数,使得的_buttonnormalRenderer确定,即确定按钮的大小;2.调用loadnormalTransparentInfo函数,确定normalTransparent_的数组值,即得到了精灵的每一点的是否透明。3.绑定触摸响应,并进行判断点击的那一点是否点击在精灵的透明区域。
2.4loadnormalTransparentInfo函数
IrregularButton的loadnormalTransparentInfo函数,保存normalTransparent_的数组值。
void IrregularButton::loadnormalTransparentInfo(std::string sName)
{
Image* normalImage = new Image();
normalImage->initWithImageFile(sName);
normalImageWidth_ = normalImage->getWidth();
normalImageHeight_ = normalImage->getHeight();
this->setContentSize(CCSizeMake(normalImageWidth_,normalImageHeight_));
auto dataLen = normalImage->getDataLen();
if (normalTransparent_ != nullptr) {
delete[] normalTransparent_;
}
auto normalPixels = normalImage->getData();
normalTransparent_ = new bool[dataLen / (sizeof(unsigned char)* 4)];
for (auto i = 0; i < normalImageHeight_; i++) {
for (auto j = 0; j < normalImageWidth_; j++) {
normalTransparent_[i * normalImageWidth_ + j] = (normalPixels[(i * normalImageWidth_ + j) * 4] == 0);
}
}
delete normalImage;
}
initWithImageFile得到精灵的RGBA信息。扫描精灵的每一点,当前点透明时为true放入normalTransparent中,否则为false放入normalTransparent中。
2.5hitTest函数
IrregularButton的hitTest函数,用来确定点击的是否透明区域。
bool IrregularButton::hitTest(const Vec2 &pt)
{
Vec2 localLocation = _buttonnormalRenderer->convertToNodeSpace(pt);
Rect validTouchedRect;
validTouchedRect.size = _buttonnormalRenderer->getContentSize();
if (validTouchedRect.containsPoint(localLocation) && getIsTransparentAtPoint(localLocation) == false)
{
cclOG("IN");
NotificationCenter::getInstance()->postNotification("NotifyIrregularBtn",(Ref*)m_iBtnID);
return true;
}
return false;
}
首先点击坐标转化为世界坐标,再转化为相对精灵坐标,当点击点包含在精灵的范围内并且点击的是非透明区域,说明点中了这个不规则按钮,向外发送通知,通知传递自身的m_iBtnID号。
2.6getIsTransparentAtPoint函数
bool IrregularButton::getIsTransparentAtPoint(cocos2d::Vec2 point)
{
point.y = _buttonnormalRenderer->getContentSize().height - point.y;
int x = (int)point.x - 1;
if (x < 0) {
x = 0;
}
else if (x >= normalImageWidth_) {
x = normalImageWidth_ - 1;
}
int y = (int)point.y - 1;
if (y < 0) {
y = 0;
}
else if (y >= normalImageHeight_) {
y = normalImageHeight_ - 1;
}
return normalTransparent_[normalImageWidth_ * y + x];
}
这里值得注意的是相对精灵坐标的是从精灵的左下角为原点,这里要转化为以左上角为原点的坐标。
2.7调用
const string sNameN = "smile.png"; const string sNameP = "angry.png"; IrregularButton* alphaBtn = IrregularButton::create(sNameN,sNameP,sNameP); alphaBtn->setiBtnID(0); alphaBtn->setPosition(ccp(400,400)); this->addChild(alphaBtn); NotificationCenter::getInstance()->addobserver(this,callfuncO_selector(HelloWorld::onNotifyIrregularBtn),"NotifyIrregularBtn",NULL);
在调用的类中绑定通知,把转换的传入参数转为BtnID即可知道是哪个不规则按钮的通知。
void HelloWorld::onNotifyIrregularBtn(Ref* ref)
{
int iBtnID = (int)ref;
}
实现效果
没点击时:
当点击到笑脸的圆形区域(即非透明区域)时:
点击精灵其他区域(透明区域)是不会响应的。IrregularButton类继承自Button类,这样按钮就可以通过传入三张精灵,实现正常,按压,禁止的按钮三种状态