由于Cocos2d-x封装的很好,所以对于很多新手,他们只知道先new一个场景,在场景上添加布景或精灵,然后用Director的runWithScene便可以运行游戏了。如果给一个精灵加个动作,精灵就会动,如果给布景层添加个定时器,游戏会定时执行。你知道为什么会这样吗?
作为一个游戏开发者,我觉得进入游戏这一行业之前,一定要先搞清楚“游戏主循环”这个东东,可惜我到现在才来研究这个东东。或许网上关于Cocos2d-x游戏主循环的讲解一大把,但是这篇文章,我会教你怎么来实现游戏主循环。
一、了解Cocos2d-x游戏的入口
windows应用程序入口一般都在main(),我们Cocos2d-x找到main.cpp文件,里面的代码很简单:
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(); }
好了,我们知道了Application类里面的run函数就是入口了。找到Application.cpp文件,我截取run函数最重要的那部分代码:
while(!glview->windowShouldClose()) { QueryPerformanceCounter(&nNow); if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart) { nLast.QuadPart = nNow.QuadPart; director->mainLoop(); glview->pollEvents(); } else { Sleep(0); } }
看到没,这里有一个死循环,粗略地看,也就是这样的:_animationInterval是我们设定的循环间隔,每次前后两个时间的差大于这个间隔,就执行一次主循环director->mainLoop(),小于这个间隔呢,就一直获取当前的时间,直到大于这个间隔。
大概就是这样,为什么程序会一直执行呢?接下来我们来自己动手实现以下这个循环。
二、Windows性能计数器(Performance Counter)
在run函数中,我们发现每次时间差大于设定的间隔,就会执行游戏主循环,那么这个时间差是哪里来的呢?就是这个东东:QueryPerformanceCounter,选中QueryPerformanceCounter按F12,看看是何方神圣:
// // Performance counter API's // WINBASEAPI BOOL WINAPI QueryPerformanceCounter( _Out_ LARGE_INTEGER * lpPerformanceCount ); WINBASEAPI BOOL WINAPI QueryPerformanceFrequency( _Out_ LARGE_INTEGER * lpFrequency );
也就是说,这两个函数其实是Windows API来的。
QueryPerformanceCounter():返回高精度性能计数器的值,QueryPerformanceCounter确切的精确计时的最小单位是与系统有关的,所以必须查询系统以得到QueryPerformanceCounter返回的滴答声的频率。
QueryPerformanceFrequency()则提供了这个频率值,返回每秒滴答声的个数。
计算两个时间的间隔,就是用QueryPerformanceCounter()先后获取两个时间,这两个时间的间隔再除以时钟频率,就是真正的时间差了。
再看类型LAGER_INTEGER:
#if defined(MIDL_PASS) typedef struct _LARGE_INTEGER { #else // MIDL_PASS typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; } DUMMYSTRUCTNAME; struct { DWORD LowPart; LONG HighPart; } u; #endif //MIDL_PASS LONGLONG QuadPart; } LARGE_INTEGER;
LARGE_INTEGER既可以是一个8字节长的整型数(LONGLONG),也可以是两个4字节长的整型数的联合结构,具体用法根据编译器是否支持64位而定。
三、具体实现主循环
往事具备,只欠东风。有了上面那些知识,我们就可以自己动手实现了。
用VS2012新建一个win32控制台工程,打开main.cpp文件开始敲代码了。
先创建一个MainLoop类:
class MainLoop { public: MainLoop():timeCount(0){} void setAnimationInterval(double interval); void run(); private: LARGE_INTEGER _animationInteval; unsigned timeCount; }; void MainLoop::setAnimationInterval(double interval) { LARGE_INTEGER nFreq; QueryPerformanceFrequency(&nFreq); _animationInteval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart); } void MainLoop::run() { LARGE_INTEGER nFreq; LARGE_INTEGER nBeginTime; LARGE_INTEGER nEndTime; QueryPerformanceFrequency(&nFreq); // 获取机器内部计时器的时钟频率 QueryPerformanceCounter(&nBeginTime); // 获得第一次计数 while (1) // 死循环 { QueryPerformanceCounter(&nEndTime); // 获得第二次计数 if (nEndTime.QuadPart - nBeginTime.QuadPart > _animationInteval.QuadPart) // 两次计数差大于指定间隔则执行 { timeCount++; printf("%d\n", timeCount); nBeginTime = nEndTime; } } }
这个类可以设置时间间隔,当前后两次时间差大于该间隔的时候,就输出一次timeCount,timeCount就是计数器。
int _tmain(int argc, _TCHAR* argv[]) { MainLoop loop; loop.setAnimationInterval(1); loop.run(); system("pause"); return 0; }
在main函数中,我们先设置时间间隔为1秒,开始run,这时可以看到,每隔1秒钟,控制台就会输出计数器:
这就是我们要的循环了,想一下,时间间隔越小,计数器就加的越快,当时间间隔为1/60时,就跟Cocos2d-x的一样了。
回去看看Application的代码,是不是觉得很像很像。只不过在Application中执行的不是计数器叠加,而是director->mainLoop(),也就是所谓的游戏主循环。
游戏主循环的分析就到这里了,网上充斥着大量一样的文章,ctrl+c和ctrl+v太简单了,希望我这一篇有别于其他Cocos2d-x游戏主循环的文章能帮到你。