主題
- 場景切換
- 場景間數(shù)據(jù)傳遞方式
- 小游戲全局背景音效
特別說明
CocosCreator微信小游戲開發(fā)系列文章,是我在逐步開發(fā)過程中,基于官方文檔之上,記錄一些重點(diǎn)內(nèi)容脆贵,以及對官方文檔中有些知識點(diǎn)的補(bǔ)充和分析。
正文
引擎同時只會運(yùn)行一個場景起暮,當(dāng)切換新場景時卖氨,默認(rèn)會將當(dāng)前場景內(nèi)所有節(jié)點(diǎn)和其他實(shí)例銷毀。
1. 場景切換
假設(shè)從場景A切換到新場景B,中間會經(jīng)歷那些過程呢双泪?
場景B預(yù)加載preloadScene -> 場景B的onLoad -> 場景B的onStart -> 跳轉(zhuǎn)到場景B -> 場景A資源釋放
//后臺靜默預(yù)加載新場景持搜,
cc.director.preloadScene("table", function () {
cc.log("Next scene preloaded");
});
//預(yù)加載沒完成,也可以調(diào)用
cc.director.loadScene("MyScene");
1.1 預(yù)加載階段做了什么焙矛?
會加載場景B中引用的靜態(tài)資源葫盼,即屬性檢查器中引用的資源
場景B中cc.resources和cc.assetManager加載的資源屬于動態(tài)加載,不會在preloadScene階段預(yù)加載
1.2 在onLoad和onStart方法中執(zhí)行了過于耗時的操作村斟,會導(dǎo)致頁面黑屏或者跳轉(zhuǎn)到場景B的速度很慢贫导,造成無響應(yīng)的假象。
1.3 場景A資源釋放
場景A中引用的靜態(tài)資源會被自動釋放蟆盹,但是動態(tài)加載的資源則需要開發(fā)者自己手動釋放孩灯。
- 對場景內(nèi)單個資源的釋放,可以使用release函數(shù):
//cc.resources.load加載的單個資源釋放逾滥,可以調(diào)用cc.resources.release
cc.resources.release("test assets/image", cc.SpriteFrame);
cc.resources.release("test assets/anim");
//也可以使用 cc.assetManager.releaseAsset 來釋放特定的 Asset 實(shí)例峰档。
cc.assetManager.releaseAsset(spriteFrame);
- 單個資源出現(xiàn)被多個節(jié)點(diǎn)或者組件復(fù)用時,Asset Manager提供了一套基于引用計數(shù)的資源釋放機(jī)制:
//每個組件加載資源時addRef()增加一個資源引用
cc.resources.load('image', cc.SpriteFrame, (err, spriteFrame) => {
this.spriteFrame = spriteFrame;
spriteFrame.addRef();
});
//每個組件destroy時寨昙,對應(yīng)的調(diào)用decRef()減少一個資源引用
this.spriteFrame.decRef();
this.spriteFrame = null;
問題場景:
場景A在onLoad中動態(tài)加載了資源D讥巡,切到場景B中也動態(tài)加載了資源D,而根據(jù)引用計數(shù)的資源釋放機(jī)制用法舔哪,在場景A中加載資源D時要addRef = 1欢顷,場景A銷毀時要decRef = 0,資源D被釋放了捉蚤,但是這時候場景B中又動態(tài)加載了資源D要addRef + 1抬驴,就會出現(xiàn)場景B中資源D的isValid=false,導(dǎo)致資源無法顯示了缆巧。而如果不decRef布持,場景B中的資源D會因為refCount不為0,而不會被釋放陕悬。
結(jié)論:
從這個問題理解题暖,引用計數(shù)的資源釋放機(jī)制,應(yīng)當(dāng)是針對單個場景中多個組件引用相同資源D墩莫,才適合引用計數(shù),在場景銷毀時用來控制資源是否銷毀的逞敷。
釋放資源狂秦,要根據(jù)資源在各場景中的實(shí)際使用情形,結(jié)合release和引用計數(shù)來使用推捐。
2. 場景間數(shù)據(jù)傳遞方式
2.1 常駐節(jié)點(diǎn)傳遞數(shù)據(jù)
場景A跳轉(zhuǎn)場景B時裂问,如何傳遞參數(shù)數(shù)據(jù)給場景B使用呢?
“cc.director.loadScene”方法沒有攜帶參數(shù)啟動場景的功能,CocosCreator是通過“常駐節(jié)點(diǎn)”進(jìn)行場景資源管理和參數(shù)傳遞堪簿∪“常駐節(jié)點(diǎn)”怎么理解呢?
當(dāng)切換新場景時椭更,默認(rèn)會將當(dāng)前場景內(nèi)所有節(jié)點(diǎn)和其他實(shí)例銷毀哪审,而所謂“常駐節(jié)點(diǎn)”,是指在場景切換時不被自動銷毀虑瀑,常駐內(nèi)存中的節(jié)點(diǎn)組件湿滓。那這個常駐節(jié)點(diǎn)應(yīng)當(dāng)建在哪里呢?
首先CocosCreator是推薦使用Canvas節(jié)點(diǎn)作為渲染根節(jié)點(diǎn)的舌狗,并且微信小游戲強(qiáng)制要求渲染根節(jié)點(diǎn)必須是Canvas叽奥。常駐節(jié)點(diǎn)創(chuàng)建的位置是和Canvas節(jié)點(diǎn)平級,即不能作為Canvas節(jié)點(diǎn)的子節(jié)點(diǎn)痛侍,而是應(yīng)當(dāng)在場景的根節(jié)點(diǎn)下朝氓,如下圖所示。分析原因可能有兩種:
- 如果常駐節(jié)點(diǎn)在Canvas內(nèi)主届,因為節(jié)點(diǎn)不會被銷毀赵哲,會導(dǎo)致Canvas節(jié)點(diǎn)也不能銷毀,多切換幾個場景岂膳,內(nèi)存可能就已經(jīng)滿了誓竿;
- 從節(jié)點(diǎn)功能上看,常駐節(jié)點(diǎn)只是空節(jié)點(diǎn)谈截,而Canvas屬于渲染節(jié)點(diǎn)筷屡,它的子節(jié)點(diǎn)都是用于渲染UI使用,所以也不應(yīng)該放到Canvas節(jié)點(diǎn)下簸喂。
將StartData節(jié)點(diǎn)設(shè)置成常駐節(jié)點(diǎn):
//添加dataNode為常駐節(jié)點(diǎn)
cc.game.addPersistRootNode(this.dataNode);
//設(shè)置dataNode要傳遞的數(shù)據(jù)
this.dataNode.data = {data : "123"}
在新場景中獲取傳遞的數(shù)據(jù):
onLoad() {
//從上一個場景的常駐節(jié)點(diǎn)上獲取當(dāng)前場景需要使用的參數(shù)
var startData = cc.director.getScene().getChildByName('StartData');
if (startData) {
this.data = startData.data;
// cc.log('頁面?zhèn)鬟f的參數(shù)毙死,從常駐節(jié)點(diǎn)中獲得data:', this.data);
//取消一個節(jié)點(diǎn)的常駐屬性
cc.game.removePersistRootNode(startData);
}
...
},
注意: cc.game.removePersistRootNode 并不會立即銷毀指定節(jié)點(diǎn),只是將節(jié)點(diǎn)還原為可在場景切換時銷毀的節(jié)點(diǎn)喻鳄。
2.2 使用全局變量
定義全局變量 window.Global:
window.Global = {
data: null
};
由于所有腳本都強(qiáng)制聲明為 "use strict"扼倘,因此定義全局變量時的 window. 不可省略。
接著在需要使用的地方可直接初始化 Global 并訪問它:
// home.js
cc.Class({
extends: cc.Component,
onLoad: function () {
Global.data = { data : "123"};
},
// start 會在 onLoad 之后執(zhí)行除呵,所以這時 Global 已經(jīng)初始化過了
start: function () {
this.txtLabel.string = Global.data.data;
}
});
注意:
- 不推薦濫用全局變量再菊;
- 訪問全局變量時,需確保全局變量已初始化和賦值颜曾,否則將會拋出異常纠拔;
- 定義全局變量時,不能和系統(tǒng)已有的全局變量重名泛豪;
- 你需要小心確保全局變量使用之前都已初始化和賦值稠诲。
3. 小游戲全局背景音效
全局背景音樂的播放侦鹏,看完上面的內(nèi)容應(yīng)當(dāng)知道怎么實(shí)現(xiàn)了吧,如果還不知道做的臀叙,你可以再仔細(xì)往上看一看略水。
創(chuàng)建常駐節(jié)點(diǎn)AudioNode
編寫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;
}
},
});
- 把AudioManager.js腳本掛載到AudioNode節(jié)點(diǎn)上
如果在屬性面板沒有設(shè)置bgMusic播放的音頻資源劝萤,則動態(tài)播放默認(rèn)音頻資源渊涝。
結(jié)尾
既然您看到這了,說明文章對你還有用稳其,幫忙點(diǎn)個贊再走吧驶赏,謝謝!
關(guān)注我的公眾號「掉隊程序員」既鞠,持續(xù)輸出更多內(nèi)容煤傍!
自己動手寫,分解項目中的各個模塊需求嘱蛋,通過查文檔和搜索Cocos社區(qū)蚯姆,解決碰到的問題,最終在微信上線了下面這款微信小游戲《成語錦衣衛(wèi)》洒敏,歡迎大家掃碼體驗龄恋,并作為參考項目模版,開發(fā)出屬于自己的小游戲
預(yù)告
下一節(jié)和朋友們說一說:微信登錄功能的實(shí)現(xiàn)凶伙,CocosCreator第三方服務(wù)騰訊TCB云開發(fā)踩坑和微信云開發(fā)