很多引擎內(nèi)部都封裝了自己的Update函數(shù)渗磅,比如Unity的Update生命周期,并傳遞了一個(gè)deltaTime检访,如果還有人不太了解deltaTime的作用的話始鱼,這里做下解釋:首先deltaTime是引擎運(yùn)行上一幀所花費(fèi)的時(shí)間(以秒為單位),為什么需要這么個(gè)東西脆贵?因?yàn)橐娴拿恳粠倪\(yùn)行都不能確保運(yùn)行的時(shí)間都能相同(硬件與軟件層面的限制都可能對其影響且不同機(jī)器的運(yùn)行效率也不同)医清,我們以用戶機(jī)器的不同運(yùn)行效率舉例,當(dāng)我們把所有速度去乘以deltaTime值時(shí)結(jié)果會是:如果乘上的deltaTime很大卖氨,就意味著上一幀的渲染花費(fèi)了更多時(shí)間会烙,所以這一幀的速度需要變得更高來平衡渲染所花去的時(shí)間。使用這種方法時(shí)筒捺,無論你的電腦快還是慢柏腻,速度都會相應(yīng)平衡,這樣每個(gè)用戶的體驗(yàn)就都一樣了系吭。那么Update函數(shù)內(nèi)部又如何實(shí)現(xiàn)的呢五嫂?
因?yàn)樽罱诖罱╓ebgl版的3D引擎,所以以js相關(guān)api舉例肯尺。通過思維導(dǎo)圖沃缘,我們一步步分析,首先定義isActive來決定當(dāng)前是否重復(fù)執(zhí)行Update函數(shù)则吟,msLastFrame代表上一幀的開始時(shí)間(毫秒為單位), 通過requestAnimationFrame來開啟幀運(yùn)行run函數(shù)槐臀,run函數(shù)中通過是否手動設(shè)置fps來區(qū)分run的運(yùn)行邏輯,當(dāng)手動設(shè)置了fps時(shí)氓仲,通過msFpsLimit = 1000/fps來記錄一秒內(nèi)每運(yùn)行一幀所花費(fèi)的毫秒數(shù)水慨,通過msCurrent = performance.now()來記錄這一幀開始的時(shí)間(確切的說它是一個(gè)時(shí)間戳)得糜,再通過msDelta = (msCurrent - msLastFrame)來記錄上一幀結(jié)束的毫秒數(shù)時(shí)間間隔,再計(jì)算deltaTime = msDelta / 1000.0把毫秒數(shù)間隔規(guī)范化為秒數(shù)間隔。通過判斷msDelta >= msFpsLimit來判定上一幀經(jīng)歷的時(shí)間是否超過規(guī)定的時(shí)間間隔讥巡,如果是的話就先計(jì)算fps = Math.floor(1/deltaTime)代表一秒內(nèi)總共又多少幀數(shù)掀亩,再重置msLastFrame = msCurrent,調(diào)用Update回調(diào)欢顷,傳遞deltaTime槽棍,最后判斷isActive是否處于激活狀態(tài),如果是抬驴,繼續(xù)重復(fù)執(zhí)行run函數(shù)炼七。如果不設(shè)置fps那就不需要對比上一幀經(jīng)過的時(shí)間與規(guī)定時(shí)間的大小。
示例代碼如下:
class RenderLoop {
constructor(callback,fps) {
this.msLastFrame = null;
this.callback = callback;
this.isActive = false;
this.fps = 0;
if(fps && fps > 0) {
this.msFpsLimit = 1000/fps;
this.run = ()=> {
let msCurrent = performance.now(),
msDelta = (msCurrent - this.msLastFrame),
deltaTime = msDelta/1000.0;
if(msDelta >= this.msFpsLimit) {
this.fps = Math.floor(1/deltaTime);
this.msLastFrame = msCurrent;
this.callback(deltaTime);
}
if(this.isActive) window.requestAnimationFrame(this.run);
}
} else {
this.run = ()=> {
let msCurrent = performance.now(),
deltaTime = (msCurrent - this.msLastFrame) / 1000.0;
this.fps = Math.floor(1/deltaTime);
this.msLastFrame = msCurrent;
this.callback(deltaTime);
if(this.isActive) window.requestAnimationFrame(this.run);
}
}
}
start() {
this.isActive = true;
this.msLastFrame = performance.now();
window.requestAnimationFrame(this.run);
return this;
}
stop() {
this.isActive = false;
}
}