深入了解Cocos2d-x中几个代表性的类

人前些日子看到一篇关于cocos2d-x的典型类的介绍的文章,感觉甚好,今日特整理转来,希望能帮助到一些初学者~


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


GLView

Cocos2d-x对openGL的封装。不同平台下,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指令语法

* OpenGL使用前缀gl加每个单词的首字母组成一个指令名称

* 一些看似无关的字母附加在指令名称中(如,glColor3f()和glVertex3f()中的3f)


OpenGL是一个状态机

OpenGL是一个状态机,它保持自身的状态,除非用户输入一条命令让它改变状态。


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
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
//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
// 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函数主要实现了[添加一个QuadCommand到Render中去]的功能。


再看看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)
         {
             ...
         }
         ...
     }
}


Schelduler介绍

Schelduler是Cocos2d-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

EventDispatcher,EventListener,Event之间的关系

1. EventDispatcher: 事件分发器, 相当于所有事件的中控中心。管理着EventListener,当一个Event到来的时候决定CallBack的调用顺序。

2. Event ( EventTouch, EventKeyboard 等), 具体的事件数据。

3. EventListener ( EventListenerTouch, EventListenerKeyboard 等 ): 建立了Event到CallBack的映射关系, EventDispatcher 根据这种映射关系调用对应的 CallBack。


Event

Event有以下几种类型:

1
2
3
4
5
6
7
8
9
enum  class  Type
     {
         TOUCH,
         KEYBOARD,
         ACCELERATION,
         MOUSE,
         FOCUS,
         CUSTOM
     };

Event最重要的属性就是type, 标识了它是那种类型的事件, 也决定了由哪个EventListner来处理它。


EventListener

EventListner有以下几种类型:

1
2
3
4
5
6
7
8
9
10
11
enum  class  Type
     {
         UNKNOWN,
         TOUCH_ONE_BY_ONE,
         TOUCH_ALL_AT_ONCE,
         KEYBOARD,
         MOUSE,
         ACCELERATION,
         FOCUS,
         CUSTOM
     };

    

除了UNKNOWN, 跟Event::Type相比,Event::Type::TOUCH会同时被两种类型的EventListener处理: TOUCH_ONE_BY_ONE和TOUCH_ALL_AT_ONCE. 这两种EventListener分别处理单点触摸事件和多点触摸事件。多说几句: 假如一个TouchEvent事件中有多个触摸点, 那么类型为 EventListener::Type::TOUCH_ONE_BY_ONE 的 EventListener 会把这个事件分解成若干个单点触摸事件来处理。而类型为 EventListener::Type::TOUCH_ALL_AT_ONCE 的 EventListener 就是来处理多点触摸的, 会一次处理它。


其它几种类型都是一一对应的, 即一种Event::Type的Event会被对应类型的EventListener接受。


存放 EventListener 的地方

在EventDispatcher中, 它把以上7种 EventListener::Type 类型的 EventListner 放到7个队列中。也就是在这样一个字段中:

1
std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;


* EventListener::ListenerID : 每一种EventListener::Type有唯一的 EventListener::ListenerID. 其实通过这段代码 typedef std::string ListenerID; 可知: EventListener::ListenerID 就是简单 string, 就是一个名称而已。

* EventListenerVector: 顾名思义, 就是一个 EventListener 的向量容器. 相对于普通的向量容器, 它增加了priority管理功能。


EventListener的fixedPriority

简单来说, 每个 EventListener 有自己的 fixedPriority 属性, 它是一个整数。


EventListener的遍历顺序

EventDispatcher 在抛发事件的时候, 会先处理 Event 的时候, 会优先遍历 fixedPriority 低的 EventListener, 调用它的 CallBack。在某些条件下, 一个 Event 被一个 EventListener 处理之后, 会停止遍历其它的 EventListener。反映到实战中就是: 你监听了某种事件, 这种事件也出发了, 但是对应的回调函数并没有被调用, 也就是被优先级更高的 EventListener 截获了。


如果 fixedPriority 一样呢? 按照什么顺序?

1. fixedPriority 为0, 这个值是专门为 Scene Object 预留的。即, 默认情况下, 绝大多数继承自 Node 的对象添加的普通事件监听器, 其 fixedPriority 都为0。此时, Node 的 globalZOrder决定了优先级, 值越大, 越先被遍历到, 即在显示层中层级越高, 越先接受事件。 这在UI响应逻辑中也是合理的。


2. fixedPriority 不为0, 那就按添加顺序。


Event在什么条件下会被优先级更高的EventListener截获?

对于 EventListenerTouchOneByOne, 它有一个字段: _needSwallow, 当它为 true 的时候, 如果它接受了某个 Event, 优先级更低的 EventListener 就接受不到了. 可以用 EventListenerTouchOneByOne::setSwallowTouches(bool needSwallow) 来改变它。


对于其它类型的 EventLIstener, 只有在显示调用了 Event::stopPropagation()的时候, 才会中断遍历。


核心函数: EventDispatcher::dispatchEvent()

下面我们看看EventDispatcher最核心的函数: 

void EventDispatcher::dispatchEvent(Event* event): 当有响应的事件到来的时候, 都会调用这个函数来通知监听了此事件的对象。


其实, 上面的介绍, 已经把这个函数里绝大部分逻辑都描述了,这里做一个最后的总结。


事件抛发的简要流程如下:

1. 检查 _listenerMap 中所有的EventListnerVector, 如果哪个容器的 EventListener 优先级顺序需要更新, 则重新排序。

2. 对于类型为 Event::Type::TOUCH 的事件, 则按照EventListener的遍历顺序遍历所有的 EventListener. 只有接受了 EventTouch::EventCode::BEGAN 事件的 EventListener, 才会收到其他类型的 EventTouch 事件。

3. 对于其他类型的事件, 也按照EventListener的遍历顺序的顺序遍历对应的EventListener。


总结

Eventdispatcher 中的其它函数, 主要功能都是 添加EventListener, 删除EventListener等, 不做详细介绍。


总的来说, Eventdispatcher 是一个中转器:

1. 事件的产生模块儿, 只关心自己构造正确的 Event, 调用 EventDispatcher::dispatchEvent(Event* event) 交给 EventDispatcher。

2. 需要监听事件的模块儿, 只需调用 EventDispatcher::addEventListener(EventListener* listener) (或者它的其它变种)来注册自己作为监听者。

3. 而 EventDispatcher 的作用是: 

   1)把特定类型的 Event 送给对应类型的 EventListener。

   2)对于同一种 Event, 规定了事件送达的优先级。


来源网址:http://www.cnblogs.com/jhzhu/p/3859964.html#opengl-vao-vbo-介绍


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值