[历史归档]本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。
发布时间:2014-04-29| 标题:cocos2dx2.2.x、3.0版本绘制流程|分类:编程 / C && C++ / cocos2dx |标签:cocos2dx
cocos2dx2.2.x、3.0版本绘制流程
- cocos2dx 2.2.x版本绘制流程
- cocos2dx 3.0版本绘制流程
cocos2dx 3.0版本出来之后,可以发现绘制流程与cocos2dx 2.2.x版本相比,改动很大。这里笔记下两个版本的不同。
cocos2dx 2.2.x版本绘制流程
首先打开main.cpp:
intAPIENTRY_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_pSharedApplication=0;CCApplication::CCApplication():m_hInstance(NULL),m_hAccelTable(NULL){m_hInstance=GetModuleHandle(NULL);m_nAnimationInterval.QuadPart=0;CC_ASSERT(!sm_pSharedApplication);sm_pSharedApplication=this;}这里就知道了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_SharedDirector=newCCDisplayLinkDirector();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 won't be drawn.if(!m_bVisible){return;}kmGLPushMatrix();。。。。。this->transform();CCNode*pNode=NULL;unsignedinti=0;if(m_pChildren&&m_pChildren->count()>0){sortAllChildren();// draw children zOrder < 0ccArray*arrayData=m_pChildren->data;for(;i<arrayData->num;i++){pNode=(CCNode*)arrayData->arr;if(pNode&&pNode->m_nZOrder<0){pNode->visit();}else{break;}}// self drawthis->draw();for(;i<arrayData->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方法。
由于CCSprite,CCLayer,CCScene都是继承自CCNode,而且重写了draw方法,那么通过这样的树形设计和递归调用,就可以使场景中每个节点(Sprite,Layer等)的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,constkmMat4&parentTransform,boolparentTransformUpdated){// quick return if not visible. children won't be drawn.if(!_visible){return;}booldirty=_transformUpdated||parentTransformUpdated;if(dirty)_modelViewTransform=this->transform(parentTransform);_transformUpdated=false;// 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);inti=0;if(!_children.empty()){sortAllChildren();// draw children zOrder < 0for(;i<_children.size();i++){autonode=_children.at(i);if(node&&node->_localZOrder<0)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_orderOfArrival=0;kmGLPopMatrix();}和2.2.x的类似,也是遍历子节点,调用draw方法。那么看下draw方法:
voidNode::draw(Renderer*renderer,constkmMat4&transform,booltransformUpdated){}空函数,没有实现,我们在看下CCSprite的draw方法:
voidSprite::draw(Renderer*renderer,constkmMat4&transform,booltransformUpdated){// Don't do calculate the culling if the transform was not updated_insideBounds=transformUpdated?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中。那么通过树形设计和递归调用,场景中每个节点(Sprite,Layer等)的draw方法都会把自己要绘制的信息添加到renderer中。然后回到drawScene方法,可以发现,当场景的visit方法调用完并返回后,程序会调用_renderer的render()方法,在此方法中进行统一绘制。
分析到此,就可以初步的看出3.0版本的绘制流程了:先把要绘制的信息和数据收集起来,然后统一绘制。由于篇幅关系,这里就不继续分析CCRenderer类的方法了。