上一講我們講了爲什麼調用了cc.game.run後,會執行cc.game.onStart函數。這一講,我們通過分析一下 CCDirector.js的代碼,來了解一下游戲是怎麼渲染的。
上一講中,我們看到,在cc.game.prepare方法裏面,在加載引擎代之後,執行了 cc.game._setAnimFrame()和cc.game._runMainLoop()。
接下來我們看下這兩個函數到底做了什麼。
// @Time ticker section
// 設置幀率
_setAnimFrame: function () {
// 預計下一幀執行後的時間
this._lastTime = new Date();
// 幀率
var frameRate = cc.game.config[cc.game.CONFIG_KEY.frameRate];
// 每幀的時間
this._frameTime = 1000 / frameRate;
// 如果幀率不是60或30的時候,使用window.setTimeout來控制執行頻率
if (frameRate !== 60 && frameRate !== 30) {
window.requestAnimFrame = this._stTime;
window.cancelAnimationFrame = this._ctTime;
} else {
// 60或者30幀的時候,使用默認的,30時通過 skip 跳過一幀的方式
// 使用默認比setTimeout速度快
// 疑問 不是30幀的時候,爲什麼不使用跳過的方式來實現?
window.requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
this._stTime;
window.cancelAnimationFrame = window.cancelAnimationFrame ||
window.cancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.msCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.oCancelAnimationFrame ||
this._ctTime;
}
},
// 定時函數
_stTime: function (callback) {
// 當前時間戳
var currTime = new Date().getTime();
// cc.game._fameTime 值爲 1000 / 幀率 單位爲毫秒
// cc.game._lastTime 值爲上次預計 執行到這裏的時間戳
// timeToCall下次執行callback間隔時間,正常時每幀的時間cc.game._frameTime
// currTime-cc.game._lastTime 就是預計和實際時間的差值
// 如果性能不好,currTime - cc.game._lastTime大於0,那下一幀的時間就要少一些
var timeToCall = Math.max(0, cc.game._frameTime - (currTime - cc.game._lastTime));
var id = window.setTimeout(function () {
callback();
},
timeToCall);
// 下一幀執行的預計時間,如果性能好的話,下次執行到_stTime函數時,這個值等你
cc.game._lastTime = currTime + timeToCall;
return id;
},
// 取消定時
_ctTime: function (id) {
window.clearTimeout(id);
},
通過上面的代碼我們可以看到,如果幀率爲60或30的時候,使用window.requestAnimationFrame的方式來定時執行函數。其他值時,使用window.setTimeout來實現。其中使用window.setTimeout的時候,通過記錄預計下次執行的時間 cc.game._lastTime的值,來動態計算下次執行 回調的時間。
接下來看下
// 運行主循環
_runMainLoop: function () {
var self = this,
callback, config = self.config,
// projection.json的值
CONFIG_KEY = self.CONFIG_KEY,
// 導演
director = cc.director,
// 是否跳過渲染。幀率爲30幀的時候 用
skip = true,
// 幀率
frameRate = config[CONFIG_KEY.frameRate];
// 設置FPS顯示狀態 爲true的時候,左下角顯示幀率等信息
director.setDisplayStats(config[CONFIG_KEY.showFPS]);
// 每幀回調的函數
callback = function () {
// 沒有暫停的時候,繼續執行
if (!self._paused) {
// 如果幀率是30
if (frameRate === 30) {
// 因爲 幀率是30和60的時候,調用的是window.requestAnimationFrame,默認是60幀 (原因見cc.game._setAnimFrame方法)
// 所以每隔一幀跳過不執行
if (skip = !skip) {
self._intervalId = window.requestAnimFrame(callback);
return;
}
}
// 每幀執行導演類的 主循環
director.mainLoop();
self._intervalId = window.requestAnimFrame(callback);
}
};
// 間隔id,可通過該值來取消要執行的函數
self._intervalId = window.requestAnimFrame(callback);
// 是否暫停
self._paused = false;
},
可以看出,在cc.game._runMainLoop裏面,通過調用window.requestAnimFrame來實現循環執行callback。在callback裏面調用director.mainLoop(),來實現每一幀的更新。
下一講,我們將通過分析CCDirector.js這個類來了解整個遊戲渲染的流程