欢迎转载:http://blog.csdn.net/fylz1125/article/details/8524081
游戏逻辑其实就像拍摄一部电影,整个过程都由导演来驱动。
游戏中的一个一个关卡,一个一个场景都跟电影中类似。每一个场景都有一些特定的元素,比如林冲风雪山神庙这个场景,大环境就是一个大风雪的夜晚,一个银装素裹的破败山头,有一个破败的山神庙,当然还可以有一些其他的元素,比如火光,野狼,灯光, 乌鸦连声叫、黄狗大声吼等。这就是一个特定的场景了,呵呵。然后导演大叫一声:“Action!”,然后所有的故事情节就在这里展开,主角和一干配角上场了...
以上所有的元素就组成了这么一个场景,那么在游戏里又如何组织这些元素呢。先来看看cocos2d-x的场景处理流程。
cocos2d-x中的导演执行单元就是场景CCScene。一个场景又有那么多元素,比如一个大的背景,然后又有一些小元素,比如星星,火堆,狗等,又是如何加到场景里的呢。
可以这么说,一个场景就是一个特别的容器(其实就是一个CCNode),它能容纳各类节点(通过addChild)。那么跟电影一样,镜头一出来先有一个大的环境。这个就是主背景层(CCLayer),对,就是CCLayer(怎么翻译好一点)。然后在这个主Layer里面再添加一些其他的元素,比如人物、火堆、狂奔的大狗、清唱的小鸟等。这些元素都是独立的对象(继承CCSprite),他们都有各自的属性和行为,比如人物是一个男人,他在漫无目的的行走,火堆在燃烧放出一跳一跳的火光,狗在奔跑,鸟在飞来飞去等。所有这些元素都添加到主Layer里面,然后这个主Layer又被添加都场景里面(CCScene),最后导演执行这个场景(pDirector->runWithScene(pScene)),一个画面就出来了。
来看一看代码级别的执行过程:
主循环(不知道的看我前面的文章)
void CCDisplayLinkDirector::mainLoop(void) { if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene();// 注意这里就是画场景了 // release the objects CCPoolManager::sharedPoolManager()->pop(); } }
void CCDirector::drawScene(void) { // calculate "global" dt calculateDeltaTime(); //tick before glClear: issue #533 if (! m_bPaused) { m_pScheduler->update(m_fDeltaTime);//按照优先级调度update函数(每个节点都有这么个函数) } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* to avoid flickr, nextScene MUST be here: after tick and before draw. XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ if (m_pNextScene) // 这个变量存下次绘制的场景,本次绘制则是上一次设置的场景 { setNextScene();// 注意这个,不要被名字误导,其实就是设置下一次要运行的场景 } kmGLPushMatrix(); // draw the scene if (m_pRunningScene) { m_pRunningScene->visit();// 注意看这里,这个visit函数就是递归绘制子节点 } // draw the notifications node if (m_pNotificationNode) { m_pNotificationNode->visit(); } if (m_bDisplayStats) { showStats(); } kmGLPopMatrix(); m_uTotalFrames++; // swap buffers if (m_pobOpenGLView) { m_pobOpenGLView->swapBuffers(); } if (m_bDisplayStats) { calculateMPF(); } }
void CCNode::visit() { // quick return if not visible. children won't be drawn. if (!m_bVisible) { return; } kmGLPushMatrix(); if (m_pGrid && m_pGrid->isActive()) { m_pGrid->beforeDraw(); } this->transform(); CCNode* pNode = NULL; unsigned int i = 0; if(m_pChildren && m_pChildren->count() > 0)//这里开始,如果有子节点就进入 { sortAllChildren();// 按z坐标排序,就是z序排列子节点 // draw children zOrder < 0 ccArray *arrayData = m_pChildren->data; for( ; i < arrayData->num; i++ )//画z序<0的子节点 { pNode = (CCNode*) arrayData->arr[i]; if ( pNode && pNode->m_nZOrder < 0 ) { pNode->visit(); } else { break; } } // self draw this->draw(); // 画自己 for( ; i < arrayData->num; i++ ) { pNode = (CCNode*) arrayData->arr[i]; if (pNode) { pNode->visit(); } } } else { this->draw(); // 没有子节点就画自己 } // reset for next frame m_uOrderOfArrival = 0; if (m_pGrid && m_pGrid->isActive()) { m_pGrid->afterDraw(this); } kmGLPopMatrix(); }
CCScene *pScene = HelloWorld::scene(); // run pDirector->runWithScene(pScene);
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");// 当前运行为NULL(第一次) pushScene(pScene);// push是什么呢 startAnimation(); // 这里控制帧率,同时又进入主循环 }
void CCDirector::pushScene(CCScene *pScene) { CCAssert(pScene, "the scene should not null"); m_bSendCleanupToScene = false; m_pobScenesStack->addObject(pScene);// 当前scene入栈 m_pNextScene = pScene; // next Scene,就是下一次绘制当前scene }
好了,这个pushScene干了两件事,将要绘制的scene入栈,将下次要绘制的场景设为pScene。这个函数返回后接着是
startAnimation(),这个函数调整帧率然后再次进入主循环。
再回头看看主循环的drawScene()函数(上面有),其中有段:
if (m_pNextScene)// 刚才的pushScene函数设置了这个值 { setNextScene(); // 将下个要运行的Scene给m_pRunningScene } kmGLPushMatrix(); // draw the scene if (m_pRunningScene) // 这里开始渲染当前要运行的Scene { m_pRunningScene->visit(); }
导演每次只能执行一个场景,执行并不仅仅是绘制,还包括其动作,回调,过渡等。每个场景实质上是一个CCNode,
前面讲过,CCNode有个很重要的成员变量m_pChildren,这是一个CCArray,用来存放添加到该节点的子节点。Scene在渲染的时候会递归渲染其子节点,这就组成一个渲染链,保证整个场景的所有元素都会被渲染。