这节开始讲CCNode,之前讲过一部分,却是介绍cocos2d-x的其他的内容。毕竟CCNode太过基础,而且大范围使用,不一点一点讲也不太现实。在cocos2d-x的开发中,没有特殊的情况,我很少继承CCNode之外的类。很多情况下,我先选择继承CCSprite来添加一些逻辑,但是在几次尝试之后发现太过愚蠢。所以我总是选择使用CCNode来作为容器,包含一个CCSprite做显示,其他的代码来做逻辑。还有时候会直接使用一个新的class,连CCNode都不用继承,但是到后来要使用回调或者Action的时候又发现当初还是太过天真。说真的,cocos2d-x为了和cocos2d保持一致连起码的原则都丢失了。换来的不过是看起来很easy的甜点。伴随移动游戏开发的成熟,游戏引擎的开源,cocos2d-x还能走多久呢?最近微博上有cocos2d-x和Unity3D口水,不忍多说了几句。
先从CCNodeRGBA开始讲起,他是实现了CCRGBAProtocol的Node。这个类主要做为继承使用。
virtual void setColor(const ccColor3B& color) = 0;
virtual void setOpacity(GLubyte opacity) = 0;
virtual void setCascadeColorEnabled(bool cascadeColorEnabled) = 0;
上面就是这个类的核心内容。第三个函数设置是否是级联处理。我们知道CCNode本身是没有绘制内容的,所以显示的逻辑做它的子类当中,这里仅仅是做一些get和set函数,我们之后再讲。
下面我们来看看CCAtlasNode,CCAtlasNode是实现了CCTextureProtocol的CCNodeRGBA。
而CCTextureProtocol就是一个简单的seter和geter。
class CC_DLL CCAtlasNode : public CCNodeRGBA, public CCTextureProtocol
virtual CCTexture2D* getTexture(void) = 0;
virtual void setTexture(CCTexture2D *texture) = 0;
有意思的是CCTextureProtocol继承之CCBlendProtocol,而它也是一个seter和geter。
class CC_DLL CCTextureProtocol : public CCBlendProtocol
virtual void setBlendFunc(ccBlendFunc blendFunc) = 0;
virtual ccBlendFunc getBlendFunc(void) = 0;
在CCNodeRGBA中使用两个下面的方法设置成员变量:
CC_PROPERTY(CCTextureAtlas*, m_pTextureAtlas, TextureAtlas);
CC_PROPERTY(ccBlendFunc, m_tBlendFunc, BlendFunc);
其定义如下,说实话很不习惯。
#define CC_PROPERTY(varType, varName, funName)\
protected: varType varName;\
public: virtual varType get##funName(void);\
public: virtual void set##funName(varType var);
这个类是个很重要的类,如果你用到了,说明你进入了cocos2d-x高手范畴。他是优化游戏的一个重要手段。在普通的CCNode体系中,一个draw函数要做很多重复的事情,对于一个通用且灵活的解决方案来说这是必要的。但是很多cpu被白白消耗了。CCAtlasNode体系提供了一个优化的解决方案,来提高绘制效率。效果是明显的,当然使用上就有限制了。应为普通的CCNode的draw函数交给子类去实现了,所以我们之后讲解,但是现在我们来看看CCAtlasNode的draw函数:
void CCAtlasNode::draw(void)
{
CC_NODE_DRAW_SETUP();
ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
GLfloat colors[4] = {_displayedColor.r / 255.0f, _displayedColor.g / 255.0f, _displayedColor.b / 255.0f, _displayedOpacity / 255.0f};
getShaderProgram()->setUniformLocationWith4fv(m_nUniformColor, colors, 1);
m_pTextureAtlas->drawNumberOfQuads(m_uQuadsToDraw, 0);
}
和普通的CCTexture不同这里用到了CCTextureAtlas。那么这是怎么加快绘制效率的呢?还记得CCTexture的那个draw函数吗?他用到了一个最简单的draw函数所必须的步骤,也就是不能再少了。但是CCTextureAtlas的绘制就不一样。
void CCTextureAtlas::drawNumberOfQuads(unsigned int n, unsigned int start)
{
if (0 == n)
{
return;
}
ccGLBindTexture2D(m_pTexture->getName());
glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);
#define kQuadSize sizeof(m_pQuads[0].bl)
// XXX: update is done in draw... perhaps it should be done in a timer
if (m_bDirty)
{
glBufferSubData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0])*start, sizeof(m_pQuads[0]) * n , &m_pQuads[start] );
m_bDirty = false;
}
ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex);
// vertices
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(ccV3F_C4B_T2F, vertices));
// colors
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(ccV3F_C4B_T2F, colors));
// tex coords
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(ccV3F_C4B_T2F, texCoords));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
glDrawElements(GL_TRIANGLES, (GLsizei)n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(m_pIndices[0])));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
CC_INCREMENT_GL_DRAWS(1);
CHECK_GL_ERROR_DEBUG();
}
其关键的不同在与数据来源是一个m_pQuads的结构,而不是一个普通的矩形。也就是说普通的Node一次只绘制了一个矩形出来,而CCNodeAltas一次绘制出了多个矩形。当然这就要求需要显示的图片要在同一张纹理上。实际上如果我们需要做一个显示单元的话,它的资源通常都会合成一张图片。加上使用CCNodeAltas就会极大的提高效率。大家不妨测试下,一百次绘制一个矩形和一次绘制一百个矩形的效率差异到底有多大。这就是CCNodeAltas能提高效率的根源。