本文基于cocos2d-x 2.2.3
项目需要一个功能,就是在点击某个不规则边缘图片的时候,不响应图片的透明部分。
以前在 AS3 中处理类似需求的时候,就是获取点击点的像素值,得到 Alpha 的值,然后根据 Alpha 的值来判断是否需要响应。
但在 cocos2d-x 中,有一些问题。
cocos2d-x 的渲染流程是这样的:
- 载入一张图片,将它解析成 CCImage 对象;
- 根据 CCImage 对象,建立 CCTexture2D 对象;
- 在 CCTexturte2D 对象中,将 CCImage 对象中的定点和像素信息上传到显卡缓存;
- 删除 CCImage 对象。
从上面的流程可以看出,其实我们在看到这张显示在 cocos2d-x 中的图片时,图像的像素信息已经不存在 CCTexture2D 中了。
那么,怎么得到这些像素值呢?
一、预先存储的方法
这种方法的原理就是修改 CCTexture2D ,使其在处理 CCImage 的时候将需要的像素信息缓存下来。
这种方法的弊端如下:
- 会占用内存来保存像素信息,如果保存所有的像素信息,则占用的内存还相当大;
- 需要修改 CCTexture2D。
二、使用 CCTexture2DMutable
下面两篇文章中都提到了这个类:
这个类继承了 CCTextuer2D,并在内部实现了对像素信息的缓存。如果要实现绘图等功能,这个类倒是挺有用的。
另外,在 cocos2d-x 中的 extensions/CCArmature
扩展中也带有这个类。
这种方法的弊端如下:
- 与 CCTextureCache 和 CCSprite 等常用类不兼容;
- 占用内存保存像素信息。
三、重绘图片取得像素
我采用的是这种办法。流程如下:
这种方法的弊端如下:
当然,如果确实需要在同一张图片上多次操作,缓存可以程序员自己来做。
为了实现这个流程,我修改了 CCImage.h,增加了两个方法 getColor4B
和 getColor4F
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
ccColor4B
getColor4B
(
float
x
,
float
y
)
{
ccColor4B
color
=
{
0
,
0
,
0
}
;
int
ix
=
(
int
)
x
-
1
;
int
iy
=
(
int
)
y
-
1
;
m_pData
+=
(
iy*
getWidth
(
)
+
ix
)
*
4
;
color
.
r
=
*
(
m_pData
++
)
;
color
.
g
=
*
(
m_pData
++
)
;
color
.
b
=
*
(
m_pData
++
)
;
color
.
a
=
*
(
m_pData
++
)
;
return
color
;
}
;
ccColor4F
getColor4F
(
float
x
,
float
y
)
{
return
ccc4FFromccc4B
(
getColor4B
(
x
,
y
)
)
;
}
;
|
2014-10-24更新:上面的代码没有考虑越界问题,在传递的坐标不在图像中时,程序会崩溃。
由于 CCImage 是跨平台实现的,所以放在头文件中比放在实现文件中要方便许多。否则,就需要在 CCImage 的若干个平台相关实现中分别执行实现了。
下面是 quick-cocos2d-x 中的实现代码,我将其放在了 CCSpriteExtned.lua 框架中,这样能让所有的 CCSprite 实例都支持这个方法。
具体的实现请看代码,不解释了。
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
|
--
NOTE
!
!
!
The
method
is
very
slowly
!
Please
use
it
in
carefully
.
--
@
param
_
_point
A
coordinate
for
color
.
--
@
param
__convertToNodeSpace
Optional
,
default
is
true
,
convert
a
coordinate
to
node
space
from
world
space
.
--
@
param
__isFloat
Optional
,
default
is
false
,
convert
a
coordinate
to
node
space
from
world
space
.
function
CCSpriteExtend
:
getColor
(
__point
,
__convertToNodeSpace
,
__isFloat
)
if
__convertToNodeSpace
==
nil
then
__convertToNodeSpace
=
true
end
if
__convertToNodeSpace
then
__point
=
self
:
convertToNodeSpace
(
__point
)
end
--
Create
a
new
Texture
to
get
the
pixel
datas
.
local
__size
=
self
:
getContentSize
(
)
local
__rt
=
CCRenderTexture
:
create
(
__size
.
width
,
__size
.
height
)
--
Hold
the
old
anchor
and
position
to
restore
it
late
on
.
local
__oldAnchor
=
self
:
getAnchorPoint
(
)
local
__oldPos
=
self
:
getPositionInCCPoint
(
)
--
Move
the
sprite
to
left
bottom
.
self
:
align
(
display
.
LEFT_BOTTOM
,
0
)
--
print
(
"getColor:"
,
__point
.
x
,
__point
.
y
,
__size
.
width
,
__size
.
height
)
--
Render
the
sprite
to
get
a
new
texture
.
__rt
:
begin
(
)
;
self
:
visit
(
)
__rt
:
endToLua
(
)
;
--
Restore
the
original
anchor
and
position
.
self
:
setAnchorPoint
(
__oldAnchor
)
self
:
setPosition
(
__oldPos
)
local
__img
=
__rt
:
newCCImage
(
false
)
local
__color
=
nil
if
__isFloat
then
__color
=
__img
:
getColor4F
(
__point
.
x
,
__point
.
y
)
else
__color
=
__img
:
getColor4B
(
__point
.
x
,
__point
.
y
)
end
return
__color
,
__rt
end
--
Only
get
a
alpha
value
.
function
CCSpriteExtend
:
getColorAlpha
(
__point
,
__isFloat
)
local
color
=
self
:
getColor
(
__point
,
__isFloat
)
return
color
.
a
en
|
这个方法已经合并进入 quick-cocos2d-x 的 develop 分支。
2014-10-24更新:由于 newCCImage 方法在 C++ 中是请求堆内存并返回一个指针。因此必须手动释放。上面的代码没有考虑释放问题,将会导致内存泄露。
源: http://zengrong.net/post/2104.htm