<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Cocos Creator 腳本組件的生與死!

          共 15738字,需瀏覽 32分鐘

           ·

          2021-05-12 23:38

          ?

          ?目前 Creator 提供給用戶的生命周期回調(diào)函數(shù)主要有:

          onLoad
          start
          update
          lateUpdate
          onDestroy
          onEnable
          onDisable


          測試腳本


          const { ccclass, property } = cc._decorator;
          @ccclassexport default class LifeCycle extends cc.Component {
              // LIFE-CYCLE CALLBACKS:    __preload() { debugger }
          onLoad() { debugger }
          onEnable() { debugger }
          start() { debugger }
          update(dt) { // debugger }
          lateUpdate(dt) { // debugger }
          onDisable() { debugger }
          onDestroy() { debugger }
          clickEvent() { // this.node.active = false; // this.node.active = true; // this.node.destroy(); this.destroy(); }}


          觸發(fā)順序


          一個組件從初始化到激活,再到銷毀的生命周期函數(shù)調(diào)用順序?yàn)椋?/span>


          接下來我們按生命周期觸發(fā)的先后順序,看一下每個回調(diào)函數(shù)的調(diào)用時機(jī)。

          要特別區(qū)分一下,生命周期是依賴節(jié)點(diǎn)還是依賴組件。

          __preload、onLoad 依賴節(jié)點(diǎn)

          onEnable、start、update、lateUpdate、onDisable、onDestroy 依賴組件。

          它們的區(qū)別在于:

          如果腳本組件所在節(jié)點(diǎn)的 active = true,但腳本組件的 enabled = false,依然會執(zhí)行節(jié)點(diǎn)相關(guān)回調(diào),但不會執(zhí)行組件相關(guān)回調(diào)。只有在 enabled = true 的情況下,組件關(guān)回調(diào)才會被執(zhí)行。

          如果腳本組件所在節(jié)點(diǎn)的 active = false,無論腳本組件的 enabled 為 true 或 false,都不會執(zhí)行任何回調(diào)。


          調(diào)用時機(jī)


          1__preload onLoad onEnable


          · __preload

          官方文檔沒有說明,以下僅是個人理解。

          見名知意,其功能和 onLoad 相同,僅僅是調(diào)用時機(jī)在 onLoad 之前。

          · onLoad

          腳本組件的初始化階段,該回調(diào)會在節(jié)點(diǎn)首次激活時觸發(fā),比如所在的場景被載入,或者所在節(jié)點(diǎn)被激活的情況下。

          在 onLoad 階段,保證了你可以獲取到場景中的其他節(jié)點(diǎn),以及節(jié)點(diǎn)關(guān)聯(lián)的資源數(shù)據(jù)。

          該回調(diào)總是會在 start 方法調(diào)用前執(zhí)行,這能用于安排腳本的初始化順序。

          · onEnable

          當(dāng)組件的 enabled 屬性從 false 變?yōu)?true 時,或者所在節(jié)點(diǎn)的 active 屬性從 false 變?yōu)?true 時,會激活 onEnable 回調(diào)。

          倘若節(jié)點(diǎn)第一次被創(chuàng)建且 enabled 為 true,則會在 onLoad 之后,start 之前被調(diào)用。

          調(diào)用時機(jī):


          從以上調(diào)用時機(jī)可以推斷,如果一個節(jié)點(diǎn)掛載了多個腳本組件,那么執(zhí)行順序是:
          所有腳本組件的 onLoad → 所有腳本組件的 onEnable → 所有腳本組件的 start  等等。

          · 當(dāng)節(jié)點(diǎn)激活后,即 active = true:


          相關(guān)源碼路徑:

          resources\engine\cocos2d\core\node-activator.js
          resources\engine\cocos2d\core\component-scheduler.js

          ① active:訪問器屬性

          當(dāng)前節(jié)點(diǎn)的自身激活狀態(tài)

          值得注意的是,一個節(jié)點(diǎn)的父節(jié)點(diǎn)如果不被激活,那么即使它自身設(shè)為激活,它仍然無法激活。

          active: { get () { return this._active; }, set (value) { value = !!value; if (this._active !== value) { this._active = value; var parent = this._parent; if (parent) {                // 父節(jié)點(diǎn)的激活狀態(tài) var couldActiveInScene = parent._activeInHierarchy; if (couldActiveInScene) {                    // 更改節(jié)點(diǎn)激活狀態(tài) cc.director._nodeActivator.activateNode(this, value); } } } }}

          ② activateNode:更改節(jié)點(diǎn)激活狀態(tài)

          activateNode (node, active) { // 激活 if (active) { var task = activateTasksPool.get(); this._activatingStack.push(task);
          // 遞歸激活所有子節(jié)點(diǎn)及節(jié)點(diǎn)上的所有組件 this._activateNodeRecursively(node, task.preload, task.onLoad, task.onEnable); // 調(diào)用 __preload() task.preload.invoke(); // 調(diào)用 onLoad() task.onLoad.invoke(); // 調(diào)用 onEnable() task.onEnable.invoke();
          this._activatingStack.pop(); activateTasksPool.put(task); } // 禁用 else { // 遞歸禁用所有子節(jié)點(diǎn)及節(jié)點(diǎn)上的所有組件 this._deactivateNodeRecursively(node);
          // remove children of this node from previous activating tasks to debounce // (this is an inefficient operation but it ensures general case could be implemented in a efficient way) var stack = this._activatingStack; for (var i = 0; i < stack.length; i++) { var lastTask = stack[i]; lastTask.preload.cancelInactive(IsPreloadStarted); lastTask.onLoad.cancelInactive(IsOnLoadStarted); lastTask.onEnable.cancelInactive(); } } node.emit('active-in-hierarchy-changed', node);}

          其中 task.preload,task.onLoad,task.onEnable 
          分別對應(yīng) _activateNodeRecursively 中的  preloadInvoker、onLoadInvoker、onEnableInvoker。

          另外關(guān)于生命周期中的幾個調(diào)用方法,由于篇幅所限,就不再啰嗦了,感興趣的可以自己查閱。

          UnsortedInvoker
          OneOffInvoker
          ReusableInvoker

          invokePreload
          invokeOnLoad
          invokeOnEnable
          invokeStart
          invokeUpdate
          invokeLateUpdate

          ③ _activateNodeRecursively:遞歸激活所有子節(jié)點(diǎn)及節(jié)點(diǎn)上的所有組件

          _activateNodeRecursively (node, preloadInvoker, onLoadInvoker, onEnableInvoker) {    // 如果節(jié)點(diǎn)正在反激活的過程中則返回 if (node._objFlags & Deactivating) { // 對相同節(jié)點(diǎn)而言,無法撤銷反激活,防止反激活 - 激活 - 反激活的死循環(huán)發(fā)生。        // 這樣設(shè)計簡化了一些引擎的實(shí)現(xiàn),而且對調(diào)用者來說能保證反激活操作都能成功。 cc.errorID(3816, node.name); return; }
          node._activeInHierarchy = true;
          // component maybe added during onEnable, and the onEnable of new component is already called // so we should record the origin length var originCount = node._components.length;    // 激活該節(jié)點(diǎn)所有組件 for (let i = 0; i < originCount; ++i) { let component = node._components[i]; if (component instanceof cc.Component) { this.activateComp(component, preloadInvoker, onLoadInvoker, onEnableInvoker);        } else { _componentCorrupted(node, component, i); --i; --originCount; } }
          node._childArrivalOrder = node._children.length; // activate children recursively for (let i = 0, len = node._children.length; i < len; ++i) { let child = node._children[i];        // 根據(jù)子節(jié)點(diǎn)排列順序設(shè)置 _localZOrder child._localZOrder = (child._localZOrder & 0xffff0000) | (i + 1); if (child._active) {            // 遞歸調(diào)用 this._activateNodeRecursively(child, preloadInvoker, onLoadInvoker, onEnableInvoker); } } node._onPostActivated(true);}

          _localZOrder 和 zIndex 的關(guān)系為:

          zIndex = _localZOrder >> 16_localZOrder = (_localZOrder & 0x0000ffff) | (value << 16)

          ④ activateComp:激活組件,填充對應(yīng)的生命周期回調(diào)

          activateComp: function (comp, preloadInvoker, onLoadInvoker, onEnableInvoker{ if (!cc.isValid(comp, true)) { // destroyed before activating return; } if (!(comp._objFlags & IsPreloadStarted)) { comp._objFlags |= IsPreloadStarted; if (comp.__preload) { if (preloadInvoker) { preloadInvoker.add(comp);            } else { comp.__preload(); } } } if (!(comp._objFlags & IsOnLoadStarted)) { comp._objFlags |= IsOnLoadStarted; if (comp.onLoad) { if (onLoadInvoker) { onLoadInvoker.add(comp);            } else { comp.onLoad(); comp._objFlags |= IsOnLoadCalled; }        } else { comp._objFlags |= IsOnLoadCalled; } } if (comp._enabled) { var deactivatedOnLoading = !comp.node._activeInHierarchy; if (deactivatedOnLoading) { return; }        // 啟用組件 cc.director._compScheduler.enableComp(comp, onEnableInvoker); }}

          · 當(dāng)組件啟用后,即 enable = true:


          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\components\CCComponent.js

          ① enabled:訪問器屬性

          表示該組件自身是否啟用。

          enabled: { get () { return this._enabled; }, set (value) { if (this._enabled !== value) { this._enabled = value; if (this.node._activeInHierarchy) { var compScheduler = cc.director._compScheduler;                if (value) {                    // 啟動該組件 compScheduler.enableComp(this);                } else {                    // 禁用該組件 compScheduler.disableComp(this); } } } }, visible: false, animatable: true}

          ② enableComp:啟用組件,填充對應(yīng)的生命周期回調(diào)

          enableComp: function (comp, invoker) { if (!(comp._objFlags & IsOnEnableCalled)) { if (comp.onEnable) { if (invoker) { invoker.add(comp); return;            } else { comp.onEnable();
          var deactivatedDuringOnEnable = !comp.node._activeInHierarchy; if (deactivatedDuringOnEnable) { return; } } } this._onEnabled(comp); }}

          ③ _onEnabled:啟用組件

          _onEnabled (comp) { cc.director.getScheduler().resumeTarget(comp); comp._objFlags |= IsOnEnableCalled;
          // schedule if (this._updating) { // 如果在當(dāng)前幀內(nèi)激活該組件,則會被放入到延時隊列中 this._deferredComps.push(comp); } else { this._scheduleImmediate(comp); }

          如果是在當(dāng)前幀內(nèi)激活組件,就會把該組件放入到延時隊列中,等到當(dāng)前幀結(jié)束時(lateUpdatePhase)觸發(fā) start 回調(diào),然后在下一幀觸發(fā) update 和 lateUpdate。

          lateUpdatePhase:

          lateUpdatePhase (dt) { this.lateUpdateInvoker.invoke(dt);
          // End of this frame this._updating = false;
          this._startForNewComps();}

          其中 __preload 、onLoad 通過對標(biāo)志位(_objFlags)進(jìn)行位運(yùn)算來判斷是否填充過對應(yīng)的回調(diào),以保證生命周期內(nèi)只調(diào)用一次。

          if (!(comp._objFlags & IsPreloadStarted)) { comp._objFlags |= IsPreloadStarted; xxx}if (!(comp._objFlags & IsOnLoadStarted)) { comp._objFlags |= IsOnLoadStarted; xxx}

          而 onEnable 是通過判斷該組件是否啟用(_enabled),所以該組件每次啟用后都會被調(diào)用一次。

          if (comp._enabled) { xxx}

          · 利用位運(yùn)算設(shè)置標(biāo)志位

          ① 設(shè)置標(biāo)志位
          comp._objFlags |= IsOnEnableCalled;

          ② 關(guān)閉標(biāo)志位
          comp._objFlags &= ~IsOnEnableCalled;

          ③ 判斷標(biāo)志位
          if (comp._objFlags & IsOnEnableCalled)

          3start update lateUpdate


          · start

          start 回調(diào)函數(shù)會在組件第一次激活后,也就是第一次執(zhí)行 update 之前觸發(fā)

          · update

          游戲開發(fā)的一個關(guān)鍵點(diǎn)是在每一幀渲染前更新物體的行為,狀態(tài)和方位。這些更新操作通常都放在 update 回調(diào)中

          · lateUpdate

          update 會在所有動畫更新前執(zhí)行,但如果我們要在動效(如動畫、粒子、物理等)更新之后才進(jìn)行一些額外操作,或者希望在所有組件的 update 都執(zhí)行完之后才進(jìn)行其它操作,那就需要用到 lateUpdate 回調(diào)

          調(diào)用時機(jī):

          相關(guān)源碼路徑:
          resources/engine/cocos2d/core/CCDirector.js

          mainLoop:
          Run main loop of director

          mainLoop: function (now) { if (this._purgeDirectorInNextLoop) { this._purgeDirectorInNextLoop = false; this.purgeDirector();    } else { // 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);
          // 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(); }
          // Render this.emit(cc.Director.EVENT_BEFORE_DRAW); renderer.render(this._scene, this._deltaTime);
          // After draw this.emit(cc.Director.EVENT_AFTER_DRAW);
          eventManager.frameUpdateListeners(); this._totalFrames++; }}

          4onDisable onDestroy


          · onDisable

          當(dāng)組件的 enabled 屬性從 true 變?yōu)?nbsp;false 時,或者所在節(jié)點(diǎn)的 active 屬性從 true 變?yōu)?nbsp;false 時,會激活 onDisable 回調(diào)

          · onDestroy

          當(dāng)組件或者所在節(jié)點(diǎn)調(diào)用了 destroy(),則會調(diào)用 onDestroy 回調(diào),并在當(dāng)前幀結(jié)束時統(tǒng)一回收組件

          · 當(dāng)節(jié)點(diǎn)禁用后,即 active = false:


          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\node-activator.js

          ① _deactivateNodeRecursively:遞歸禁用所有子節(jié)點(diǎn)及節(jié)點(diǎn)上的所有組件

          _deactivateNodeRecursively (node) {    // 設(shè)置標(biāo)志位 node._objFlags |= Deactivating; node._activeInHierarchy = false; // 禁用該節(jié)點(diǎn)所有組件 var originCount = node._components.length; for (let c = 0; c < originCount; ++c) { let component = node._components[c]; if (component._enabled) { cc.director._compScheduler.disableComp(component);
          if (node._activeInHierarchy) { // reactivated from root node._objFlags &= ~Deactivating; return; } } }     // 遞歸調(diào)用 for (let i = 0, len = node._children.length; i < len; ++i) { let child = node._children[i]; if (child._activeInHierarchy) { this._deactivateNodeRecursively(child);
          if (node._activeInHierarchy) { // reactivated from root node._objFlags &= ~Deactivating; return; } } }
          node._onPostActivated(false); node._objFlags &= ~Deactivating;}

          · 當(dāng)組件禁用后,即 enable = false:


          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\component-scheduler.js

          ① disableComp:禁用組件

          disableComp: : function (comp) { if (comp._objFlags & IsOnEnableCalled) {        if (comp.onDisable) { comp.onDisable(); } this._onDisabled(comp); }}

          · 當(dāng)節(jié)點(diǎn)銷毀后,即 node.destroy():

          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\utils\base-node.js

          destroy:銷毀節(jié)點(diǎn)

          destroy () { if (cc.Object.prototype.destroy.call(this)) { this.active = false; }}

          ① cc.Object.prototype.destroy.call(this):銷毀該對象,并釋放所有它對其它對象的引用

          實(shí)際銷毀操作會延遲到當(dāng)前幀渲染前執(zhí)行,從下一幀開始,該對象將不再可用,您可以在訪問對象之前使用 cc.isValid(obj) 來檢查對象是否已被銷毀。

          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\platform\CCObject.js

          destroy:

          prototype.destroy = function () { if (this._objFlags & Destroyed) { cc.warnID(5000); return false; } if (this._objFlags & ToDestroy) { return false; }    // 設(shè)置標(biāo)志位 this._objFlags |= ToDestroy;    // 放入待銷毀隊列,在當(dāng)幀結(jié)束時統(tǒng)一回收組件 objectsToDestroy.push(this);
          return true;};

          ② this.active = false 同禁用節(jié)點(diǎn)

          · 當(dāng)組件銷毀后,即 comp.destroy()

          destroy:組件銷毀

          destroy () { if (CC_EDITOR) { var depend = this.node._getDependComponent(this); if (depend) { return cc.errorID(3626, cc.js.getClassName(this), cc.js.getClassName(depend)); } } if (this._super()) { if (this._enabled && this.node._activeInHierarchy) { cc.director._compScheduler.disableComp(this); } }}

          ① disableComp 同禁用組件

          · onDestroy() 的觸發(fā)時機(jī)

          當(dāng)一個對象的 destroy 調(diào)用以后,會在當(dāng)前幀結(jié)束后才真正銷毀。

          注意:是當(dāng)前幀結(jié)束后銷毀,而不是下一幀銷毀。



          相關(guān)源碼路徑:
          resources\engine\cocos2d\core\platform\CCObject.js

          deferredDestroy:銷毀所有待銷毀隊列中的數(shù)據(jù)。

          function deferredDestroy () { var deleteCount = objectsToDestroy.length; for (var i = 0; i < deleteCount; ++i) { var obj = objectsToDestroy[i]; if (!(obj._objFlags & Destroyed)) {            // 立即銷毀 obj._destroyImmediate(); } } // if we called b.destory() in a.onDestroy(), objectsToDestroy will be resized, // but we only destroy the objects which called destory in this frame. if (deleteCount === objectsToDestroy.length) { objectsToDestroy.length = 0;    } else { objectsToDestroy.splice(0, deleteCount); }
          if (CC_EDITOR) { deferredDestroyTimer = null; }}

          官方文檔:

          生命周期回調(diào):

          https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html


          往期精彩

          ?

          ?

          瀏覽 198
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  四虎国产精品成人久久 | 青青草手机免费在线看片 | 欧美一級黃色A片免費看 | 在线无码高清视频 | 鸡巴美女91 |