游戲速率倍速與慢動作
CocosCreator之控制游戲速率實現(xiàn)倍速與慢動作
摘要
在游戲開發(fā)中,游戲速率控制一直是一個需求,官方提供了計時器的控制接口以及動作系統(tǒng)的 cc.speed,但是使用起來不是很方便且無法影響到 update 控制邏輯以及物理系統(tǒng),那么如何實現(xiàn)這一需求呢?
正文
使用版本
CocosCreator 版本 2.3.4
思維過程
想問題還是要去根上找,跑到源碼里先看看官方實現(xiàn)的計時器控制邏輯是怎么樣的?在 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 時間給改了,那我們想實現(xiàn)全局的控制應(yīng)該再往根源處尋找。
導(dǎo)演類控制
正常講游戲循環(huán)是每秒 60 幀,那么每幀的主循環(huán)邏輯應(yīng)該不是在 CCGame.js 就是在 CCDirector.js 中,果然在導(dǎo)演類中看到了 mainLoop 方法,而其中有這么一段代碼(省略了無關(guān)代碼):
//?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 后分發(fā)下去,那我們在 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?實現(xiàn)倍數(shù)
????this._deltaTime?*=?2;
},
或者把這個乘以邏輯放在 calculateDeltaTime 調(diào)用的下面也可以:
//?calculate?"global"?dt
this.calculateDeltaTime(now);
//?乘以?2?實現(xiàn)倍數(shù)
this._deltaTime?*=?2;
更好的實現(xiàn)
試了試還真實現(xiàn)了,能夠做到全局控制速率,但是這個方法要魔改下引擎,換項目或者引擎版本無法做到復(fù)用,有沒有更好的辦法呢?當然,是可以不改引擎還能改引擎的(怪怪的,嘿嘿)。其實就是在自己的代碼里去更改引擎代碼,但是又涉及一個順序問題,要確保引擎的更改順序早于你使用的邏輯。
如果你翻過文檔,你會知道插件腳本就能實現(xiàn)這個需求,在 CocosCreator 中腳本執(zhí)行順序為:Cocos2d 引擎最先執(zhí)行,然后是插件腳本(有多個的話按項目中的路徑字母順序依次加載),最后才是我們寫的普通腳本(打包后只有一個文件,內(nèi)部按 require 的依賴順序依次初始化)。
那就寫個引擎擴展腳本 k-cocos.js 去擴展引擎就行了,不用魔改引擎,完美!
cc.kSpeed()誕生
接下來就是在這個插件腳本中修改一下引擎計算 dt 的方法,為了方便控制,可以引入一個變量,然后在計算后讓時間乘以這個變量,變量默認為 1 代表正常速度,想倍數(shù)我們把變量改為 2 就可以了。導(dǎo)演類作為一個單例對象讓這個實現(xiàn)更加簡單,在 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 方法,在調(diào)用保留后的原方法后進行一次乘法即可?。╟all 方法中的 this 換成 cc.director 也是一樣的,如果用箭頭函數(shù)記得改)
為了不與未來引擎的接口沖突,所有擴展的屬性方法都加個 k 字母,這樣 cc.kSpeed() 就誕生啦!可以在任何地方愉快的進行 cc.kSpeed(0.3) 這種寫法了。
效果圖:
干脆開源吧
實現(xiàn)了想要的效果,到這里文章其實應(yīng)該結(jié)束了,但是誰讓闊闊這么有奉獻精神,獨樂樂不如眾樂樂,論壇那么多人問游戲倍速的問題,干脆開源吧!名稱就叫 KCocos 擴展庫,再給自己設(shè)計一個圖標:

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

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