var macro = require('../../platform/CCMacro');
const PerfCounter = require('./perf-counter');
/* 是否展示FPS变量 */
let _showFPS = false;
/* 字体大小15 */
let _fontSize = 15;
let _stats = null;
let _rootNode = null;
let _label = null;
function generateStats() {
if (_stats) return;
_stats = {
/* 每帧的时间;如果你游戏帧率是60fps,,则每帧时间为1/60=0.01666,就是16ms; */
frame: { desc: 'Frame time (ms)', min: 0, max: 50, average: 500, color: '#FF0' },
/* Framerate(FPS): 帧率是以帧称为单位的位图图像连续出现在显示器上的频率,物质在1s内完成周期性变化的次数叫做频率,常用f表示 f=1/T;
一秒展示次数越多,看起来越流畅 */
fps: { desc: 'Framerate (FPS)', below: 30, average: 500, color: '#000' },
/*
是一种行为(指令),即 CPU 调用图形 API,命令 GPU 进行图形绘制,可以了理解为渲染调用次数;详细可以参考https://www.jianshu.com/p/c0f4598cbb20
*/
draws: { desc: 'Draw call', color: '#000' },
/* 游戏逻辑耗时 */
logic: { desc: 'Game Logic (ms)', min: 0, max: 50, average: 500, color: '#000' },
/* 渲染耗时 */
render: { desc: 'Renderer (ms)', min: 0, max: 50, average: 500, color: '#000' },
/* 渲染方式:webgl或者canvas */
mode: { desc: cc.game.renderType === cc.game.RENDER_TYPE_WEBGL ? 'WebGL' : 'Canvas', min: 1 }
};
let now = performance.now();
/* performance.now() 返回的时间戳以毫秒为单位,通常具有微秒级或纳秒级的精确度。它可以用于测量非常小的时间间隔,帮助我们了解代码的性能和优化需求
*/
for (let id in _stats) {
_stats[id]._counter = new PerfCounter(id, _stats[id], now);
}
}
function generateNode() {
/* 假如已经存在rootnode,则返回 */
if (_rootNode && _rootNode.isValid) return;
/* 展示监控信息的总node */
_rootNode = new cc.Node('PROFILER-NODE');
_rootNode.x = _rootNode.y = 10;
//枚举数值31
// 节点的分组索引
_rootNode.groupIndex = cc.Node.BuiltinGroupIndex.DEBUG;
cc.Camera._setupDebugCamera();
_rootNode.zIndex = macro.MAX_ZINDEX;//Math.pow(2, 15) - 1,
/* 常驻根节点,不会被销毁 */
cc.game.addPersistRootNode(_rootNode);
/* 创建左侧node */
let left = new cc.Node('LEFT-PANEL');
/* 锚点0,0 */
left.anchorX = left.anchorY = 0;
/* 添加label的component, */
let leftLabel = left.addComponent(cc.Label);
/* 设置字体大小 */
leftLabel.fontSize = _fontSize;
/* 行间距 */
leftLabel.lineHeight = _fontSize;
/* 设置父节点,addchild结果一样 */
left.parent = _rootNode;
/* 创建右侧node */
let right = new cc.Node('RIGHT-PANEL');
right.anchorX = 1;
right.anchorY = 0;
right.x = 200;
let rightLabel = right.addComponent(cc.Label);
rightLabel.horizontalAlign = cc.Label.HorizontalAlign.RIGHT;
rightLabel.fontSize = _fontSize;
rightLabel.lineHeight = _fontSize;
right.parent = _rootNode;
/* 当平台信息不是百度和微信,把左右侧label的模式设置为char模式 */
if (cc.sys.platform !== cc.sys.BAIDU_GAME_SUB &&
cc.sys.platform !== cc.sys.WECHAT_GAME_SUB) {
leftLabel.cacheMode = cc.Label.CacheMode.CHAR;
rightLabel.cacheMode = cc.Label.CacheMode.CHAR;
}
_label = {
left: leftLabel,
right: rightLabel
};
}
function beforeUpdate() {
generateNode();//update之前判断根节点是否已有
let now = cc.director._lastUpdate;
/* 帧率和逻辑开启计时 */
_stats['frame']._counter.start(now);
_stats['logic']._counter.start(now);
}
function afterUpdate() {
let now = performance.now();
if (cc.director.isPaused()) {
/* 暂停了,counter 重置 */
_stats['frame']._counter.start(now);
}
else {
/* 添加时间样本 */
_stats['logic']._counter.end(now);
}
_stats['render']._counter.start(now);
}
/* 没搜到有调用这个方法, */
function updateLabel(stat) {
let length = 20;
let desc = stat.desc;
let value = stat._counter.human() + '';
stat.label.string = stat.desc + ' ' + stat._counter.human();
}
/* 这是个监听回调函数,在releaseManager的tryRelease 和 CCGame的restart都有调用,
这两个变化,主要是在于场景更新后,这些属性要及时更新 */
function afterDraw() {
/* 渲染后 */
let now = performance.now();
_stats['render']._counter.end(now);
_stats['draws']._counter.value = cc.renderer.drawCalls;
_stats['frame']._counter.end(now);
_stats['fps']._counter.frame(now);
let left = '';
let right = '';
/* 遍历object属性 */
for (let id in _stats) {
let stat = _stats[id];//获取一个object,
stat._counter.sample(now);//计算平均值
left += stat.desc + '\n';
right += stat._counter.human() + '\n';
}
/* 对象下的两个label,设置string 和颜色更新 */
if (_label) {
_label.left.string = left;
_label.left.node.color = new cc.Color(255, 0, 0);//red
_label.right.string = right;
_label.right.node.color = new cc.Color(255, 255, 0);//yellow
}
}
cc.profiler = module.exports = {
/* 判断当前状态是否是展示的 */
isShowingStats() {
return _showFPS;
},
hideStats() {
/* 关闭性能参数的展示,设置为隐藏,去掉监听 */
if (_showFPS) {
if (_rootNode) {
_rootNode.active = false;
}
/* 去掉三个监听 */
cc.director.off(cc.Director.EVENT_BEFORE_UPDATE, beforeUpdate);
cc.director.off(cc.Director.EVENT_AFTER_UPDATE, afterUpdate);
cc.director.off(cc.Director.EVENT_AFTER_DRAW, afterDraw);
_showFPS = false;
}
},
/* 展示性能label,添加监听 */
showStats() {
if (!_showFPS) {
generateStats();
if (_rootNode) {
_rootNode.active = true;
}
/* 添加三个监听行数 */
cc.director.on(cc.Director.EVENT_BEFORE_UPDATE, beforeUpdate);
/* 导演类CCDirector 执行mainloop后,调用 */
cc.director.on(cc.Director.EVENT_AFTER_UPDATE, afterUpdate);
/* /* 这是个监听回调函数,在releaseManager的tryRelease 和 CCGame的restart都有调用,
这两个变化,主要是在于场景更新后,这些属性要及时更新 */ */
cc.director.on(cc.Director.EVENT_AFTER_DRAW, afterDraw);
_showFPS = true;
}
}
}