【2014-04-29】cocos2dx2.2.x、3.0版本绘制流程

📅 2026/8/19 22:30:03
【2014-04-29】cocos2dx2.2.x、3.0版本绘制流程
[历史归档]本文原发布于 cstriker1407.info 个人博客内容为历史存档仅供参考。发布时间2014-04-29 标题cocos2dx2.2.x、3.0版本绘制流程分类编程 / C C / cocos2dx 标签cocos2dxcocos2dx2.2.x、3.0版本绘制流程cocos2dx 2.2.x版本绘制流程cocos2dx 3.0版本绘制流程cocos2dx 3.0版本出来之后可以发现绘制流程与cocos2dx 2.2.x版本相比改动很大。这里笔记下两个版本的不同。cocos2dx 2.2.x版本绘制流程首先打开main.cppintAPIENTRY_tWinMain(。。。。。){。。。。。 AppDelegate app;。。。。。returnCCApplication::sharedApplication()-run();}然后打开AppDelegate文件classAppDelegate:privatecocos2d::CCApplication{public:AppDelegate();virtual~AppDelegate();virtualboolapplicationDidFinishLaunching();virtualvoidapplicationDidEnterBackground();virtualvoidapplicationWillEnterForeground();}可以看到AppDelegate继承自CCApplication并且重写了3个虚函数。AppDelegate初始化时会调用到CCApplication构造函数// sharedApplication pointerCCApplication*CCApplication::sm_pSharedApplication0;CCApplication::CCApplication():m_hInstance(NULL),m_hAccelTable(NULL){m_hInstanceGetModuleHandle(NULL);m_nAnimationInterval.QuadPart0;CC_ASSERT(!sm_pSharedApplication);sm_pSharedApplicationthis;}这里就知道了sm_pSharedApplication的指针为AppDelegate的实例。那么根据代码CCApplication*CCApplication::sharedApplication(){CC_ASSERT(sm_pSharedApplication);returnsm_pSharedApplication;}。。。。。intCCApplication::run(){。。。。。if(!applicationDidFinishLaunching()){return0;}。。。。。while(1){CCDirector::sharedDirector()-mainLoop();。。。。。}return(int)msg.wParam;}可知程序初始化时会调用AppDelegate的applicationDidFinishLaunching方法然后循环调用mainLoop。分析mainLoop代码CCDirector*CCDirector::sharedDirector(void){if(!s_SharedDirector){s_SharedDirectornewCCDisplayLinkDirector();s_SharedDirector-init();}returns_SharedDirector;}voidCCDisplayLinkDirector::mainLoop(void){。。。。。drawScene();。。。。。}可知mainLoop其实是调用了drawScene方法。voidCCDirector::drawScene(void){。。。。。glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);。。。。。kmGLPushMatrix();// draw the sceneif(m_pRunningScene){m_pRunningScene-visit();}。。。。。}可以看到drawScene方法实际上就是cocos2dx的绘图流程主入口每次循环中都是先glClear,然后调用场景的viist方法进行绘制场景。classCC_DLLCCScene:publicCCNodevoidCCNode::visit(){// quick return if not visible. children wont be drawn.if(!m_bVisible){return;}kmGLPushMatrix();。。。。。this-transform();CCNode*pNodeNULL;unsignedinti0;if(m_pChildrenm_pChildren-count()0){sortAllChildren();// draw children zOrder 0ccArray*arrayDatam_pChildren-data;for(;iarrayData-num;i){pNode(CCNode*)arrayData-arr;if(pNodepNode-m_nZOrder0){pNode-visit();}else{break;}}// self drawthis-draw();for(;iarrayData-num;i){pNode(CCNode*)arrayData-arr;if(pNode){pNode-visit();}}}else{this-draw();}。。。。。kmGLPopMatrix();}分析下CCNode的visit方法可以发现它的流程如果Node没有子节点那么直接调用draw方法如果Node有子节点那么就根据子节点的Zorder进行排序先找到子节点中Zorder小于Node本身的这些子节点调用它们的visit方法再调用Node本身的draw方法在调用其余子节点的visit方法这样就可以保证Zorder下的正确的层次关系。而且这里子节点调用的是visit方法如果这个子节点还有子节点就会一层一层的递归调用直到子节点下没有节点了直接调用draw方法。由于CCSpriteCCLayerCCScene都是继承自CCNode而且重写了draw方法那么通过这样的树形设计和递归调用就可以使场景中每个节点SpriteLayer等的draw方法都被调用而且是按Zorder的顺序调用的。我们这里再看一下CCSprite的draw方法voidCCSprite::draw(void){。。。。。glDrawArrays(GL_TRIANGLE_STRIP,0,4);。。。。。}可以看到这里CCSprite的draw方法中直接把自己绘制出来了。cocos2dx 2.2.x版本基本上都这个绘制流程。这个绘制流程有个好处就是便于理解每个节点都有自己的绘制方法而且会在这个绘制方法中把自己绘制出来。但是有两个问题1 opengl绘制方法和游戏业务方法混在一块opengl的代码遍布于整个cocos2dx工程不好管理代码层次较乱。2 性能较低由于各个节点都只绘制自己无法得到其他节点的绘制情况。在有遮盖的情况下绘制效率较低。比如某个节点A先绘制然后第二个节点B绘制的时候覆盖掉了节点A从效率上将由于节点A被覆盖实际上是可以不绘制的绘制了也会被B遮住。cocos2dx 3.0版本绘制流程cocos2dx 3.0版本和2.2.x版本的初始化流程差不多最后都会调到Director的drawScene方法voidDirector::drawScene(){。。。。。glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);。。。。。kmGLPushMatrix();// global identity matrix is needed... come on kazmath!kmMat4 identity;kmMat4Identity(identity);// draw the scene if (_runningScene){_runningScene-visit(_renderer,identity,false);_eventDispatcher-dispatchEvent(_eventAfterVisit);}。。。。。 _renderer-render();_eventDispatcher-dispatchEvent(_eventAfterDraw);。。。。。}这里看下3.0版本的CCNode的visit方法voidNode::visit(Renderer*renderer,constkmMat4parentTransform,boolparentTransformUpdated){// quick return if not visible. children wont be drawn.if(!_visible){return;}booldirty_transformUpdated||parentTransformUpdated;if(dirty)_modelViewTransformthis-transform(parentTransform);_transformUpdatedfalse;// IMPORTANT:// To ease the migration to v3.0, we still support the kmGL stack,// but it is deprecated and your code should not rely on itkmGLPushMatrix();kmGLLoadMatrix(_modelViewTransform);inti0;if(!_children.empty()){sortAllChildren();// draw children zOrder 0for(;i_children.size();i){autonode_children.at(i);if(nodenode-_localZOrder0)node-visit(renderer,_modelViewTransform,dirty);elsebreak;}// self drawthis-draw(renderer,_modelViewTransform,dirty);for(autoit_children.cbegin()i;it!_children.cend();it)(*it)-visit(renderer,_modelViewTransform,dirty);}else{this-draw(renderer,_modelViewTransform,dirty);}// reset for next frame_orderOfArrival0;kmGLPopMatrix();}和2.2.x的类似也是遍历子节点调用draw方法。那么看下draw方法voidNode::draw(Renderer*renderer,constkmMat4transform,booltransformUpdated){}空函数没有实现我们在看下CCSprite的draw方法voidSprite::draw(Renderer*renderer,constkmMat4transform,booltransformUpdated){// Dont do calculate the culling if the transform was not updated_insideBoundstransformUpdated?renderer-checkVisibility(transform,_contentSize):_insideBounds;if(_insideBounds){_quadCommand.init(_globalZOrder,_texture-getName(),_shaderProgram,_blendFunc,_quad,1,transform);renderer-addCommand(_quadCommand);。。。。。}}这里就有区别了可以发现3.0版本的Sprite的draw方法中没有直接调用opengl绘制而是把要绘制的内容和信息添加到了renderer中。那么通过树形设计和递归调用场景中每个节点SpriteLayer等的draw方法都会把自己要绘制的信息添加到renderer中。然后回到drawScene方法可以发现当场景的visit方法调用完并返回后程序会调用_renderer的render()方法在此方法中进行统一绘制。分析到此就可以初步的看出3.0版本的绘制流程了先把要绘制的信息和数据收集起来然后统一绘制。由于篇幅关系这里就不继续分析CCRenderer类的方法了。