您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-x: 死磕"HelloWorld"(5)——场景渲染准备工作

2014-07-05 07:41 549 查看
场景创建好之后,下一步便是将场景在屏幕上渲染出来。但是在渲染之前还有一些准备工作,该篇就是分析渲染准备函数runWithScene()。在第三篇中我们看到它在应用初始化函数applicationDidFinishLaunching()中被调用,且紧接场景创建函数HelloWorld::scene()之后。我们现在就来看看该函数定义:
CCDirector.cpp
void CCDirector::runWithScene(CCScene *pScene)
{
CCAssert(pScene != NULL, "This command can only be used to start the CCDirector. There is already a scene present.");
CCAssert(m_pRunningScene == NULL, "m_pRunningScene should be null");

pushScene(pScene);
startAnimation();
}

先判断即将运行的场景是否为空,如果是空的就无法运行,然后判断当前是否已经有场景在运行,如果已经有场景在运行就无法运行新的场景。判断完之后调用了两个函数,一是加载场景,二是开始动画。下面就来看第一个函数pushScence():

CCDirector.cpp
void CCDirector::pushScene(CCScene *pScene)
{
CCAssert(pScene, "the scene should not null");

m_bSendCleanupToScene = false;

m_pobScenesStack->addObject(pScene);
m_pNextScene = pScene;
}

照样先判断即将加载的场景是否为空。然后将m_bSendCleanupToScene设为假。接着调用m_pobScenesStack成员函数addObject加载场景,最后将场景指针传递给m_pNextScene。下面进入函数addObject()

CCArray.cpp
void CCArray::addObject(CCObject* object)
{
ccArrayAppendObjectWithResize(data, object);
}

该函数是CCArray类的成员函数。CCArray从名字上看是一个容器类,容器里面装的是CCObject类对象指针。由于场景继承了节点,而节点继承了CCObject类,所以场景对象也是CCObject对象。这里只调用了一个函数,顾名思义,调整大小加载对象。具体内容必须看一下该函数的定义

ccCArray.cpp
/** Appends an object. Capacity of arr is increased if needed. */
void ccArrayAppendObjectWithResize(ccArray *arr, CCObject* object)
{
ccArrayEnsureExtraCapacity(arr, 1);
ccArrayAppendObject(arr, object);
}

注意该函数不是CCArray类的成员函数,而是在ccCArray(不是CCArray)里定义的一个普通函数。它有两个参数,一是ccArray指针,另一个是CCObject指针。注意ccArray也不是CCArray,它是一个结构类型,定义如下

ccCArray.h
typedef struct _ccArray {
unsigned int num, max;
CCObject** arr;
} ccArray;

它有两个无符号整数num和max,和一个对象指针数组(即数组元素为对象指针),用于存放对象。知道了ccArray类型定义,对ccArrayAppendObjectWithResize()函数的理解就容易了一些,先看它调用的第一个函数

ccCArray.cpp
void ccArrayEnsureExtraCapacity(ccArray *arr, unsigned int extra)
{
while (arr->max < arr->num + extra)
{
ccArrayDoubleCapacity(arr);
}
}

假如arr的数组大小小于arr的当前存储量(已利用元素数目)加上将要增加储存量,则调用ccArrayDoubleCapacity(arr)将arr数组扩大一倍。该扩充函数定义如下

ccCArray.cpp
void ccArrayDoubleCapacity(ccArray *arr)
{
arr->max *= 2;
CCObject** newArr = (CCObject**)realloc( arr->arr, arr->max * sizeof(CCObject*) );
// will fail when there's not enough memory
CCAssert(newArr != 0, "ccArrayDoubleCapacity failed. Not enough memory");
arr->arr = newArr;
}

主要就是调用了一个realloc来扩充内存空间。(有个疑问,为什么不用list来保存对象呢?这样不就不用担心存储空间了吗?待解决)。当有了足够的存储空间之后,便调用第二个函数ccArrayAppendObject()来加载场景

ccCArray.cpp
void ccArrayAppendObject(ccArray *arr, CCObject* object)
{
CCAssert(object != NULL, "Invalid parameter!");
object->retain();
arr->arr[arr->num] = object;
arr->num++;
}

先判断添加的对象是否为空。不为空则调用对象的retain()函数将引用计数器加一(这是一种内存管理方式,当引用计数器降到零时,也就是对象不再被引用时才删除对象,详情请参考点击打开链接),然后就将对象指针添加到arr数组里,并将元素数目加一。

runWithScene()调用的另一函数定义为startAnimation()

CCDirector.cpp
void CCDisplayLinkDirector::startAnimation(void)
{
if (CCTime::gettimeofdayCocos2d(m_pLastUpdate, NULL) != 0)
{
CCLOG("cocos2d: DisplayLinkDirector: Error on gettimeofday");
}

m_bInvalid = false;
#ifndef EMSCRIPTEN
CCApplication::sharedApplication()->setAnimationInterval(m_dAnimationInterval);
#endif // EMSCRIPTEN
}

如果gettimeofdayCocos2d()返回不为零,则说明获取当前时间失败。然后将m_bInvalid设置为假。如果宏EMSCRIPTEN未定义(默认未定义),则调用CCApplication函数setAnimationInterval()来设置动画帧间隔。

CCApplication.cpp
void CCApplication::setAnimationInterval(double interval)
{
LARGE_INTEGER nFreq;
QueryPerformanceFrequency(&nFreq);
m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart);
}注意setAnimationInterval()有两个定义,一个在CCDirector里,另一个在CCApplication里。前者在第三节的applicationDidFinishLaunching()里出现过,它设置的动画帧间隔的单位是秒(在该HelloWorld例子里为1/60秒),而后者在此处被调用。它的功能是将之前设置的时间间隔转换为windows高精度定时器的振荡次数。先利用QuerPerformanceFrequency()获取定时器的频率,然后将之前设置的时间间隔m_dAnimationInterval乘上该频率便得到以定时器的振荡次数为单位的动画帧间隔。

至此导演类成员函数runWithScene()已经基本解释完毕。它其实只是做了一个渲染准备工作,即将场景(严格的说只是场景指针)压入栈m_pobScenesStack中,便于导演管理场景切换渲染。另外顺便设置了动画帧间隔。但是对于HelloWorld这个简单例子而言,它只有一个场景,无需切换,所以并未用到场景栈。我们在pushScene()函数中已经看到HelloWorld场景指针已经被传递给m_pNextScene,我们在下一篇场景渲染实施中将看到,导演还会把m_pNextScene传递给 m_pRunningScene,然后对其进行渲染。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: