天天看點

cocos3.x建立不規則按鈕

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.2 Create函數

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.3 init函數

bool IrregularButton::init(const std::string &normalImage,
  const std::string& selectedImage,
  const std::string& disableImage,
  TextureResType texType)
{
  bool ret = true;
  do {
    if (!Button::init(normalImage, selectedImage, disableImage, 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.4 loadNormalTransparentInfo函數

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.5 hitTest函數

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.6 getIsTransparentAtPoint函數

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

實作效果

沒點選時:

cocos3.x建立不規則按鈕

當點選到笑臉的圓形區域(即非透明區域)時: