想要了解cocos2dx的启动流程可以从hellocpp示例项目的源码看起,因为这是cocos的入门项目,就如同我们以前学习第一个程序的hello world一样。
在hellocpp工程中,win32目录下只有一个main文件,而Classes目录下则是包含有从cocos2dx引擎继承的类它们是:CCAPPlication和CCLayer,在这个目录下还有一个包含有宏定义的头文件。
要分析cocos2dx的启动流程那么就从main函数开始吧,打开main.cpp文件可以看到这里有win32的入口函数_tWinMain
。
#include "main.h"
#include "../Classes/AppDelegate.h"
#include "CCEGLView.h"
USING_NS_CC;//使用cocos2dx的命名空间 参考 using namespace std;
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);//这里使用宏包含没有用到的参数可以让vc编译器不提出警告。
UNREFERENCED_PARAMETER(lpCmdLine);
// create the application instance
AppDelegate app;
CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView->setViewName("HelloCpp");
eglView->setFrameSize(2048, 1536);
// The resolution of ipad3 is very large. In general, PC's resolution is smaller than it.
// So we need to invoke 'setFrameZoomFactor'(only valid on desktop(win32, mac, linux)) to make the window smaller.
eglView->setFrameZoomFactor(0.4f);
return CCApplication::sharedApplication()->run();
}
在入口函数中,首先创建了一个AppDeletegate对象,但是下面的代码却没有使用到这个对象。怎么回事呢?原来是AppDeletegate继承自CCApplication,在创建APPDeletegate对象的时候就会隐式调用CCApplication构造函数,在这个构造函数里边会将this指针传递给全局共享对象(这个时候的this指针是CCApplication的还是AppDeletegate的?如果不是隐式调用而是使用显示调用父类的构造函数的话应该是CCApplication::CCApplication();所以说这里并没有涉及到父类的对象,实际上创建派生类对象的时候是不会创建父类对象的只会显示或者隐式的调用父类的构造函数,所以this类型是AppDeletegate的)
CCApplication::CCApplication()
: m_hInstance(NULL)//初始化win32应用程序对象
, m_hAccelTable(NULL)//初始化快捷键相关数据。
{
m_hInstance = GetModuleHandle(NULL);
m_nAnimationInterval.QuadPart = 0;//用于控制帧数的计数值。
CC_ASSERT(! sm_pSharedApplication);
sm_pSharedApplication = this;//全局共享实例对象。
}
继续分析之后的代码,CCEGLView* eglView = CCEGLView::sharedOpenGLView();获得CCEGLView全局唯一共享对象,这个对象调用了setviewname方法、setframesize方法和setframezoomfactor方法。这些方法作用都是设置窗口属性……实际上,CCEGLView包含了相应平台(这里是win32)视口的实现(win32,创建相应的窗口类、窗口过程等),这个实现被封装在获取全局共享对象里边了(
CCEGLView::sharedOpenGLView()
)。它们遵循CCEGLViewProtocol协议,也即必须提供某些接口。
最后返回CCApplication::sharedApplication()->run();这里返回的sharedApplication对象是AppDeletegate类型的,因为AppDeletegate没有重载run()所以调用的还是CCApplication的run()方法。
这里贴一下run()函数代码以及注释。
int CCApplication::run()
{
PVRFrameEnableControlWindow(false);//这个好像是专门用于windows的,写注册表禁用pvr frame。
// Main message loop:
MSG msg;
//获取cpu滴答数相关数据结构,可以认为是高精度的计时器。
LARGE_INTEGER nFreq;
LARGE_INTEGER nLast;
LARGE_INTEGER nNow;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nLast);
// Initialize instance and cocos2d.
//执行AppDeletegate重载的applicationDidFinishLaunching函数,如果进入这个函数里边可以看到这个函数里代码初始化了cocos2dx的导演类对象、文件模块对象,设置帧数创建场景等等。
if (!applicationDidFinishLaunching())
{
return 0;
}
//创建窗口,并显示。
CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
pMainWnd->centerWindow();
ShowWindow(pMainWnd->getHWnd(), SW_SHOW);
//消息循环。
while (1)
{
if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Get current time tick.
QueryPerformanceCounter(&nNow);
// If it's the time to draw next frame, draw it, else sleep a while.控制帧数的。
if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
{
nLast.QuadPart = nNow.QuadPart;
CCDirector::sharedDirector()->mainLoop();
}
else
{
Sleep(0);//如果不需要绘制下一帧那么释放CPU控制权。
}
continue;
}
if (WM_QUIT == msg.message)
{
// Quit message loop.
break;
}
// Deal with windows message.消息分发
if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
从run()函数代码可以看出来,渲染主要发生在CCDirector::sharedDirector()->mainLoop();里边。mainLoop()函数循环检查程序是否退出的标志位(
m_bPurgeDirecotorInNextLoop,导演类调用end()方法将会使这个标志位记为true。这个方法在窗口过程收到WM_CLOSE消息的时候调用的
),如果为true则清除资源。如果
m_bPurgeDirecotorInNextLoop为false
并且
m_bInvalid 并没有被置为true(这个变量由startAnimation()和stopAnimation()控制)
,那么绘制场景以及检查是否需要释放没有引用计数的内存资源。
void CCDisplayLinkDirector::mainLoop(void)//实际上CCDirector::sharedDirector();获取的是CCDisplayLinkDirector类型对象。
{
if (m_bPurgeDirecotorInNextLoop)//是否停止渲染。
{
m_bPurgeDirecotorInNextLoop = false;
purgeDirector();
}
else if (! m_bInvalid)//可以通过调用stopAnimation()的方式停止渲染,在win32下通常在对窗口进行缩小的时候会被调用。
{
drawScene();
// release the objects
CCPoolManager::sharedPoolManager()->pop();
}
}