CCDirector类是Cocos2D-x游戏引擎的核心,它用来创建并且控制着屏幕的显示,同时控制场景的显示时间和显示方式。
在整个游戏里一般只有一个导演。游戏的开始、结束、暂停都会调用CCDirector类的方法。CCDirector类具有如下功能。
- 初始化OpenGL会话。
- 设置OpenGL的一些参数和方式。
- 访问和改变场景以及访问Cocos2D-x的配置细节。
- 访问视图。
- 设置投影和朝向。
- CCDirector::sharedDirector()->函数名
CCDirector类的继承关系,如图3-8所示。
CCDisplayLinkDirector继承了CCDirector,是一个可以自动刷新的导演类。它只支持1/60 、1/30 和1/15三种动画间隔(帧间隔)。在Cocos2D-x里面,在游戏的任何时间,只有一个场景对象实例处于运行状态,而导演就是流程的总指挥,它负责游戏全过程的场景切换,这也是典型的面向对象和分层的设计原则。下面分别介绍CCDirector类的成员数据和函数。
先看返回单例对象的方法
Director* Director::getInstance(){
if (!s_SharedDirector)
{
s_SharedDirector = new DisplayLinkDirector();
s_SharedDirector->init();
}
return s_SharedDirector;}
注意,返回的是DisplayLinkDirector这个类,并且在创建完 DisplayLinkDirector对象之后调用了init方法,看一下init方法。
从这个方法里面我们再一次了解一下,Director具体都能干什么,和一些内部初始化的工作是怎么完成的
bool Director::init(void){
setDefaultValues();
// scenes
_runningScene = nullptr;
_nextScene = nullptr;
_notificationNode = nullptr;
_scenesStack.reserve(15);
// FPS
_accumDt = 0.0f;
_frameRate = 0.0f;
_FPSLabel = _drawnBatchesLabel = _drawnVerticesLabel = nullptr;
_totalFrames = _frames = 0;
_lastUpdate = new struct timeval;
// paused ?
_paused = false;
// purge ?
_purgeDirectorInNextLoop = false;
_winSizeInPoints = Size::ZERO;
_openGLView = nullptr;
_contentScaleFactor = 1.0f;
// scheduler
_scheduler = new Scheduler();
// action manager
_actionManager = new ActionManager();
_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
_eventDispatcher = new EventDispatcher();
_eventAfterDraw = new EventCustom(EVENT_AFTER_DRAW);
_eventAfterDraw->setUserData(this);
_eventAfterVisit = new EventCustom(EVENT_AFTER_VISIT);
_eventAfterVisit->setUserData(this);
_eventAfterUpdate = new EventCustom(EVENT_AFTER_UPDATE);
_eventAfterUpdate->setUserData(this);
_eventProjectionChanged = new EventCustom(EVENT_PROJECTION_CHANGED);
_eventProjectionChanged->setUserData(this);
//init TextureCache
initTextureCache();
_renderer = new Renderer;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
_console = new Console;
#endif
return true;}
可以看到,Director这个大管家初始化了 ActionManager 动作管理器,并将 _actionManager加到了定时器里,初始化了EventDispatcher EventCustom 等事件。初始化了纹理 和渲染器 Rendere。
下面我们再看一下DisplayLinkDirector这个类,这是Director的实体类。
class DisplayLinkDirector : public Director
{
public:
DisplayLinkDirector(): _invalid(false){}
//
// Overrides
//
virtual void mainLoop() override;
virtual void setAnimationInterval(double value) override;
virtual void startAnimation() override;
virtual void stopAnimation() override;
protected:
bool _invalid;};
这个类实现了Director的几个关键的虚函数。mainLoop这个是最主要的了,上面我们一再说逻辑循环,其实都是指这个函数,所有的操作,动画,渲染,定时器都在这里驱动的。游戏主循环里反复的调度 mainLoop来一帧一帧的实现游戏的各种动作,动画……. mainLoop来决定当前帧该执行什么,是否到时间执行等等。
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop){
_purgeDirectorInNextLoop = false;
purgeDirector();
}else if (!_invalid)
{drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
代码很简单,根据对 purgeDirectorInNextLoop 判断来决定mainLoop是否应该清除。
_invalid来决定 Director是否应该进行逻辑循环。这段代码很简单,主要操作都封闭到了 drawScene里面后面我们跟进drawScene来看看每个逻辑帧都干了些什么。
后面还有一句代码:PoolManager::getInstance()->getCurrentPool()->clear();
从命名上来看是做清除操作,应该是内存操作,每帧回收不用的引用对象应该是在这里触发的,我们在内存应用的章节再回过头来讨论这块。
下面看drawScene的代码
void Director::drawScene(){
// 计算帧之间的时间间隔,下面根据这个时间间隔来判断是否应该进行某某操作
calculateDeltaTime();
// skip one flame when _deltaTime equal to zero.
if(_deltaTime < FLT_EPSILON) {return;}
if(_openGLView){_openGLView->pollInputEvents();}
//Director没有暂停的情况下,更新定时器,分发 update后的消息
if(!_paused){
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);}
// opengl清理
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*设置下个场景*/
if (_nextScene){
setNextScene();}
kmGLPushMatrix();
// global identity matrix is needed... come on kazmath!
kmMat4 identity;
kmMat4Identity(&identity);
// 渲染场景
if (_runningScene)
{_runningScene->visit(_renderer, identity, false);
// 分发场景渲染后的消息
_eventDispatcher->dispatchEvent(_eventAfterVisit);}
// 渲染notifications 结点,这个结点有什么用现在还看不太清楚,后面章节我们一定会摸清楚的
if (_notificationNode){_notificationNode->visit(_renderer, identity, false); }
if (_displayStats){showStats();} // 渲染 FPS等帧频显示
_renderer->render(); // 调用了渲染器的render方法,具体我们在分析Render类的时候再回过来看都干了些什么
_eventDispatcher->dispatchEvent(_eventAfterDraw);
kmGLPopMatrix();
_totalFrames++;
// swap buffers
if (_openGLView){_openGLView->swapBuffers();}
if (_displayStats){calculateMPF();}
}
到现在,我们完整的分析了Director类,了解了这个大管家都管理了哪些对象。
下面我们做个总结。Director主要管理了场景,四个事件的分发,渲染, Opengl对象等。它主要是以场景为单位来控制游戏的逻辑帧,通过场景的切换来实现游戏中不同界面的变化。mainloop这个函数调用了drawscene来实现每一帧的逻辑,主要是渲染逻辑。