摘要
在游戲開發中,游戲速率控制一直是一個需求,官方提供了計時器的控制介面以及動作系統的 cc.speed,但是使用起來不是很方便且無法影響到 update 控制邏輯以及物理系統,那么如何實作這一需求呢?
正文
使用版本
CocosCreator 版本 2.3.4
思維程序
想問題還是要去根上找,跑到原始碼里先看看官方實作的計時器控制邏輯是怎么樣的?在 CCSchedule.js 中可以看到有這樣一個方法:
setTimeScale: function (timeScale) {
this._timeScale = timeScale;
}
這個私有屬性是如何控制速率的呢?一番尋找,是在 update 中進行了計算:
update: function (dt) {
this._updateHashLocked = true;
if(this._timeScale !== 1)
dt *= this._timeScale;
var i, list, len, entry;
//......
}
這樣就明白了,實際上就是把被計時器控制的組件的 dt 時間給改了,那我們想實作全域的控制應該再往根源處尋找,
導演類控制
正常講游戲回圈是每秒 60 幀,那么每幀的主回圈邏輯應該不是在 CCGame.js 就是在 CCDirector.js 中,果然在導演類中看到了 mainLoop 方法,而其中有這么一段代碼(省略了無關代碼):
// calculate "global" dt
this.calculateDeltaTime(now);
// Update
if (!this._paused) {
// before update
this.emit(cc.Director.EVENT_BEFORE_UPDATE);
// Call start for new added components
this._compScheduler.startPhase();
// Update for components
this._compScheduler.updatePhase(this._deltaTime);
// Engine update with scheduler
this._scheduler.update(this._deltaTime);
// Late update for components
this._compScheduler.lateUpdatePhase(this._deltaTime);
// After life-cycle executed
this._compScheduler.clearup();
// User can use this event to do things after update
this.emit(cc.Director.EVENT_AFTER_UPDATE);
// Destroy entities that have been removed recently
Obj._deferredDestroy();
}
在沒暫停的情況,計算完 dt 后分發下去,那我們在 this.calculateDeltaTime(now) 方法里面把 this._deltaTime 給改了不就可以了,比如這樣:
calculateDeltaTime: function (now) {
if (!now) now = performance.now();
this._deltaTime = now > this._lastUpdate ? (now - this._lastUpdate) / 1000 : 0;
if (CC_DEBUG && (this._deltaTime > 1))
this._deltaTime = 1 / 60.0;
this._lastUpdate = now;
// 乘以 2 實作倍數
this._deltaTime *= 2;
},
或者把這個乘以邏輯放在 calculateDeltaTime 呼叫的下面也可以:
// calculate "global" dt
this.calculateDeltaTime(now);
// 乘以 2 實作倍數
this._deltaTime *= 2;
更好的實作
試了試還真實作了,能夠做到全域控制速率,但是這個方法要魔改下引擎,換專案或者引擎版本無法做到復用,有沒有更好的辦法呢?當然,是可以不改引擎還能改引擎的(怪怪的,嘿嘿),其實就是在自己的代碼里去更改引擎代碼,但是又涉及一個順序問題,要確保引擎的更改順序早于你使用的邏輯,
如果你翻過檔案,你會知道插件腳本就能實作這個需求,在 CocosCreator 中腳本執行順序為:Cocos2d 引擎最先執行,然后是插件腳本(有多個的話按專案中的路徑字母順序依次加載),最后才是我們寫的普通腳本(打包后只有一個檔案,內部按 require 的依賴順序依次初始化),
那就寫個引擎擴展腳本 k-cocos.js 去擴展引擎就行了,不用魔改引擎,完美!

cc.kSpeed()誕生
接下來就是在這個插件腳本中修改一下引擎計算 dt 的方法,為了方便控制,可以引入一個變數,然后在計算后讓時間乘以這個變數,變數默認為 1 代表正常速度,想倍數我們把變數改為 2 就可以了,導演類作為一個單例物件讓這個實作更加簡單,在 k-cocos.js 中寫入:
// 游戲速率變數
cc.director._kSpeed = 1;
var _originCalculateDeltaTime = cc.Director.prototype.calculateDeltaTime;
cc.director.calculateDeltaTime = function (now) {
_originCalculateDeltaTime.call(this, now);
this._deltaTime *= this._kSpeed;
}
// 將方法掛到 cc 物件上
cc.kSpeed = function (speed) {
cc.director._kSpeed = speed;
}
因為是單例物件,直接為其宣告 _kSpeed 這個私有變數,然后重寫 calculateDeltaTime 方法,在呼叫保留后的原方法后進行一次乘法即可!(call 方法中的 this 換成 cc.director 也是一樣的,如果用箭頭函式記得改)
為了不與未來引擎的介面沖突,所有擴展的屬性方法都加個 k 字母,這樣 cc.kSpeed() 就誕生啦!可以在任何地方愉快的進行 cc.kSpeed(0.3) 這種寫法了,
效果圖:

干脆開源吧
實作了想要的效果,到這里文章其實應該結束了,但是誰讓闊闊這么有奉獻精神,獨樂樂不如眾樂樂,論壇那么多人問游戲倍速的問題,干脆開源吧!名稱就叫 KCocos 擴展庫,再給自己設計一個圖示:

不僅開源,再寫個檔案,寫就寫的高大上點!

結語
擴展腳本已經開源,還實作了全域觸點數量控制、也擴展了節點的一些屬性和方法,還有許多想法沒加進去,比如 _hitTest 等!
歡迎大家提建議、給點個星星 Star,歡迎加入討論 QQ 群:1085201157
GitHub地址: https://github.com/KuoKuo666/k-cocos
碼云地址: https://gitee.com/kuokuo666/k-cocos
對應檔案地址: https://kuokuo666.github.io
2020!我們一起進步!O(∩_∩)O~~
個人網站
內容也會同步 CSDN 與微信公眾號哦!
www.kuokuo666.com
微信公眾號

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/171637.html
標籤:其他
上一篇:學習筆記(十一):游戲引擎架構
