cocos2d-js提供的定時器是有誤差的,這會導致遊戲的一些邏輯判斷出現問題。下面的代碼是一個實驗,驗證一下schedule循環執行的時間誤差:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var deviationTestLayer = cc.Layer.extend({ ctor: function (){ this ._super(); //獲取初始時間 var startTime = new Date().getTime(); var count = 0; //執行定時任務 沒0.1s執行一次 this .schedule( function (){ var timePass = new Date().getTime() - startTime; count++; var delta = timePass - count*100; console.log( "timePass=" +timePass + ", total delta=" +delta+ ", count=" +count); },0.1); } }); |
上面的代碼定時器回調函數記錄了當前運行時間timePass,誤差delta,運行次數count。
下面是在chrome瀏覽器運行部分截圖:
可以發現隨着count越來越大,delta誤差(和當前運行環境,機器性能,頻幀有關)也越來越大。理論上應該執行159次,實際執行了152次!
Cocos2d-js自帶的定時器爲什麼會越走越慢,究其原因,其實schedule是受頻幀驅動的,Cocos2d-js框架在每幀的計算中都會遍歷全部的定時器,如果此時某個定時器到了觸發的時間,則觸發這個定時器,框架忽略了定時器週期和幀週期的差距問題,這會導致多次循環之後誤差越來越大,甚至漏執行。在一般的遊戲開發中,也許沒什麼影響,那如果在對時間要求很高的遊戲中,那如何解決這個問題呢,下面我們來一起優化一下Cocos2d-Js的定時器,實現一個不變慢定時器,請看代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mySchedule: function (callbanck,interval){ var then = Date.now(); console.log(then); //用戶設定的時間間隔,單位:秒(S) interval = interval*1000; //bind 傳參數this this .schedule( function (){ var now = Date.now(); var delta = now - then; if (delta>interval){ then = now - (delta%interval); callbanck.call( this ); } }.bind( this ),0); //此處的0表示每幀都觸發 } |
mySchedule和cocos2d-js提供的schedule使用方法一樣,實現依賴於schedule方法,mySchedule讓schedule每幀都觸發,也就是遊戲的最小時間間隔。在每幀觸發的時候,判斷當前時間和上一次的時間間隔delta,如果delta超過了用戶設定的interval值,則觸發用戶的回調函數。
其中代碼 then = now - (delta%interval)是實現不變慢定時器的關鍵點,在cocos2d-js中是用then = now。
舉個例子說明:假設用戶設定interval = 0.1s 也就是100ms觸發一次,頻幀60fps,每16ms觸發一次mySchedule的計算,計算可得16x7=112>100,也就是說,需要112ms才實際出發了用戶設定的回調函數,比設定的100ms晚了12ms。而delta%interval是本次出發的誤差,爲了解決誤差就要讓then = now - (delta%interval),來達到抵消誤差目的。
測試一下新的定時器,是否能達到我們想要的效果:
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
|
var deviationTestLayer = cc.Layer.extend({ ctor: function (){ this ._super(); //獲取初始時間 var startTime = new Date().getTime(); var count = 0; //執行定時任務 沒0.1s執行一次 this .mySchedule( function (){ var timePass = new Date().getTime() - startTime; count++; var delta = timePass - count*100; console.log( "timePass=" +timePass + ", total delta=" +delta+ ", count=" +count); },0.1); this .scheduleUpdate(); }, mySchedule: function (callbanck,interval){ var then = Date.now(); console.log(then); interval = interval*1000; //bind 傳參數this this .schedule( function (){ var now = Date.now(); var delta = now - then; if (delta>interval){ then = now - (delta%interval); callbanck.call( this ); } }.bind( this ),0); //此處的0表示每幀都觸發 }, //在update函數中做大量計算,模擬低頻幀的情況 update: function (){ for ( var i=0; i<1000000; i++){ var b = 1/0.22222; } } }); |
下面是相同環境下運行結果:
誤差一直保持在11-13ms之間,並沒有變慢,而且執行次數也和理論次數一致!優化定時器成功!!