Cocos2d-x 动手实现游戏主循环

因为Cocos2d-x封装的非常好,所以对于非常多新手,他们仅仅知道先new一个场景,在场景上加入布景或精灵,然后用Director的runWithScene便能够执行游戏了。假设给一个精灵加个动作,精灵就会动,假设给布景层加入个定时器,游戏会定时执行。你知道为什么会这样吗?

作为一个游戏开发人员,我认为进入游戏这一行业之前,一定要先搞清楚“游戏主循环”这个东东,可惜我到如今才来研究这个东东。也许网上关于Cocos2d-x游戏主循环的解说一大把,可是这篇文章,我会教你怎么来实现游戏主循环。

一、了解Cocos2d-x游戏的入口

windows应用程序入口一般都在main(),我们Cocos2d-x找到main.cpp文件,里面的代码非常easy:

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控制台project,打开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游戏主循环的文章能帮到你。

posted on 2014-11-04 14:35  gcczhongduan  阅读(292)  评论(0编辑  收藏  举报