文章目录
一、前言
嗨,大家好,我是思航。今天打算分享一下 cocos2d-x-html的渲染流程。
二、流程图
三、源码解析
1、CCDirector.js (导演类)
导演类,控制游戏每帧执行的逻辑
1.1 drawScene 方法 (绘画场景)
drawScene: function () {
var renderer = cc.renderer;
...
// draw the scene
if (this._runningScene) {
// 需要更新渲染命令队列
if (renderer.childrenOrderDirty) {
// 清空渲染命令队列
cc.renderer.clearRenderCommands();
cc.renderer.assignedZ = 0;
this._runningScene._renderCmd._curLevel = 0; //level start from 0;
// 调用visit,重新生成 渲染命令队列
this._runningScene.visit();
// 重置标识 childrenOrderDirty改为 false
renderer.resetFlag();
} else if (renderer.transformDirty()) {
renderer.transform();
}
}
// 清空画布
renderer.clear();
...
// 根据渲染命令队列,重新渲染画布
renderer.rendering(cc._renderContext);
},
2、RendererWebGL.js (渲染类)
渲染类,其成员变量 _renderCmds 为存放渲染命令的队列。下面介绍几个使用的到方法。
2.1 clearRenderCommands 方法 (清空渲染命令队列)
clearRenderCommands: function () {
// Copy previous command list for late check in rendering
this._renderCmds.length = 0;
},
2.2 rendering 方法 (根据渲染命令队列,重新渲染画布)
通过遍历渲染命令队列,执行每个渲染命令的_uploadBufferData,然后进行 _batchRendering
rendering: function (ctx, cmds) {
var locCmds = cmds || this._renderCmds,
i, len, cmd,
context = ctx || cc._renderContext;
// Reset buffer for rendering
context.bindBuffer(gl.ARRAY_BUFFER, null);
for (i = 0, len = locCmds.length; i < len; ++i) {
cmd = locCmds[i];
if (!cmd.needDraw()) continue;
if (cmd.uploadData) {
this._uploadBufferData(cmd);
}
else {
if (_batchingSize > 0) {
this._batchRendering();
}
cmd.rendering(context);
}
}
this._batchRendering();
_batchedInfo.texture = null;
}
2.3 _uploadBufferData 方法 (处理渲染命令的数据)
_uploadBufferData: function (cmd) {
// 当前批次大小大于最大,进行一次渲染
if (_batchingSize >= _maxVertexSize) {
this._batchRendering();
}
// Check batching
var node = cmd._node;
var texture = node._texture || (node._spriteFrame && node._spriteFrame._texture);
var blendSrc = node._blendFunc.src;
var blendDst = node._blendFunc.dst;
var glProgramState = cmd._glProgramState;
if (_batchBroken ||
_batchedInfo.texture !== texture ||
_batchedInfo.blendSrc !== blendSrc ||
_batchedInfo.blendDst !== blendDst ||
_batchedInfo.glProgramState !== glProgramState) {
// Draw batched elements
// 如果不能合并,直接执行渲染(调用一次,一个drawcall)
this._batchRendering();
// Update _batchedInfo
_batchedInfo.texture = texture;
_batchedInfo.blendSrc = blendSrc;
_batchedInfo.blendDst = blendDst;
_batchedInfo.glProgramState = glProgramState;
_batchBroken = false;
}
// Upload vertex data
// 执行渲染命令的uploadData
var len = cmd.uploadData(_vertexDataF32, _vertexDataUI32, _batchingSize * _sizePerVertex);
if (len > 0) {
this._increaseBatchingSize(len, cmd.vertexType);
}
},
3、CCNode.js (节点类)
节点类,有很多方法,我们这边重点看下 visit 方法
3.1 visit 方法 (递归子节点的visit方法,把对应的节点渲染命令添加到 渲染器)
visit: function (parent) {
var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null;
// quick return if not visible
if (!this._visible) {
cmd._propagateFlagsDown(parentCmd);
return;
}
var renderer = cc.renderer;
// 同步父节点的Dirty状态,这里先不细介绍
cmd.visit(parentCmd);
var i, children = this._children, len = children.length, child;
if (len > 0) {
if (this._reorderChildDirty) {
this.sortAllChildren();
}
// 先执行 zOrder < 0 的,这边可以先忽略
// draw children zOrder < 0
for (i = 0; i < len; i++) {
child = children[i];
if (child._localZOrder < 0) {
child.visit(this);
}
else {
break;
}
}
// 把节点对应的渲染命令压入到 渲染队列里面
renderer.pushRenderCommand(cmd);
for (; i < len; i++) {
// 在执行子节点的visit,这样就会吧子节点的渲染命令也压进入
children[i].visit(this);
}
} else {
renderer.pushRenderCommand(cmd);
}
cmd._dirtyFlag = 0;
},
4、CCNodeWebGLRenderCmd.js (节点渲染命令)
4.1 uploadData 方法 (把数据传到渲染类)
这里我们看下 CCSpriteWebGLRenderCmd.js 里面的uploadData方法
// 参数为RendererWebGL.js的变量,把渲染命令的数据修改到里面
// 这里具体数据不展开介绍,后面另开文章专门介绍
proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) {
var node = this._node, locTexture = node._texture;
if (!(locTexture && locTexture._textureLoaded && node._rect.width && node._rect.height) || !this._displayedOpacity)
return 0;
// Fill in vertex data with quad information (4 vertices for sprite)
var opacity = this._displayedOpacity;
var r = this._displayedColor.r,
g = this._displayedColor.g,
b = this._displayedColor.b;
if (node._opacityModifyRGB) {
var a = opacity / 255;
r *= a;
g *= a;
b *= a;
}
this._color[0] = ((opacity << 24) | (b << 16) | (g << 8) | r);
var z = node._vertexZ;
var vertices = this._vertices;
var i, len = vertices.length, vertex, offset = vertexDataOffset;
for (i = 0; i < len; ++i) {
vertex = vertices[i];
f32buffer[offset] = vertex.x;
f32buffer[offset + 1] = vertex.y;
f32buffer[offset + 2] = z;
ui32buffer[offset + 3] = this._color[0];
f32buffer[offset + 4] = vertex.u;
f32buffer[offset + 5] = vertex.v;
offset += 6;
}
return len;
};
四、结束语
通过上面的介绍,我们可以了解到游戏渲染的整体流程。这边节点使用了CCNode.js来介绍,这只是个基类,具体的每个控件以及渲染命令都不一样,这个后面课程继续介绍。今天就到这里, 下次再见!