本人前些日子看到一篇关于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-介绍