解讀CocosCreator源碼之引擎啟動(dòng)與主循環(huán)
前言
預(yù)備
不知道你有沒有想過,假如把游戲世界比作一輛汽車,那么這輛“汽車”是如何啟動(dòng),又是如何持續(xù)運(yùn)轉(zhuǎn)的呢?
如題,本文的內(nèi)容主要為 Cocos Creator 引擎的啟動(dòng)流程和主循環(huán)。
而在主循環(huán)的內(nèi)容中還會(huì)涉及到:組件的生命周期和計(jì)時(shí)器、緩動(dòng)系統(tǒng)、動(dòng)畫系統(tǒng)和物理系統(tǒng)等...
本文會(huì)在宏觀上為大家解讀主循環(huán)與各個(gè)模塊之間的關(guān)系,對(duì)于各個(gè)模塊也會(huì)簡(jiǎn)單介紹,但不會(huì)深入到模塊的具體實(shí)現(xiàn)。
因?yàn)槿绻衙總€(gè)模塊都“摸”一遍,那這篇文章怕是寫不完了。
Go!
希望大家看完這篇文章之后能夠更加了解 Cocos Creator 引擎。
同時(shí)也希望本文可以起到“師傅領(lǐng)進(jìn)門”的作用,大家一起加油修行鴨~
另外《源碼解讀》系列(應(yīng)該)會(huì)持續(xù)更新,如果你想要皮皮來解讀解讀引擎的某個(gè)模塊,也歡迎留言告訴我,我...我考慮下哈哈哈~
本文以 Cocos Creator 2.4.3 版本為參考。
正文
啟動(dòng)流程
index.html
對(duì)于 Web 平臺(tái) index.html 文件就是絕對(duì)的起點(diǎn)。
在默認(rèn)的 index.html 文件中,定義了游戲啟動(dòng)頁(yè)面的布局,加載了 main.js 文件,并且還有一段立即執(zhí)行的代碼。
這里截取文件中一部分比較關(guān)鍵的代碼:
// 加載引擎腳本 loadScript(debug ? 'cocos2d-js.js' : 'cocos2d-js-min.ec334.js', function () { // 是否開啟了物理系統(tǒng)? if (CC_PHYSICS_BUILTIN || CC_PHYSICS_CANNON) { // 加載物理系統(tǒng)腳本并啟動(dòng)引擎 loadScript(debug ? 'physics.js' : 'physics-min.js', window.boot); } else { // 啟動(dòng)引擎 window.boot(); } });
上面這段代碼主要用于加載引擎腳本和物理系統(tǒng)腳本,腳本加載完成之后就會(huì)調(diào)用 main.js 中定義的 window.boot()
函數(shù)。
💡 對(duì)于原生平臺(tái),會(huì)在 {項(xiàng)目目錄}build\jsb-link\frameworks\runtime-src\Classes\AppDelegate.cpp
文件的 applicationDidFinishLaunching()
函數(shù)中加載 main.js 文件。(感謝請(qǐng)容我安眠大佬的補(bǔ)充)
🧵 代碼壓縮
腳本文件名中帶有 -min
字樣一般代表著這個(gè)文件內(nèi)的代碼是被壓縮過的。
壓縮代碼可以節(jié)省代碼文件所占用的空間,加快文件加載速度,減少流量消耗,但同時(shí)也讓代碼失去了可閱讀性,不利于調(diào)試。
所以開啟調(diào)試模式后會(huì)直接使用未經(jīng)過壓縮的代碼文件,便于開發(fā)調(diào)試和定位錯(cuò)誤。
main.js
window.boot()
對(duì)于不同平臺(tái) main.js 的內(nèi)容也有些許差異,這里我們忽略差異部分,只關(guān)注其中關(guān)鍵的共同行為。
關(guān)于 main.js 文件的內(nèi)容基本上就是定義了 window.boot()
函數(shù)。
💡 對(duì)于非 Web 平臺(tái),會(huì)在定義完之后直接就調(diào)用 window.boot()
函數(shù),所以 main.js 就是他們的起點(diǎn)。
而 window.boot()
函數(shù)內(nèi)部有以下關(guān)鍵行為:
- 定義
onStart
回調(diào)函數(shù):主要用于加載啟動(dòng)場(chǎng)景 cc.assetManager.init(...)
:初始化 AssetManagercc.assetManager.loadScript(...)
:加載 src 目錄下的插件腳本cc.assetManager.loadBundle(...)
:加載項(xiàng)目中的 bundlecc.game.run(...)
:?jiǎn)?dòng)引擎
這部分的代碼就不貼了,小伙伴們可以看看自己的項(xiàng)目構(gòu)建后的 main.js 文件。
cc.game
cc.game
對(duì)象是 cc.Game
類的一個(gè)實(shí)例,cc.game
包含了游戲主體信息并負(fù)責(zé)驅(qū)動(dòng)游戲。
說人話,cc.game
對(duì)象就是管理引擎生命周期的模塊,啟動(dòng)、暫停和重啟等操作都需要用到它。
CCGame.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js
run()
cc.game.run()
函數(shù)內(nèi)指定了引擎配置和 onStart
回調(diào)并觸發(fā) cc.game.prepare()
函數(shù)。
run: function (config, onStart) { // 指定引擎配置 this._initConfig(config); this.onStart = onStart; this.prepare(game.onStart && game.onStart.bind(game)); }
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js#L491
prepare()
cc.game.prepare()
函數(shù)內(nèi)主要在項(xiàng)目預(yù)覽時(shí)快速編譯項(xiàng)目代碼并調(diào)用 _prepareFinished()
函數(shù)。
prepare(cb) { // 已經(jīng)準(zhǔn)備過則跳過 if (this._prepared) { if (cb) cb(); return; } // 加載預(yù)覽項(xiàng)目代碼 this._loadPreviewScript(() => { this._prepareFinished(cb); }); }
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js#L472
對(duì)于快速編譯的細(xì)節(jié),可以在項(xiàng)目預(yù)覽時(shí)打開瀏覽器的開發(fā)者工具,在 Sources 欄中搜索(Ctrl + P) __quick_compile_project__
即可找到 __quick_compile_project__.js
文件。
_prepareFinished()
cc.game._prepareFinished()
函數(shù)的作用主要為初始化引擎、設(shè)置幀率計(jì)時(shí)器和初始化內(nèi)建資源(effect 資源和 material 資源)。
當(dāng)內(nèi)建資源加載完成后就會(huì)調(diào)用 cc.game._runMainLoop()
啟動(dòng)引擎主循環(huán)。
_prepareFinished(cb) { // 初始化引擎 this._initEngine(); // 設(shè)置幀率計(jì)時(shí)器 this._setAnimFrame(); // 初始化內(nèi)建資源(加載內(nèi)置的 effect 和 material 資源) cc.assetManager.builtins.init(() => { // 打印引擎版本到控制臺(tái) console.log('Cocos Creator v' + cc.ENGINE_VERSION); this._prepared = true; // 啟動(dòng) mainLoop this._runMainLoop(); // 發(fā)射 ‘game_inited' 事件(即引擎已初始化完成) this.emit(this.EVENT_GAME_INITED); // 調(diào)用 main.js 中定義的 onStart 函數(shù) if (cb) cb(); }); }
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js#L387
💡 對(duì)于 _prepareFinished()
內(nèi)調(diào)用的 _setAnimFrame()
函數(shù)這里必須提一下。
_setAnimFrame()
cc.game._setAnimFrame()
內(nèi)部對(duì)不同的游戲幀率做了適配。
另外還對(duì) window.requestAnimationFrame()
接口做了兼容性封裝,用于兼容不同的瀏覽器環(huán)境,具體的我們下面再說。
這里就不貼 _setAnimFrame()
的代碼了,有需要的小伙伴可自行查閱。
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js#L564
_runMainLoop()
cc.game._runMainLoop()
這個(gè)函數(shù)的名字取得很簡(jiǎn)單直接,攤牌了它就是用來運(yùn)行 mainLoop()
函數(shù)的。
讓我們瞧瞧代碼:
_runMainLoop: function () { if (CC_EDITOR) return; if (!this._prepared) return; // 定義局部變量 var self = this, callback, config = self.config, director = cc.director, skip = true, frameRate = config.frameRate; // 展示或隱藏性能統(tǒng)計(jì) debug.setDisplayStats(config.showFPS); // 設(shè)置幀回調(diào) callback = function (now) { if (!self._paused) { // 循環(huán)調(diào)用回調(diào) self._intervalId = window.requestAnimFrame(callback); if (!CC_JSB && !CC_RUNTIME && frameRate === 30) { if (skip = !skip) return; } // 調(diào)用 mainLoop director.mainLoop(now); } }; // 將在下一幀開始循環(huán)回調(diào) self._intervalId = window.requestAnimFrame(callback); self._paused = false; }
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCGame.js#L612
通過以上代碼我們可以得知,_runMainLoop()
主要通過 window.requestAnimFrame()
接口來實(shí)現(xiàn)循環(huán)調(diào)用 mainLoop()
函數(shù)。
window.requestAnimFrame()
window.requestAnimFrame()
就是我們上面說到的 _setAnimFrame()
內(nèi)部對(duì)于 window.requestAnimationFrame()
的兼容性封裝。
對(duì)前端不太熟悉的小伙伴可能會(huì)有疑問,window.requestAnimationFrame()
又是啥,是用來干嘛的,又是如何運(yùn)行的?
window.requestAnimationFrame()
簡(jiǎn)單來說,window.requestAnimationFrame()
用于向?yàn)g覽器請(qǐng)求進(jìn)行一次重繪(repaint),并在重繪之前調(diào)用指定的回調(diào)函數(shù)。
window.requestAnimationFrame()
接收一個(gè)回調(diào)作為參數(shù)并返回一個(gè)整數(shù)作為唯一標(biāo)識(shí),瀏覽器將會(huì)在下一個(gè)重繪之前執(zhí)行這個(gè)回調(diào);并且執(zhí)行回調(diào)時(shí)會(huì)傳入一個(gè)參數(shù),參數(shù)的值與 performance.now()
返回的值相等。
performance.now()
的返回值可以簡(jiǎn)單理解為瀏覽器窗口的運(yùn)行時(shí)長(zhǎng),即從打開窗口到當(dāng)前時(shí)刻的時(shí)間差。
MDN 文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/now
回調(diào)函數(shù)的執(zhí)行次數(shù)通常與瀏覽器屏幕刷新次數(shù)相匹配,也就是說,對(duì)于刷新率為 60Hz 的顯示器,瀏覽器會(huì)在一秒內(nèi)執(zhí)行 60 次回調(diào)函數(shù)。
對(duì)于 window.requestAnimationFrame()
的說明到此為止,如果想要了解更多信息請(qǐng)自行搜索。
MDN 文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
小結(jié)
畫一張圖來對(duì)引擎的啟動(dòng)流程做一個(gè)小小的總結(jié)叭~
主循環(huán)
經(jīng)歷了一番波折后,終于來到了最期待的引擎主循環(huán)部分,話不多說,我們繼續(xù)!
cc.director
cc.director
對(duì)象是導(dǎo)演類 cc.Director
的實(shí)例,引擎通主要過 cc.director
對(duì)象來管理游戲的邏輯流程。
CCDirector.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCDirector.js
mainLoop()
🍖 cc.director.mainLoop()
函數(shù)可能是引擎中最關(guān)鍵的邏輯之一了,包含的內(nèi)容很多也很關(guān)鍵。
現(xiàn)在讓我們進(jìn)入 mainLoop()
函數(shù)內(nèi)部來一探究竟吧!
這里我選擇性剔除掉了函數(shù)中一些的代碼,還搞了點(diǎn)注釋:
mainLoop: function(now) { // 計(jì)算“全局”增量時(shí)間(DeltaTime) // 也就是距離上一次調(diào)用 mainLoop 的時(shí)間間隔 this.calculateDeltaTime(now); // 游戲沒有暫停則進(jìn)行更新 if (!this._paused) { // 發(fā)射 'director_before_update' 事件 this.emit(cc.Director.EVENT_BEFORE_UPDATE); // 調(diào)用新增的組件(已啟用)的 start 函數(shù) this._compScheduler.startPhase(); // 調(diào)用所有組件(已啟用)的 update 函數(shù) this._compScheduler.updatePhase(this._deltaTime); // 更新調(diào)度器(cc.Scheduler) this._scheduler.update(this._deltaTime); // 調(diào)用所有組件(已啟用)的 lateUpdate 函數(shù) this._compScheduler.lateUpdatePhase(this._deltaTime); // 發(fā)射 'director_after_update' 事件 this.emit(cc.Director.EVENT_AFTER_UPDATE); // 銷毀最近被移除的實(shí)體(節(jié)點(diǎn)) Obj._deferredDestroy(); } // 發(fā)射 'director_before_draw' 事件 this.emit(cc.Director.EVENT_BEFORE_DRAW); // 渲染游戲場(chǎng)景 renderer.render(this._scene, this._deltaTime); // 發(fā)射 'director_after_draw' 事件 this.emit(cc.Director.EVENT_AFTER_DRAW); // 更新事件管理器的事件監(jiān)聽(cc.eventManager 已被廢棄) eventManager.frameUpdateListeners(); // 累加游戲運(yùn)行的總幀數(shù) this._totalFrames++; }
傳送門:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/CCDirector.js#L843
接下來我們來對(duì)主循環(huán)中的關(guān)鍵點(diǎn)一一進(jìn)行分解。
ComponentScheduler
cc.director
對(duì)象中的 _compScheduler
屬性 是 ComponentScheduler
類的實(shí)例。
大多數(shù)小伙伴可能對(duì)于 ComponentScheduler
這個(gè)類沒有什么印象,我來簡(jiǎn)單解釋一下。
將 ComponentScheduler
的名字直譯過來就是“組件調(diào)度器”,從名字上就可以看出,這個(gè)類是用來調(diào)度組件的。
說人話,ComponentScheduler
類是用來集中調(diào)度(管理)游戲場(chǎng)景中所有組件(cc.Component
)的生命周期的。
文字不夠直觀,看完下面這張圖大概就懂了:
component-scheduler.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/component-scheduler.js
startPhase
// 調(diào)用新增的組件(已啟用)的 start 函數(shù) this._compScheduler.startPhase();
組件的 start
回調(diào)函數(shù)會(huì)在組件第一次激活前,也就是第一次執(zhí)行 update
之前觸發(fā)。
在組件的一生中 start
回調(diào)只會(huì)被觸發(fā)一次,onLoad
和 onEnable
也一樣。
只不過 onLoad
和 onEnable
是由 NodeActivator
類的實(shí)例來管理的:
onLoad
會(huì)在節(jié)點(diǎn)激活時(shí)就觸發(fā)onEnable
會(huì)在組件被啟用時(shí)觸發(fā)
而 start
則會(huì)等到下一次主循環(huán) mainLoop()
時(shí)才觸發(fā)。
🥁 NodeActivator
NodeActivator
類主要用于啟用和禁用節(jié)點(diǎn)以及身上的組件。
cc.director
對(duì)象中就擁有一個(gè)實(shí)例 _nodeActivator
,游戲中所有節(jié)點(diǎn)的啟用和禁用都需要通過它來操作。
像這樣:cc.director._nodeActivator.activateNode(this, value);
node-activator.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/node-activator.js
updatePhase
// 調(diào)用所有組件(已啟用)的 update 函數(shù) this._compScheduler.updatePhase(deltaTime);
組件的 update
函數(shù)在每一幀都會(huì)被觸發(fā)一次。
lateUpdatePhase
// 調(diào)用所有組件(已啟用)的 lateUpdate 函數(shù) this._compScheduler.lateUpdatePhase(deltaTime);
組件的 lateUpdate
函數(shù)會(huì)在 update
和調(diào)度器 cc.Scheduler
更新之后被觸發(fā)。調(diào)度器的更新內(nèi)容包括緩動(dòng)、動(dòng)畫和物理等,這一點(diǎn)下面會(huì)展開。
ParticleSystem
BTW,粒子系統(tǒng)組件(cc.ParticleSystem
)就是在 lateUpdate
回調(diào)函數(shù)中進(jìn)行更新的。
CCParticleSystem.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/particle/CCParticleSystem.js
Tips
請(qǐng)謹(jǐn)慎使用 update
和 lateUpdate
回調(diào),因?yàn)樗鼈兠恳粠紩?huì)被觸發(fā),如果 update
或 lateUpdate
內(nèi)的邏輯過多,就會(huì)使得每一幀的執(zhí)行時(shí)間(即幀時(shí)間 Frame time)都變長(zhǎng),導(dǎo)致游戲運(yùn)行幀數(shù)降低或出現(xiàn)不穩(wěn)定的情況。
📢 注意這不是不讓你用,該用還得用,只是不要濫用,不要啥玩意都往里邊賽~
Scheduler
cc.director
對(duì)象的 _scheduler
屬性為 cc.Scheduler
類的實(shí)例。
cc.Scheduler
是負(fù)責(zé)觸發(fā)回調(diào)函數(shù)的類。
Scheduler.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/Scheduler.js
💣 你絕對(duì)猜不到下面這一行看起來如此平平無奇的代碼執(zhí)行之后會(huì)發(fā)生什么。
// 更新調(diào)度器(cc.Scheduler 類實(shí)例) this._scheduler.update(this._deltaTime);
cc.director.mainLoop()
中使用 _scheduler.update()
函數(shù)來分發(fā) update,在調(diào)度器(cc.director._scheduler
)內(nèi)部會(huì)根據(jù)優(yōu)先級(jí)先后觸發(fā)各個(gè)系統(tǒng)模塊和組件計(jì)時(shí)器的更新。
系統(tǒng)模塊
調(diào)度器的更新會(huì)先觸發(fā)以下系統(tǒng)模塊的更新:
- ActionManager
- AnimationManager
- CollisionManager
- PhysicsManager
- Physics3DManager
- InputManager
以上這些模塊都以 cc.director._scheduler.scheduleUpdate()
的方式注冊(cè)到調(diào)度器上,因?yàn)檫@些模塊每一幀都需要進(jìn)行更新。
除了 InputManager
以外的模塊的優(yōu)先級(jí)都為 cc.Scheduler.PRIORITY_SYSTEM
,也就是系統(tǒng)優(yōu)先級(jí),會(huì)優(yōu)先被觸發(fā)。
ActionManager
ActionManager
即動(dòng)作管理器,用于管理游戲中的所有動(dòng)作,也就是緩動(dòng)系統(tǒng) Action
和 Tween
(其實(shí)它們本質(zhì)上是同一種東西)。
CCActionManager.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/actions/CCActionManager.js
AnimationManager
AnimationManager
即動(dòng)畫管理器,用于管理游戲中的所有動(dòng)畫,驅(qū)動(dòng)節(jié)點(diǎn)上的 Animation
組件播放動(dòng)畫。
animation-manager.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/animation/animation-manager.js
CollisionManager
CollisionManager
即碰撞組件管理器,用于處理節(jié)點(diǎn)之間的碰撞組件是否產(chǎn)生了碰撞,并調(diào)用相應(yīng)回調(diào)函數(shù)。
CCCollisionManager.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/collider/CCCollisionManager.js
PhysicsManager
PhysicsManager
即物理系統(tǒng)管理器,內(nèi)部以 Box2D
作為 2D 物理引擎,加以封裝并開放部分常用的接口。同時(shí) PhysicsManager
還負(fù)責(zé)管理碰撞信息的分發(fā)。
CCPhysicsManager.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/physics/CCPhysicsManager.js
Physics3DManager
Physics3DManager
即3D 物理系統(tǒng)管理器,Cocos Creator 中的 3D 物理引擎有 Cannon.js
和 Builtin
可選,Physics3DManager
給它們封裝了統(tǒng)一的常用接口。
physics-manager.ts:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/3d/physics/framework/physics-manager.ts
InputManager
InputManager
即輸入事件管理器,用于管理所有輸入事件。開發(fā)者主動(dòng)啟用加速度計(jì)(Accelerometer)之后,引擎會(huì)定時(shí)通過 InputManager
發(fā)送 cc.SystemEvent.EventType.DEVICEMOTION
事件(默認(rèn)間隔為 0.2 秒)。
CCInputManager.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d\core\platform\CCInputManager.js
組件計(jì)時(shí)器
相信大多數(shù)小伙伴都使用過組件的 schedule()
和 scheduleOnce()
接口,主要用來重復(fù)執(zhí)行或定時(shí)執(zhí)行函數(shù)。
實(shí)際上,cc.Component
的 schedule()
接口依賴的也是 cc.Scheduler
類,具體使用的也是 cc.director
對(duì)象中的 _scheduler
實(shí)例。
組件的 schedule()
接口在 cc.director._scheduler.schedule()
接口之外加了一層封裝,以組件自身作為 target
,這樣一來組件內(nèi)的定時(shí)任務(wù)就與組件生命周期綁定,當(dāng)組件被銷毀時(shí)定時(shí)任務(wù)也會(huì)被移除。
而 scheduleOnce()
接口則是在組件的 schedule()
接口之外又加了一層封裝,固定只會(huì)在指定時(shí)間后執(zhí)行一次。
CCComponent.js:https://github.com/cocos-creator/engine/blob/2.4.3/cocos2d/core/components/CCComponent.js
[文檔] 使用計(jì)時(shí)器:http://docs.cocos.com/creator/manual/zh/scripting/scheduler.html
另外我還注意到,有不少小伙伴還不是很清楚組件的計(jì)時(shí)器和 setTimeout()
、setInterval()
之間的區(qū)別和用法,那就趁這個(gè)機(jī)會(huì)簡(jiǎn)單講一下吧~
setTimeout & setInterval
setTimeout()
和 setInterval()
都是由瀏覽器或 Node.js 這類 runtime 所提供的接口。
setTimeout()
接口用于設(shè)置一個(gè)定時(shí)器,該定時(shí)器在定時(shí)器到期后執(zhí)行一個(gè)函數(shù)或指定的一段代碼。setInterval()
接口用于重復(fù)調(diào)用一個(gè)函數(shù)或執(zhí)行一個(gè)代碼段,在每次調(diào)用之間具有固定的時(shí)間延遲。
💡 再補(bǔ)充一個(gè)小知識(shí):
在瀏覽器中 setTimeout()
和 setInterval()
的最小延時(shí)(間隔)是 4ms。
如果是未激活(后臺(tái))的標(biāo)簽頁(yè)(tab),最小延時(shí)(間隔)則加長(zhǎng)到 1000ms。
🌰 舉個(gè)栗子
假如我在當(dāng)前標(biāo)簽頁(yè)設(shè)置了一個(gè)每 500ms 輸出一個(gè) log 的定時(shí)器,當(dāng)我切換到別的標(biāo)簽頁(yè)之后,那么這個(gè)定時(shí)器就會(huì)變成每 1000ms 才輸出一個(gè) log。
像這樣,感興趣的話可以自己去試試:
setInterval(() => { console.log(new Date().getTime()); }, 500); // 模擬輸出 // 標(biāo)簽頁(yè)在前臺(tái) // 1604373521000 // 1604373521500 // 1604373522000 // 切換到別的標(biāo)簽頁(yè)后 // 1604373523000 // 1604373524000 // 1604373525000
區(qū)別 & 用法
組件的計(jì)時(shí)器依賴于引擎的 mainLoop()
和組件自身,如果引擎被暫停,那么組件的計(jì)時(shí)器也會(huì)被暫停,如果組件或組件所在的節(jié)點(diǎn)被銷毀了,那么計(jì)時(shí)器也會(huì)失效。
setTimeout()
和 setInterval()
都依賴于當(dāng)前所處的 window
對(duì)象,也就是說只要當(dāng)前瀏覽器標(biāo)簽頁(yè)不關(guān)閉,setTimeout()
和 setInterval()
都還是會(huì)執(zhí)行的。
當(dāng)你需要在組件內(nèi)部定時(shí)或重復(fù)執(zhí)行某一函數(shù)或操作某個(gè)節(jié)點(diǎn),那么可以使用組件的計(jì)時(shí)器。
💬 讓我們想象一個(gè)場(chǎng)景:
在當(dāng)前場(chǎng)景中的某個(gè)腳本內(nèi)使用 setInterval()
來重復(fù)移動(dòng)場(chǎng)景中的某個(gè)節(jié)點(diǎn),當(dāng)我們切換場(chǎng)景后會(huì)發(fā)生什么?
當(dāng)定時(shí)器再次調(diào)用回調(diào)嘗試移動(dòng)節(jié)點(diǎn)的時(shí)候,會(huì)無法找到目標(biāo)節(jié)點(diǎn)而報(bào)錯(cuò),因?yàn)楣?jié)點(diǎn)已經(jīng)跟著之前的場(chǎng)景一起被銷毀了,而定時(shí)器還在繼續(xù)執(zhí)行。
這種情況下使用組件的計(jì)時(shí)器就不會(huì)有這種問題,因?yàn)橛?jì)時(shí)器會(huì)隨著組件的銷毀而被清除。
而當(dāng)我們需要執(zhí)行一些與游戲場(chǎng)景沒有關(guān)聯(lián)的事情的時(shí)候,就可以考慮使用 setTimeout()
或 setInterval()
。
🎃 當(dāng)然能用組件計(jì)時(shí)器的話最好還是用組件計(jì)時(shí)器啦~
小結(jié)
依然還是畫一張圖來小小總結(jié)一下 Scheduler
。
總結(jié)
關(guān)于引擎的啟動(dòng)流程和主循環(huán)就解讀到這里啦。
最后的最后,還是畫張圖來做一個(gè)最后的總結(jié)~
以上就是解讀CocosCreator源碼之引擎啟動(dòng)與主循環(huán)的詳細(xì)內(nèi)容,更多關(guān)于CocosCreator源碼解讀的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Unity3D實(shí)現(xiàn)攝像機(jī)鏡頭移動(dòng)并限制角度
- 詳解CocosCreator中幾種計(jì)時(shí)器的使用方法
- CocosCreator學(xué)習(xí)之模塊化腳本
- 怎樣在CocosCreator中使用物理引擎關(guān)節(jié)
- 如何在CocosCreator中使用JSZip壓縮
- CocosCreator入門教程之用TS制作第一個(gè)游戲
- CocosCreator通用框架設(shè)計(jì)之資源管理
- 如何在CocosCreator中做一個(gè)List
- 如何在CocosCreator中使用http和WebSocket
- 剖析CocosCreator新資源管理系統(tǒng)
- CocosCreator怎樣使用cc.follow進(jìn)行鏡頭跟隨
相關(guān)文章
Javascript中的return作用及javascript return關(guān)鍵字用法詳解
本文給大家介紹Javascript中的return作用及javascript return關(guān)鍵字用法詳解,對(duì)于大家學(xué)習(xí)javascript中的return知識(shí)有所幫助,感興趣的朋友一起學(xué)習(xí)吧2015-11-11JavaScript實(shí)現(xiàn)字符串與日期的互相轉(zhuǎn)換及日期的格式化
這篇文章主要介紹了JavaScript實(shí)現(xiàn)字符串與日期的互相轉(zhuǎn)換及日期的格式化的方法,這里格式化使用的是正則表達(dá)式來替換日期后進(jìn)行格式化,需要的朋友可以參考下2016-03-03JavaScript實(shí)現(xiàn)答題評(píng)分功能頁(yè)面
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)答題評(píng)分功能頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06原生JavaScript實(shí)現(xiàn)輪播圖效果
這篇文章主要為大家詳細(xì)介紹了原生JavaScript實(shí)現(xiàn)輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09JS關(guān)閉子窗口并且刷新上一個(gè)窗口的實(shí)現(xiàn)示例
這篇文章主要介紹了JS關(guān)閉子窗口并且刷新上一個(gè)窗口的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03