Director – 导演类

  • 管理场景Scene
  • 实例化各种全局XXXX
  • 绑定GLView

路径

1
2
cocos/2d/CCDirector.h
cocos/2d/CCDirector.cpp

源码分析

先上一盘头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
class CC_DLL Director : public Ref
{
public:
static const char *EVENT_PROJECTION_CHANGED;
static const char* EVENT_AFTER_UPDATE;
static const char* EVENT_AFTER_VISIT;
static const char* EVENT_AFTER_DRAW;


/* 导演类投影 */
enum class Projection
{
_2D,
_3D, /* fovy=60, znear=0.5f, zfar=1500 */
CUSTOM,
DEFAULT = _3D,
};

/* 实例 */
static Director* getInstance();
CC_DEPRECATED_ATTRIBUTE static Director* sharedDirector() { return Director::getInstance(); }

Director(void);
virtual ~Director();
virtual bool init();

/********/
/* 属性 */
/********/

inline Scene* getRunningScene() { return _runningScene; }
inline double getAnimationInterval() { return _animationInterval; }
virtual void setAnimationInterval(double interval) = 0;

inline bool isDisplayStats() { return _displayStats; }
inline void setDisplayStats(bool displayStats) { _displayStats = displayStats; }
inline float getSecondsPerFrame() { return _secondsPerFrame; }

inline GLView* getOpenGLView() { return _openGLView; }
void setOpenGLView(GLView *openGLView);

TextureCache* getTextureCache() const;
inline bool isNextDeltaTimeZero() { return _nextDeltaTimeZero; }
void setNextDeltaTimeZero(bool nextDeltaTimeZero);

inline bool isPaused() { return _paused; }
inline unsigned int getTotalFrames() { return _totalFrames; }

inline Projection getProjection() { return _projection; }
void setProjection(Projection projection);

void setViewport();
inline bool isSendCleanupToScene() { return _sendCleanupToScene; }

Node* getNotificationNode() const { return _notificationNode; }
void setNotificationNode(Node *node);

const Size& getWinSize() const;

Size getWinSizeInPixels() const;
Size getVisibleSize() const;
Point getVisibleOrigin() const;
Point convertToGL(const Point& point);
Point convertToUI(const Point& point);
float getZEye() const;

/************/
/* 场景管理 */
/************/

/* 第一个场景调用 */
/* 先入场景栈,再调用 startAnimation */
void runWithScene(Scene *scene);

/* 入场景栈 */
void pushScene(Scene *scene);

/* 出场景栈 */
void popScene();

/* 除了场景栈栈底元素,其余都出栈 */
void popToRootScene();

/* 出栈,知道栈中只剩 Level 个元素 */
void popToSceneStackLevel(int level);

/* 替换当前栈顶元素 */
void replaceScene(Scene *scene);

/* 结束Direcotr场景管理 */
/* 其实只是将一个标志位设置为false */
void end();

/* 暂停当前场景 */
/* 而且FPS会降低至4 */
void pause();

/* 恢复当前场景 */
void resume();

/* 停止绘制 */
virtual void stopAnimation() = 0;

/* 开始绘制 */
virtual void startAnimation() = 0;

/* 每一帧绘制调用 */
void drawScene();

/************/
/* 内存管理 */
/************/

/* 清理缓存 */
void purgeCachedData();

/* 根据Configuration设置默认值 */
void setDefaultValues();

/***************/
/* openGL 相关 */
/***************/

/* 设置openGL默认值 */
void setGLDefaultValues();

/* 混合 */
void setAlphaBlending(bool on);

/* 深度测试 */
void setDepthTest(bool on);

/* 主循环 */
virtual void mainLoop() = 0;

void setContentScaleFactor(float scaleFactor);
float getContentScaleFactor() const { return _contentScaleFactor; }
Scheduler* getScheduler() const { return _scheduler; }
void setScheduler(Scheduler* scheduler);
ActionManager* getActionManager() const { return _actionManager; }
void setActionManager(ActionManager* actionManager);
EventDispatcher* getEventDispatcher() const { return _eventDispatcher; }
void setEventDispatcher(EventDispatcher* dispatcher);
Renderer* getRenderer() const { return _renderer; }

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
Console* getConsole() const { return _console; }
#endif

float getDeltaTime() const;
float getFrameRate() const { return _frameRate; }

protected:
void purgeDirector();
bool _purgeDirectorInNextLoop; // this flag will be set to true in end()

void setNextScene();

void showStats();
void createStatsLabel();
void calculateMPF();
void getFPSImageData(unsigned char** datapointer, ssize_t* length);

void calculateDeltaTime();
void initTextureCache();
void destroyTextureCache();

Scheduler *_scheduler;
ActionManager *_actionManager;
EventDispatcher* _eventDispatcher;
EventCustom *_eventProjectionChanged, *_eventAfterDraw, *_eventAfterVisit, *_eventAfterUpdate;

float _deltaTime;

GLView *_openGLView;

TextureCache *_textureCache;

double _animationInterval;
double _oldAnimationInterval;

bool _landscape;
bool _displayStats;
float _accumDt;
float _frameRate;

LabelAtlas *_FPSLabel;
LabelAtlas *_drawnBatchesLabel;
LabelAtlas *_drawnVerticesLabel;

bool _paused;

unsigned int _totalFrames;
unsigned int _frames;
float _secondsPerFrame;

Scene *_runningScene;
Scene *_nextScene;

bool _sendCleanupToScene;

/* 场景栈 */
Vector<Scene*> _scenesStack;

struct timeval *_lastUpdate;
bool _nextDeltaTimeZero;
Projection _projection;
Size _winSizeInPoints;
float _contentScaleFactor;
Node *_notificationNode;
Renderer *_renderer;

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
Console *_console;
#endif

friend class GLViewProtocol;
};

/* 只支持60,30,15 FPS */
class DisplayLinkDirector : public Director
{
public:
DisplayLinkDirector()
: _invalid(false)
{}

virtual void mainLoop() override;
virtual void setAnimationInterval(double value) override;
virtual void startAnimation() override;
virtual void stopAnimation() override;

protected:
bool _invalid;
};

看了头文件之后,大概就有了一个了解
头文件的注释中表明基于Director的子类或许会有4个,之后由于只需要DisplayLinkDirector,便只实现了这个。

看过头文件之后便把把内容划分成以下几个模块:

Director实例化

先看看Director的实例化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Director* Director::getInstance()
{
if (!s_SharedDirector)
{
s_SharedDirector = new DisplayLinkDirector();
s_SharedDirector->init();
}
return s_SharedDirector;
}

bool Director::init(void)
{
setDefaultValues();

_runningScene = nullptr;
_nextScene = nullptr;

_notificationNode = nullptr;

/* 初始化栈大小 */
_scenesStack.reserve(15);

_accumDt = 0.0f;
_frameRate = 0.0f;
_FPSLabel = _drawnBatchesLabel = _drawnVerticesLabel = nullptr;
_totalFrames = _frames = 0;
_lastUpdate = new struct timeval;

_paused = false;
_purgeDirectorInNextLoop = false;
_winSizeInPoints = Size::ZERO;
_openGLView = nullptr;
_contentScaleFactor = 1.0f;

/* 全局动画管理 */
_scheduler = new Scheduler();
_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);

/* 初始化纹理 */
initTextureCache();

/* 初始化渲染队列 */
_renderer = new Renderer;

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
_console = new Console;
#endif
return true;
}

看到这个实例方法,以及初始化函数init,就渐渐对Cocos2dx是怎么开始的,有了一定的了解。

  • Configuration配置一些相关设置,会在setOpenGLView的时候再次更新。
  • 使用cocos2d::Vector来管理场景栈
  • 初始化全局定时器,全局动坐管理类,全局事件管理类等等。
  • 初始化纹理缓存
  • 初始化渲染队列,Cocos2dx3.0开始,渲染都是放入放入一个渲染队列之中。
  • 其他的就是Director的一些信息的设置啦

Director主循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void DisplayLinkDirector::startAnimation()
{
if (gettimeofday(_lastUpdate, nullptr) != 0)
{
CCLOG("cocos2d: DisplayLinkDirector: Error on gettimeofday");
}

_invalid = false;

Application::getInstance()->setAnimationInterval(_animationInterval);

setNextDeltaTimeZero(true);
}

/* 主循环 */
/* 第一个场景运行之后便每一帧调用 */
void DisplayLinkDirector::mainLoop()
{
/* 是否清理 */
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();

/* 用于清除那些创建出来,但是没有加入到渲染树中 */
PoolManager::getInstance()->getCurrentPool()->clear();
}
}

void DisplayLinkDirector::stopAnimation()
{
_invalid = true;
}

void DisplayLinkDirector::setAnimationInterval(double interval)
{
_animationInterval = interval;
if (! _invalid)
{
stopAnimation();
startAnimation();
}
}
  • 主循环每一帧主要是调用drawScene,主要是计算全局时间间隔,更新全局定时器,全局动作管理,获取输入事件,还有最重要的绘制。
  • 当调用了stopAnimation的时候,即_invalid为false的时候,主循环就什么事情都不干了。

PS:对于PoolManager的相关东西见cocos2dx 3.0 —- AutoreleasePool, PoolManager

Director场景管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
void Director::runWithScene(Scene *scene)
{
CCASSERT(scene != nullptr, "This command can only be used to start the Director. There is already a scene present.");
CCASSERT(_runningScene == nullptr, "_runningScene should be null");

pushScene(scene);
startAnimation();
}

void Director::replaceScene(Scene *scene)
{
CCASSERT(_runningScene, "Use runWithScene: instead to start the director");
CCASSERT(scene != nullptr, "the scene should not be null");

if (_nextScene)
{
if (_nextScene->isRunning())
{
_nextScene->onExitTransitionDidStart();
_nextScene->onExit();
}
_nextScene->cleanup();
_nextScene = nullptr;
}

ssize_t index = _scenesStack.size();

_sendCleanupToScene = true;
_scenesStack.replace(index - 1, scene);

_nextScene = scene;
}

void Director::pushScene(Scene *scene)
void Director::popScene(void)
void Director::popToRootScene(void)
void Director::popToSceneStackLevel(int level)

void Director::end()
{
_purgeDirectorInNextLoop = true;
}

void Director::setNextScene()
{
bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;

// If it is not a transition, call onExit/cleanup
if (! newIsTransition)
{
if (_runningScene)
{
_runningScene->onExitTransitionDidStart();
_runningScene->onExit();
}

// issue #709. the root node (scene) should receive the cleanup message too
// otherwise it might be leaked.
if (_sendCleanupToScene && _runningScene)
{
_runningScene->cleanup();
}
}

if (_runningScene)
{
_runningScene->release();
}
_runningScene = _nextScene;
_nextScene->retain();
_nextScene = nullptr;

if ((! runningIsTransition) && _runningScene)
{
_runningScene->onEnter();
_runningScene->onEnterTransitionDidFinish();
}
}

void Director::pause()
{
if (_paused)
{
return;
}

_oldAnimationInterval = _animationInterval;

setAnimationInterval(1 / 4.0);
_paused = true;
}

void Director::resume()
{
if (! _paused)
{
return;
}

setAnimationInterval(_oldAnimationInterval);

_paused = false;
_deltaTime = 0;

setNextDeltaTimeZero(true);
}
  • 关于runWithScene,由于是开始第一个场景,所以,在入了场景栈之后就开始了Director的主循环。
  • 然后pause,将场景的帧数设置的很低,没有完全停止绘制
  • 然后end只是将一个_purgeDirectorInNextLoop置为true,在下一帧的时候,就执行mainLoop中的清理函数,值得注意的是,清理函数purgeDirector的不会马上释放_scenesStack的内存,主要是因为有可能有人在end之后又调用runWithScene
  • 关于_nextScene_runningScene的关系,在下一帧的时候,_nextScene会被赋值给_runningScene,然后被置空,具体见drawScene和setNextScene

总结

有关于GLView的部分需要另外写一篇文章来讲,这里就不说了,然后Director的代码我也略去了与openGL相关的一些东西,这些东西留作以后补充。


其实看完Director的代码之后,很容易就可以知道从哪里去阅读Cocos2dx源码。

  • 配置文件部分
  • 渲染机制 – Render, RenderCommand…
  • 时间分发机制 – EventDispatcher
  • 动作管理 – ActionManager
  • GLView – openGL相关