Cocos2d-x 不规则按钮处理

处理不规则按钮

问题描述

在Cocos2d-x实际项目中,经常会遇到不规则按钮的情况,或是因为项目需要,或是美术切图问题.由于按钮的响应区域是根据图片的实际尺寸来的,所以会出现透明区域也能响应的情况,这时候,就需要我们想办法屏蔽掉透明区域的触摸.

解决思路

由于Cocos2d-x本身没有做这方面的处理,我自己通过查阅资料找到了一个解决办法,那就是:复写Button类,在创建按钮的时候获取按钮正常形态的图片,根据其alpha通道值维护一个二维Bool型数组,如果像素点的透明度值为1,则该点为false,点击按钮的时候,根据点击的坐标去判断当前位置是否透明,透明则不予响应.

代码

加载函数loadNormalTransparentInfo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto start = std::chrono::steady_clock::now();
#endif

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto stepStart = std::chrono::steady_clock::now();
#endif
Sprite* normalRenderer = static_cast<Sprite*>(_buttonNormalRenderer);
auto normalTexture = normalRenderer->getTexture();
const Size& s = normalTexture->getContentSizeInPixels();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto stepEnd = std::chrono::steady_clock::now();
auto stepTime = std::chrono::duration_cast<std::chrono::milliseconds>(stepEnd - stepStart);
CCLOG("Sprite::getTexture: %lld ms\n", stepTime.count());
#endif

int savedBufferWidth = (int)s.width;
int savedBufferHeight = (int)s.height;
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
CCLOG("image: %s, savedBuffWidth: %d, savedBuffHeight: %d\n", _normalFileName.c_str(), savedBufferWidth, savedBufferHeight);
#endif

GLubyte *buffer = nullptr;

// the FBO which cocos2dx used is not window-system-provided (non-zero id)
GLint oldFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO);
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
CCLOG("oldFBO: %d\n", oldFBO);
#endif

GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepStart = std::chrono::steady_clock::now();
#endif
glBindTexture(GL_TEXTURE_2D, normalTexture->getName());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, savedBufferWidth, savedBufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, normalTexture->getName(), 0);
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepEnd = std::chrono::steady_clock::now();
stepTime = std::chrono::duration_cast<std::chrono::milliseconds>(stepEnd - stepStart);
CCLOG("bind texture: %lld ms\n", stepTime.count());
#endif

CCASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Could not attach texture to framebuffer");

buffer = new (std::nothrow) GLubyte[savedBufferWidth * savedBufferHeight * 4];

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepStart = std::chrono::steady_clock::now();
#endif
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, savedBufferWidth, savedBufferHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glBindFramebuffer(GL_FRAMEBUFFER, oldFBO);
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepEnd = std::chrono::steady_clock::now();
stepTime = std::chrono::duration_cast<std::chrono::milliseconds>(stepEnd - stepStart);
CCLOG("glReadPixels: %lld ms\n", stepTime.count());
#endif

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepStart = std::chrono::steady_clock::now();
#endif
auto dataLen = savedBufferWidth * savedBufferHeight * 4;
if (normalTransparent_ != nullptr) {
delete[] normalTransparent_;
}
normalImageWidth_ = savedBufferWidth;
normalImageHeight_ = savedBufferHeight;
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] = (buffer[(i * normalImageWidth_ + j) * 4] == 0);
}
}
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
stepEnd = std::chrono::steady_clock::now();
stepTime = std::chrono::duration_cast<std::chrono::milliseconds>(stepEnd - stepStart);
CCLOG("init normalTransparent_: %lld ms\n", stepTime.count());
#endif

CC_SAFE_DELETE_ARRAY(buffer);

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto end = std::chrono::steady_clock::now();
auto totalTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
CCLOG("load from memory: %lld ms\n", totalTime.count());
#endif
判断函数getIsTransparentAtPoint(cocos2d::Vec2 point)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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];
响应函数hitTest(const Vec2 &pt)
1
2
3
4
5
6
7
8
Vec2 localLocation = _buttonNormalRenderer->convertToNodeSpace(pt);
Rect validTouchedRect;
validTouchedRect.size = _buttonNormalRenderer->getContentSize();
if (validTouchedRect.containsPoint(localLocation) && getIsTransparentAtPoint(localLocation) == false)
{
return true;
}
return false;

需要注意的地方

alpha通道值存储的位置问题

文章目录
  1. 1. 问题描述
  • 解决思路
  • 代码
    1. 0.1. 加载函数loadNormalTransparentInfo()
    2. 0.2. 判断函数getIsTransparentAtPoint(cocos2d::Vec2 point)
    3. 0.3. 响应函数hitTest(const Vec2 &pt)
  • 需要注意的地方
  • ,