news 2026/8/19 22:29:56

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【2014-04-29】cocos2dx2.2.x、3.0版本绘制流程

[历史归档]本文原发布于 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类的方法了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 22:22:34

当贝D7XPro超亮版升级了啥?对比标准版,看完才知道差距

作为当贝爆款家用4K投影旗舰&#xff0c;当贝D7X Pro凭借高清画质、无损光学变焦、低延迟游戏模式&#xff0c;成为卧室、小户型家用投影的首选机型。随着日间观影需求提升&#xff0c;当贝推出D7X Pro超亮版&#xff0c;在保留原版所有优势的基础上实现全方位硬件与画质升级。…

作者头像 李华
网站建设 2026/8/19 22:21:38

建站平台有哪些类型?SaaS、自助建站、CMS和源码方案对比

建站平台有哪些类型&#xff1f;SaaS、自助建站、CMS和源码方案对比建站平台有哪些类型&#xff1f;如果只看广告名称&#xff0c;很容易把SaaS建站、自助建站、CMS、源码定制和设计型工具混在一起。它们都能做网站&#xff0c;但成本、维护方式、自由度和适合企业完全不同。企…

作者头像 李华
网站建设 2026/8/19 22:17:00

20万级SUV全能之选:混动技术、智能安全与空间设计如何满足家庭需求

1. 从“够用”到“全能”&#xff1a;80后购车需求的深层演变 聊到20万级别的SUV&#xff0c;市场选择多到让人眼花缭乱。但如果你问一个典型的80后&#xff0c;他到底需要一台什么样的车&#xff0c;答案往往不再是十年前“能开、省油、空间大”那么简单。我们这代人&#xff…

作者头像 李华
网站建设 2026/8/19 22:15:22

编程语言性能深度解析:从编译原理到实战选型指南

在技术社区中&#xff0c;关于“哪种编程语言运行速度最快”的讨论从未停止。无论是刚入门的新手在选择第一门语言&#xff0c;还是资深架构师在为高性能系统选型&#xff0c;这个问题都至关重要。然而&#xff0c;答案远非一个简单的名字&#xff0c;它涉及到编译与解释、静态…

作者头像 李华
网站建设 2026/8/19 22:09:13

Flipper Zero能否入侵汽车?深度解析CAN总线安全与防御

1. 项目概述&#xff1a;一个关于汽车安全的热门议题“CAN Flipper hack a car?” 这个标题&#xff0c;最近在技术爱好者和汽车安全圈子里讨论得挺多。简单来说&#xff0c;它探讨的是一个名叫Flipper Zero的便携式多功能工具&#xff0c;是否能够通过汽车的CAN总线对车辆进行…

作者头像 李华
网站建设 2026/8/19 22:07:29

FreeRTOS资源管理实战:从临界区到守护任务的五种策略详解

1. 从“能用”到“稳定”&#xff1a;为什么资源管理是FreeRTOS的必修课 刚开始玩FreeRTOS的时候&#xff0c;你是不是也和我一样&#xff0c;觉得任务创建、队列、信号量这些概念搞明白了&#xff0c;程序能跑起来&#xff0c;就算“入门”了&#xff1f;我最早也是这么想的&a…

作者头像 李华