在创建按钮的时候,按钮有三个状态:1、正常;2、被点击;3、不可用。
最常见的用法是,美术会做三个状态的按钮。
但是这样会不会太浪费?
因为这三个按钮只是颜色上有区别而已,而玩家最常见的其实就是正常和不可用状态下的按钮,而第二种状态的按钮其实并不特别关注。
cocos2dx里的精灵支持着色上的改变,于是,对于区分要求不是特别高的“被点击”的按钮,就可以用setColor方法来改变。看过精灵类源代码的使用者都能发现这个方法,这个方法很常用。
示例1:pSpirit->setColor(ccGRAY);// ccGRAY是引擎预定义的颜色常量。
示例2: pSpirit->setColor(ccc3(128,128,128));//ccc3是宏,里面的三个参数就是颜色的RBG了,可以自己调试颜色,或者让美术提供参数。
于是这样就可以节省一个“被点击”的按钮图片了,这通常能够为整个安装包缩减10K以上的体积,积少成多——在所有的地方都这样子节省吧,美术也能减少工作量哦。
但是,“不可用”的按钮怎么办呢?使用setColor的方法进行实验,发现不管如何设置,都不能达到那种“变灰”的那种味道。
为了节省资源和人力,其实可以给按钮构造方法的“disabled”参数传入一个NULL,但是这样做的结果就是,这个按钮调用setEnable()方法后,不会出现不可用的状态。
通过寻找资料,笔者找到一份网友写好的代码,实验之后可以正常使用:
GraySprite.h
// Created by rekoo on 13-7-23.
#ifndef __goddess__GraySprite__
#define __goddess__GraySprite__
#include "cocos2d.h"
#include "cocos-ext.h"
USING_NS_CC;
USING_NS_CC_EXT;
class
GraySprite :
public
CCSprite
{
public
:
GraySprite();
~GraySprite();
bool
initWithTexture(CCTexture2D* texture,
const
CCRect& rect);
void
draw();
void
initProgram();
void
listenBackToForeground(CCObject *obj);
static
GraySprite* create(
const
char
*pszFileName);
};
#endif
GraySprite.cpp
// Created by rekoo on 13-7-23.
#include "GraySprite.h"
GraySprite::GraySprite()
{
}
GraySprite::~GraySprite()
{
}
GraySprite* GraySprite::create(
const
char
*pszFileName)
{
GraySprite* pRet =
new
GraySprite();
if
(pRet && pRet->initWithFile(pszFileName))
{
pRet->autorelease();
}
else
{
CC_SAFE_DELETE(pRet);
}
return
pRet;
}
void
GraySprite::listenBackToForeground(CCObject *obj)
{
setShaderProgram(NULL);
initProgram();
}
bool
GraySprite::initWithTexture(CCTexture2D* texture,
const
CCRect& rect)
{
if
( CCSprite::initWithTexture(texture, rect) )
{
CCSize s = getTexture()->getContentSizeInPixels();
this
->initProgram();
return
true
;
}
return
false
;
}
void
GraySprite::initProgram()
{
const
GLchar * pfrag = "#ifdef GL_ES \n \
precision mediump
float
; \n \
#endif \n\
uniform sampler2D u_texture; \n \
varying vec2 v_texCoord; \n \
varying vec4 v_fragmentColor; \n \
void
main(
void
) \n \
{ \n \
float
alpha = texture2D(u_texture, v_texCoord).a; \n \
float
grey = dot(texture2D(u_texture, v_texCoord).rgb, vec3(0.299,0.587,0.114)); \n \
gl_FragColor = vec4(grey, grey, grey,alpha); \n \
} ";
CCGLProgram* pProgram =
new
CCGLProgram();
pProgram->initWithVertexShaderByteArray(ccPositionTextureColor_vert, pfrag);
setShaderProgram(pProgram);
pProgram->release();
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
getShaderProgram()->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);
getShaderProgram()->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->link();
CHECK_GL_ERROR_DEBUG();
getShaderProgram()->updateUniforms();
}
void
GraySprite::draw()
{
ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex );
ccBlendFunc blend = getBlendFunc();
ccGLBlendFunc(blend.src, blend.dst);
getShaderProgram()->use();
getShaderProgram()->setUniformsForBuiltins();
ccGLBindTexture2D( getTexture()->getName());
#define kQuadSize sizeof(m_sQuad.bl)
long
offset = (
long
)&m_sQuad;
int
diff = offsetof( ccV3F_C4B_T2F, vertices);
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (
void
*) (offset + diff));
diff = offsetof( ccV3F_C4B_T2F, texCoords);
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (
void
*)(offset + diff));
diff = offsetof( ccV3F_C4B_T2F, colors);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (
void
*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
CC_INCREMENT_GL_DRAWS(1);
}
可以看到这个类继承了精灵类,并写了自己的构造方法,还重写了父类的draw方法。
这个类该怎么使用呢?
普通的写法就是,把“不可用”状态下的按钮精灵使用这个类进行创建,这样就会是一个灰色的精灵,因为它是精灵类的子类,所以它可以直接用在MenuItem构造方法里的参数里——于是,第三个按钮图片也省下来了。
但是这个类有局限性,纹理打包技术是cocos2dx里经常会使用到的,很多图片都会使用纹理打包,而这些纹理,它并没有对应的构造方法,有的也是父类的,关键性的initProgram方法没法调用到。
那么,我们有几个方案:
方案1、修改这个类,让它重写精灵类的各种构造初始化方法;
方案2、类型转换。
方案1的工作量有些大,几乎把精灵类重新写了一遍,说不定还是直接去修改精灵类源码比较好。
然后我们来看看方案2:
示例:
((GraySprite*)pSprite)->initProgram();
这样,就能够把一个精灵灰度化了,而且除了灰度,精灵本身没有改变什么。
想想看这个功能,还能够使用在什么地方吧——