Cocos2d-x 3.x启动过程
来源:互联网 发布:300英雄官方淘宝店 编辑:程序博客网 时间:2024/04/28 23:18
目标:- 理解cocos2d-x启动过程- 对整个框架有个初步认识
程序入口
我们在学习C/C++的时候,知道每个C/C++程序都有一个且只有一个入口点(main函数),同样我们通过Cocos引擎生成的初始项目代码也有入口点,由于此时我们所写的代码是区别于控制台的代码,入口点函数便不再是main(这是在学习控制台程序时的入口点)。有过Win32应用开发经验的人知道,每个程序的入口点函数是_tWinMain(这个在win32筛选器下的main.cpp可以找到),它的函数原型如下:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
知道了入口点,我们就可以从入口点函数一条一条代码的往下读
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance AppDelegate app; return Application::getInstance()->run();}
如上述代码所示,首先实例化了一个AppDelegate对象,这个类可以理解为一个用于管理整个App的类。实例化之后,通过调用getInstance()方法获得实例指针(单例模式),然后调用run()方法,整个_tWinMain函数到此就结束了。众所周知,在C/C++中,main函数执行完了,那么整个程序也就退出了,但是为什么在这里,程序能够一直运行呢?
真正的开始—run()
接着上面的疑问,我们试着调试程序,顺表看看run()的实现。
int Application::run(){ //在注册表中写入对于PVRFrame图像文件帧的显示和隐藏的设置 PVRFrameEnableControlWindow(false); // Main message loop: LARGE_INTEGER nLast; LARGE_INTEGER nNow; //这是个WIN API,使用QueryPerformanceCounter来查询定时器的计数值,如果硬件里有定时器,它就会启动这个定时器,并且不断获取定时器的值,这样的定时器精度,就跟硬件时钟的晶振一样精确的。 QueryPerformanceCounter(&nLast); //设置GL上下文环境,需要重载实现,否则使用默认配置</span> initGLContextAttrs(); // Initialize instance and cocos2d. // 完成应用程序启动前的一些工作,主要完成导演实例化和场景布置等等工作。 if (!applicationDidFinishLaunching()) { return 1; } auto director = Director::getInstance(); //cocos2d::GLViewImp1,获得整个GL视图的管理 auto glview = director->getOpenGLView(); // Retain glview to avoid glview being released in the while loop glview->retain();//引用计数+1 while(!glview->windowShouldClose()) //默认返回false { //针对 每帧渲染时间消耗 与 fps之间的矛盾着的“异常”处理 //nNow : 本次渲染开始时刻 //nLast : 上一次渲染结束时刻 QueryPerformanceCounter(&nNow); //如果上一次渲染花费的时间 > 设定的fps if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart) { nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart); director->mainLoop(); //如果没有设置nextScene,那么将一直调用对当前场景渲染 glview->pollEvents(); } else { Sleep(1); } } // Director should still do a cleanup if the window was closed manually. if (glview->isOpenGLReady()) { director->end(); director->mainLoop(); director = nullptr; } glview->release(); return 0;}void AppDelegate::initGLContextAttrs(){ GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8}; GLView::setGLContextAttrs(glContextAttrs);}
- 在调试的过程中我们发现,程序将一直“徘徊”在while(!glview->windowShouldClose())循环处。其实,这个循环就是类似Win32开发当中的消息循环,也就是说,在关闭应用程序之前,整个程序将一直在处于上述这个循环当中。
- 其实,整个程序的开始是由调用run()开始,run()方法中,前半部分负责初始化应用程序环境,中间部分while循环负责消息循环及场景渲染,后半部份负责清理。
以下函数负责初始化OpenGL视图上下文环境。
void AppDelegate::initGLContextAttrs(){ //结构体 GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8}; //通过GLView的一些函数可以操作GL视图的界面信息 GLView::setGLContextAttrs(glContextAttrs);}struct GLContextAttrs { //设置渲染所用的调色风格,如:redBits = 8,表示R通道用8bit来表示 int redBits; int greenBits; int blueBits; int alphaBits; int depthBits; int stencilBits;};
准备工作
首先先看下AppDelegate这个类:
class AppDelegate : private cocos2d::Application{public: AppDelegate(); virtual ~AppDelegate(); virtual void initGLContextAttrs(); virtual bool applicationDidFinishLaunching(); virtual void applicationDidEnterBackground(); virtual void applicationWillEnterForeground();};
AppDelegate继承于Application,主要实现了6个方法,其中initGLContextAttrs()用于初始化GL视图环境;applicationDidFinishLaunching()是在程序位于前台时候会调用;applicationDidEnterBackground()是程序转向后台时调用,一般实现是暂停游戏;applicationWillEnterForeground()是程序由后台转向前台时调用,一般实现是恢复游戏。
在上述run()方法中,我们发现依次调用了initGLContextAttrs()用于初始化GL视图 和 applicationDidFinishLaunching()用于负责应用程序生成前的一些准备工作,比如实例化导演对象和场景布置:
bool AppDelegate::applicationDidFinishLaunching() { auto director = Director::getInstance(); auto glview = director->getOpenGLView(); if(!glview) { //因为cocos2d底层采用opengl进行渲染,首先需要一张opengl的画布 //有了画布之后,整个给app才能显示 glview = GLViewImpl::create("Cpp Empty Test"); director->setOpenGLView(glview); } director->setOpenGLView(glview); //设置分辨率,因为director->setOpenGLView(glview)传的是指针,所以此处修改,能直接director所指对象的状态。 glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER); //获得app框架大小,这个值区别于分辨率。screen size Size frameSize = glview->getFrameSize(); vector<string> searchPath; // 下面这段代码是cocos2d关于屏幕适配,省略部分代码,不影响讲解 if (frameSize.height > mediumResource.size.height) ... //得出屏幕适配方案,设置资源路径 FileUtils::getInstance()->setSearchPaths(searchPath); // 用于调试用的 director->setDisplayStats(true); // FPS 默认是1/60.0 director->setAnimationInterval(1.0 / 60); // 创建场景:通常需要我们写的就是HelloWorld等等具体的游戏场景 auto scene = HelloWorld::scene(); // 导演负责将场景布置到opengl视图之上,opengl负责渲染场景 director->runWithScene(scene); return true;}
上述代码,负责将导演实例化之后,并且为导演分配工具(GL视图)和场景,导演将根据场景布置现场。runWithScene将场景Scene压入场景栈,在Application::run()的循环中,将把该被渲染的场景(被设置为_runningScene的场景)解析成绘图命令(draw command),形成一个queue(绘图队列)。
void Director::runWithScene(Scene *scene){ CCASSERT(scene != nullptr, "This command can only be used to start the Director. There is already a scene present."); CCASSERT(_runningScene == nullptr, "_runningScene should be null"); pushScene(scene); startAnimation();}void Director::pushScene(Scene *scene){ CCASSERT(scene, "the scene should not null"); _sendCleanupToScene = false; //一个导演并不只是负责一个场景,导演类维护了一个场景栈(保存了所有要渲染的场景) _scenesStack.pushBack(scene); //当将一个场景压入场景栈中时,也顺便指定下一个要渲染的场景为压入的场景 _nextScene = scene;}void DisplayLinkDirector::startAnimation(){ if (gettimeofday(_lastUpdate, nullptr) != 0) { CCLOG("cocos2d: DisplayLinkDirector: Error on gettimeofday"); } //这个函数中并没有真正开始播放动画,设置场景无效标志。 _invalid = false; _cocos2d_thread_id = std::this_thread::get_id(); //设置FPS :帧间隔时间(单位:秒),默认是1.0/60 Application::getInstance()->setAnimationInterval(_animationInterval); // fix issue #3509, skip one fps to avoid incorrect time calculation. setNextDeltaTimeZero(true);}
所谓的“渲染”在mainLoop中进行,但是drawScene并不进行真正的渲染(v3.x版本之后,cocos2d-x改变了他的渲染方式):
void DisplayLinkDirector::mainLoop(){ //检测Director对象的标志,根据标志选择接下来的动作 if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector(); //清理 } else if (_restartDirectorInNextLoop) { _restartDirectorInNextLoop = false; restartDirector(); } else if (! _invalid) { //检测到场景无效标志,则渲染场景 drawScene(); // release the objects //因为场景中有各种Sprite/Layer等等对象,这些对象可能采用静态工厂方法创建(如:create),那么这些对象都将在创建的时候被加入到自动释放池,由引擎管理的自动释放池来维护这些对象 //当“渲染”完场景之后,自动释放池将被调用来清理这些对象。清理的 //上述glview也是采用静态工厂方法生成,所以为免被自动释放池清理,需要在创建之后retain一次。 PoolManager::getInstance()->getCurrentPool()->clear(); }}
drawScene只是生成一些渲染命令,并将命令压入一个队列:
// Draw the Scenevoid Director::drawScene(){ // calculate "global" dt calculateDeltaTime(); if (_openGLView) { //GL视图轮询事情,这是个空函数 _openGLView->pollEvents(); } //tick before glClear: issue #533 if (! _paused) { _eventDispatcher->dispatchEvent(_eventBeforeUpdate); _scheduler->update(_deltaTime); _eventDispatcher->dispatchEvent(_eventAfterUpdate); } _renderer->clear(); experimental::FrameBuffer::clearAllFBOs(); /* to avoid flickr, nextScene MUST be here: after tick and before draw. * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9 */ if (_nextScene) { setNextScene(); } pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); if (_runningScene) {#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH) _runningScene->stepPhysicsAndNavigation(_deltaTime);#endif //clear draw stats _renderer->clearDrawStats(); //render the scene//render负责节点树中的各节点渲染需要</span> _runningScene->render(_renderer); _eventDispatcher->dispatchEvent(_eventAfterVisit); } // draw the notifications node if (_notificationNode) {//visit函数负责处理由事情触发的渲染请求</span> _notificationNode->visit(_renderer, Mat4::IDENTITY, 0); } if (_displayStats) { showStats(); } _renderer->render(); _eventDispatcher->dispatchEvent(_eventAfterDraw); popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); _totalFrames++; // swap buffers if (_openGLView) { _openGLView->swapBuffers(); } if (_displayStats) { calculateMPF(); }}
小结
- 程序的路口点是_tWinMain
- 程序真的开始是由调用AppDelegate::run()开始,run()中实现了消息循环和场景渲染。
- applicationDidFinishLaunching()负责程序开始前的一些准备工作,如场景大小、导演等等的初始化。
- Cocos2d-x 3.x启动过程
- cocos2d-x启动过程
- Cocos2d-x 启动过程详解:渲染
- Cocos2d-x 启动过程详解:渲染
- Cocos2d-x 启动过程详解:渲染
- 简析 cocos2d-x Android 调用启动过程
- cocos2d-x启动次序
- Cocos2d-x启动图片
- cocos2d x 3.x
- cocos2d-x 安装过程
- Cocos2d-x 3.x Lua的启动流程
- Cocos2d-x程序在Android下的启动过程
- Cocos2d-x程序在Android下的启动过程
- COCOS2D-X 的启动流程
- Cocos2d-x 3.x部署
- cocos2d-x-3.x guides
- cocos2d-x 3.x CallFunc
- Cocos2d-x 3.x开篇
- android官网demo之view渐变
- Forward渲染路径学习笔记
- 测试-这是在使用windows live writer写CSDN博客
- 暗时间
- 文件IO(及时关闭文件以释放管理资源)
- Cocos2d-x 3.x启动过程
- Ant小小结
- IT 行业各种备份术语
- IP地址与MAC地址的区别
- from表单中action有地址,向后台提交参数的相关问题
- 第一个DX程序
- CodeForces-630C. Lucky Numbers
- 【JAVA】 基础练习 BASIC-6 杨辉三角形
- git常见参数