微信小遊戲開發之場景切換和常駐節點傳遞數據

主題

  1. 場景切換
  2. 場景間數據傳遞方式
  3. 小遊戲全局背景音效

特別說明

CocosCreator微信小遊戲開發系列文章,是我在逐步開發過程中,基於官方文檔之上,記錄一些重點內容,以及對官方文檔中有些知識點的補充和分析。

正文

引擎同時只會運行一個場景,當切換新場景時,默認會將當前場景內所有節點和其他實例銷燬。

1. 場景切換

假設從場景A切換到新場景B,中間會經歷那些過程呢?

場景B預加載preloadScene -> 場景B的onLoad -> 場景B的onStart -> 跳轉到場景B -> 場景A資源釋放

//後臺靜默預加載新場景,
cc.director.preloadScene("table", function () {
    cc.log("Next scene preloaded");
});
//預加載沒完成,也可以調用
cc.director.loadScene("MyScene");
1.1 預加載階段做了什麼?
  • 會加載場景B中引用的靜態資源,即屬性檢查器中引用的資源

  • 場景B中cc.resources和cc.assetManager加載的資源屬於動態加載,不會在preloadScene階段預加載

1.2 在onLoad和onStart方法中執行了過於耗時的操作,會導致頁面黑屏或者跳轉到場景B的速度很慢,造成無響應的假象。
1.3 場景A資源釋放

場景A中引用的靜態資源會被自動釋放,但是動態加載的資源則需要開發者自己手動釋放。

  • 對場景內單個資源的釋放,可以使用release函數:
//cc.resources.load加載的單個資源釋放,可以調用cc.resources.release
cc.resources.release("test assets/image", cc.SpriteFrame);
cc.resources.release("test assets/anim");

//也可以使用 cc.assetManager.releaseAsset 來釋放特定的 Asset 實例。
cc.assetManager.releaseAsset(spriteFrame);
  • 單個資源出現被多個節點或者組件複用時,Asset Manager提供了一套基於引用計數的資源釋放機制:
//每個組件加載資源時addRef()增加一個資源引用
cc.resources.load('image', cc.SpriteFrame, (err, spriteFrame) => {
  this.spriteFrame = spriteFrame;
  spriteFrame.addRef();
});

//每個組件destroy時,對應的調用decRef()減少一個資源引用
this.spriteFrame.decRef();
this.spriteFrame = null;

問題場景:

場景A在onLoad中動態加載了資源D,切到場景B中也動態加載了資源D,而根據引用計數的資源釋放機制用法,在場景A中加載資源D時要addRef = 1,場景A銷燬時要decRef = 0,資源D被釋放了,但是這時候場景B中又動態加載了資源D要addRef + 1,就會出現場景B中資源D的isValid=false,導致資源無法顯示了。而如果不decRef,場景B中的資源D會因爲refCount不爲0,而不會被釋放。

結論:

從這個問題理解,引用計數的資源釋放機制,應當是針對單個場景中多個組件引用相同資源D,才適合引用計數,在場景銷燬時用來控制資源是否銷燬的。

釋放資源,要根據資源在各場景中的實際使用情形,結合release和引用計數來使用。

2. 場景間數據傳遞方式

2.1 常駐節點傳遞數據

場景A跳轉場景B時,如何傳遞參數數據給場景B使用呢?

“cc.director.loadScene”方法沒有攜帶參數啓動場景的功能,CocosCreator是通過“常駐節點”進行場景資源管理和參數傳遞。“常駐節點”怎麼理解呢?

當切換新場景時,默認會將當前場景內所有節點和其他實例銷燬,而所謂“常駐節點”,是指在場景切換時不被自動銷燬,常駐內存中的節點組件。那這個常駐節點應當建在哪裏呢?

首先CocosCreator是推薦使用Canvas節點作爲渲染根節點的,並且微信小遊戲強制要求渲染根節點必須是Canvas。常駐節點創建的位置是和Canvas節點平級,即不能作爲Canvas節點的子節點,而是應當在場景的根節點下,如下圖所示。分析原因可能有兩種:

  • 如果常駐節點在Canvas內,因爲節點不會被銷燬,會導致Canvas節點也不能銷燬,多切換幾個場景,內存可能就已經滿了;
  • 從節點功能上看,常駐節點只是空節點,而Canvas屬於渲染節點,它的子節點都是用於渲染UI使用,所以也不應該放到Canvas節點下。

將StartData節點設置成常駐節點:

  //添加dataNode爲常駐節點
  cc.game.addPersistRootNode(this.dataNode);
  //設置dataNode要傳遞的數據
  this.dataNode.data = {data : "123"}

在新場景中獲取傳遞的數據:

  onLoad() {
      //從上一個場景的常駐節點上獲取當前場景需要使用的參數
      var startData = cc.director.getScene().getChildByName('StartData');
      if (startData) {
          this.data = startData.data;
          // cc.log('頁面傳遞的參數,從常駐節點中獲得data:', this.data);
          
          //取消一個節點的常駐屬性
          cc.game.removePersistRootNode(startData);
      }
      ...
  },

注意: cc.game.removePersistRootNode 並不會立即銷燬指定節點,只是將節點還原爲可在場景切換時銷燬的節點。

2.2 使用全局變量

定義全局變量 window.Global:

  window.Global = {
      data: null
  };

由於所有腳本都強制聲明爲 "use strict",因此定義全局變量時的 window. 不可省略。
接着在需要使用的地方可直接初始化 Global 並訪問它:

  // home.js

  cc.Class({
      extends: cc.Component,

      onLoad: function () {
          Global.data = { data : "123"};
      },
      
      // start 會在 onLoad 之後執行,所以這時 Global 已經初始化過了
      start: function () {
          this.txtLabel.string = Global.data.data;
      }
  });

注意:

  • 不推薦濫用全局變量;
  • 訪問全局變量時,需確保全局變量已初始化和賦值,否則將會拋出異常;
  • 定義全局變量時,不能和系統已有的全局變量重名;
  • 你需要小心確保全局變量使用之前都已初始化和賦值。

3. 小遊戲全局背景音效

全局背景音樂的播放,看完上面的內容應當知道怎麼實現了吧,如果還不知道做的,你可以再仔細往上看一看。

  1. 創建常駐節點AudioNode

  2. 編寫AudioManager.js音樂播放腳本

cc.Class({
  extends: cc.Component,

  properties: {
      bgMusic: {
          url: cc.AudioClip,
          default: null
      },
  },

  onLoad() {
      cc.game.addPersistRootNode(this.node);

      if (!this.bgMusic) {
            cc.assetManager.loadRemote('https://www.test.com/game_bgm.mp3', function(err, audio) {
                if (err) {
                    console.log("加載失敗:" + err);
                }
    
                if (audio instanceof cc.AudioClip) {
                    this.bgMusic = audio;
                    //停止再開啓背景音樂
                    this.playBgMusic();
                }
            }.bind(this));
        } else {
            this.playBgMusic();
        }
  },


  playBgMusic() {
      if (this.bgMusic) {
          this.bgMusicChannel = cc.audioEngine.play(this.bgMusic, true, 0.3)
      }
  },

  stopBgMusic: function () {
      if (this.bgMusicChannel !== undefined) {
          cc.audioEngine.stop(this.bgMusicChannel);
          this.bgMusicChannel = undefined;
      }
  },

});
  1. 把AudioManager.js腳本掛載到AudioNode節點上

如果在屬性面板沒有設置bgMusic播放的音頻資源,則動態播放默認音頻資源。

結尾

既然您看到這了,說明文章對你還有用,幫忙點個贊再走吧,謝謝!

關注我的公衆號「掉隊程序員」,持續輸出更多內容!

自己動手寫,分解項目中的各個模塊需求,通過查文檔和搜索Cocos社區,解決碰到的問題,最終在微信上線了下面這款微信小遊戲《成語錦衣衛》,歡迎大家掃碼體驗,並作爲參考項目模版,開發出屬於自己的小遊戲

預告

下一節和朋友們說一說:微信登錄功能的實現,CocosCreator第三方服務騰訊TCB雲開發踩坑和微信雲開發

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