cocos2d-js schedule定時器誤差解決辦法

 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瀏覽器運行部分截圖:

timepass.jpg

    可以發現隨着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;
        }
    }
});

下面是相同環境下運行結果:

better.png

    誤差一直保持在11-13ms之間,並沒有變慢,而且執行次數也和理論次數一致!優化定時器成功!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章