0%

[cocos2dx]深入了解几个代表性的类

摘要: 此文对cocos2d-x引擎中最具代表性,最能体现框架结构的几个类做了简单的介绍, 包括Director,Application, Renderer, EventDispatcher, Scheduler. 对于这些类, 也只对关系主要流程的方法做了介绍, 略过了容错代码和其它细节. 主要目的是让大家快速的对cocos2d-x引擎有一个全面笼统的认识, 也方便快速定位问题.


GLView

cocos2d-xopenGL的封装. 不同平台下, openGL有一些差别.

openGL

一段简单的例子

以下内容引用自Introduction to OpenGL. 需要更具体的介绍也可参考这个链接.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# include <whateverYouNeed.h>
main() {
InitializeAWindowPlease();

glClearColor (0.0, 0.0, 0.0, 0.0);
glClear (GL_COLOR_BUFFER_BIT);

glColor3f (1.0, 1.0, 1.0);
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
glBegin(GL_POLYGON);
glVertex3f (0.25, 0.25, 0.0);
glVertex3f (0.75, 0.25, 0.0);
glVertex3f (0.75, 0.75, 0.0);
glVertex3f (0.25, 0.75, 0.0);
glEnd();
glFlush();

UpdateTheWindowAndCheckForEvents();
}

OpenGL Command Syntax

  • OpenGL commands use the prefix gl and initial capital letters for each word making up the command name
  • some seemingly extraneous letters appended to some command names (for example, the 3f in glColor3f() and glVertex3f())

OpenGL as a State Machine

OpenGL is a state machine. You put it into various states (or modes) that then remain in effect until you change them.

Application

主要方法:

1
2
3
4
virtual const char * getCurrentLanguage();
virtual Platform getTargetPlatform();
virtual void setAnimationInterval(double interval);
int run();//启动主循环

run()函数

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
int Application::run()
{

...

while(!glview->windowShouldClose())
{
QueryPerformanceCounter(&nNow);
if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
{
nLast.QuadPart = nNow.QuadPart;

director->mainLoop(); //Director进行这一帧的渲染
glview->pollEvents(); // This function processes only those events that have already been received and then returns immediately.
}
else
{
Sleep(0);
}
}

...

return true;
}

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
//openGL Matrix Operate
void pushMatrix(MATRIX_STACK_TYPE type);
void popMatrix(MATRIX_STACK_TYPE type);
void loadIdentityMatrix(MATRIX_STACK_TYPE type);
void loadMatrix(MATRIX_STACK_TYPE type, const Mat4& mat);
void multiplyMatrix(MATRIX_STACK_TYPE type, const Mat4& mat);
Mat4 getMatrix(MATRIX_STACK_TYPE type);
void resetMatrixStack();

//View Data
inline double getAnimationInterval();
inline bool isDisplayStats();
inline GLView* getOpenGLView();
inline Projection getProjection();
Size getVisibleSize() const;

Vec2 getVisibleOrigin() const;
Vec2 convertToGL(const Vec2& point);
Vec2 convertToUI(const Vec2& point);
float getZEye() const;

// Scene 场景管理
inline Scene* getRunningScene();
void runWithScene(Scene *scene);
void pushScene(Scene *scene);


// 控制绘制的暂停和恢复
void end();
void pause();
void resume();

//绘制图形(界面展示最重要的函数)
void drawScene();

//Getter and Setter
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; }

drawScene(): 主要绘制函数

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
// Draw the Scene
void Director::drawScene()
{
...

if (! _paused)
{
_scheduler->update(_deltaTime); //Scheduler 定时器 更新
_eventDispatcher->dispatchEvent(_eventAfterUpdate); //Dispatcher 抛发事件.
}

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glClear

if (_nextScene) //取得下一个将要显示的Scene.
{
setNextScene();
}

pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); //将上一次绘制的Context放到堆栈

// draw the scene
if (_runningScene)
{
_runningScene->visit(_renderer, Mat4::IDENTITY, false);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}

_renderer->render(); //渲染
_eventDispatcher->dispatchEvent(_eventAfterDraw);

popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); //返回到上一次绘制时的状态.


// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers(); //把上面渲染的结果显示到屏幕
}

...
}

Node::visit() 函数

预览

Node::visit() 的主要功能就是

  1. 调用所有孩子的visit函数
  2. 调用self->draw()函数
    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
    void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
    {
    // quick return if not visible. children won't be drawn.
    if (!_visible)
    {
    return;
    }

    uint32_t flags = processParentFlags(parentTransform, parentFlags);

    // IMPORTANT:
    // To ease the migration to v3.0, we still support the Mat4 stack,
    // but it is deprecated and your code should not rely on it
    Director* director = Director::getInstance();
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);

    int i = 0;


    if(!_children.empty())
    {
    sortAllChildren();
    // draw children zOrder < 0
    for( ; i < _children.size(); i++ )
    {
    auto node = _children.at(i);

    if ( node && node->_localZOrder < 0 )
    node->visit(renderer, _modelViewTransform, flags);
    else
    break;
    }
    // self draw
    this->draw(renderer, _modelViewTransform, flags);

    for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
    (*it)->visit(renderer, _modelViewTransform, flags);
    }
    else
    {
    this->draw(renderer, _modelViewTransform, flags);
    }

    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    }

Node::draw()

因为Node是所有可显示对象的父类, 没有任何显示内容, 所以draw函数为空.
这里我们以Sprite::draw函数为例简单介绍下draw的作用.

1
2
3
4
5
6
7
8
9
10
11
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// Don't do calculate the culling if the transform was not updated
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

if(_insideBounds)
{
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);
renderer->addCommand(&_quadCommand);
}
}

我们看到, Sprite::draw函数主要实现了[添加一个QuadCommandRender中去]的功能.
再看看Label的绘制函数.

Label::draw

1
2
3
4
5
6
7
8
9
10
11
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// Don't do calculate the culling if the transform was not updated
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

if(_insideBounds) {
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, flags);
renderer->addCommand(&_customCommand);
}
}

其实, 跟Sprite::draw也差不多. 关键在于这个RenderCommand怎么构造和执行的.

Renderer 渲染器

主要函数预览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void initGLView();

/** Adds a `RenderComamnd` into the renderer */
void addCommand(RenderCommand* command);

/** Adds a `RenderComamnd` into the renderer specifying a particular render queue ID */
void addCommand(RenderCommand* command, int renderQueue);

/** Pushes a group into the render queue */
void pushGroup(int renderQueueID);

/** Pops a group from the render queue */
void popGroup();

/** Creates a render queue and returns its Id */
int createRenderQueue();

/** Renders into the GLView all the queued `RenderCommand` objects */
void render();

可见它主要由两个功能:

  1. ReanderCommand进行排序和分类管理
  2. 进行渲染:render()

渲染函数Renderer::render()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Renderer::render()
{
...

if (_glViewAssigned)
{
...
//排列渲染队列
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort();
}
//进行渲染
visitRenderQueue(_renderGroups[0]);
...
}
...
}

Renderer::visitRenderQueue

按照顺序执行所有的 RenderCommand

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
void Renderer::visitRenderQueue(const RenderQueue& queue)
{
ssize_t size = queue.size();

for (ssize_t index = 0; index < size; ++index)
{
auto command = queue[index];
auto commandType = command->getType();
if(RenderCommand::Type::QUAD_COMMAND == commandType)
{
auto cmd = static_cast<QuadCommand*>(command);
//Batch quads
if(_numQuads + cmd->getQuadCount() > VBO_SIZE)
{
drawBatchedQuads();
}

_batchedQuadCommands.push_back(cmd);

memcpy(_quads + _numQuads, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());
convertToWorldCoordinates(_quads + _numQuads, cmd->getQuadCount(), cmd->getModelView());

_numQuads += cmd->getQuadCount();

}
else if(RenderCommand::Type::GROUP_COMMAND == commandType)
{
flush();
int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
}
else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
{
...
}
...
}
}

openGL VAO, VBO 介绍.

GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形
OpenGL 4.0 VAO VBO 理解

Schelduler介绍

Scheldulercocos2d-x中实现延迟调用,定时调用时最重要的功能. 类似于其他语言中的Timer
他最核心的函数就是:

1
void schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key);

用来启动一个定时操作: 在延迟delay时间后, 每隔repeat时间, 调用一次callback. target用来标记这个操作属于谁, 方便管理, 比如在析构的时候调用void unschedule(void *target)即可移除当前对象的所有定时操作.

Schelduler的其它大部分方法, 要么是它的衍生, 为了减少调用参数; 要么是对定时操作的控制, 比如暂停, 恢复, 移除等. 如果只对想对框架的各个模块有大概的了解, 可以不做深入.

EventDispatcher

(后续添加)