Creator | 資源管理:Asset Bundle 全解析:配置/構(gòu)造/優(yōu)先級(jí)及資源引用關(guān)系/加載/預(yù)加載/釋放等

官方文檔:

Asset Bundle 介紹:

https://docs.cocos.com/creator/manual/zh/asset-manager/bundle.html

配置和加載 Asset Bundle:

https://docs.cocos.com/creator/manual/zh/scripting/asset-bundle.html

本文中 Asset Bundle 簡稱AB包

外部資源 = 不在本AB包目錄內(nèi)的資源(包括內(nèi)置AB包和其他AB包)

// AB包的定義 //

AB包作為資源模塊化工具,允許開發(fā)者按照項(xiàng)目需求將貼圖、腳本、場(chǎng)景等資源劃分在多個(gè)AB包中,然后在游戲運(yùn)行過程中,按照需求去加載不同的AB包,以減少啟動(dòng)時(shí)需要加載的資源數(shù)量,從而減少首次下載和加載游戲時(shí)所需的時(shí)間,同時(shí)可以減少內(nèi)存占用
AB包可以按需求隨意放置,比如可以放在遠(yuǎn)程服務(wù)器、本地、或者小游戲平臺(tái)的分包,也可以跨項(xiàng)目復(fù)用,用于加載子項(xiàng)目中的AB包

// 配置AB包 //

AB包是以 文件夾 為單位進(jìn)行配置的,并且 不支持嵌套不允許同名,即使是在不同的文件夾下,也不允許同名

圖片

配置方法:

① 將項(xiàng)目中的場(chǎng)景、資源、代碼等內(nèi)容按照需求劃分到不同的文件夾

圖片

② 單擊該文件夾,屬性檢查器 中就會(huì)出現(xiàn)一個(gè) 配置為 Bundle 的選項(xiàng),勾選后會(huì)出現(xiàn)如下圖的配置項(xiàng)

配置完成后點(diǎn)擊右上方的 應(yīng)用 按鈕,這個(gè)文件夾就被配置為AB包了,然后在 構(gòu)建發(fā)布 面板選擇對(duì)應(yīng)的平臺(tái)進(jìn)行構(gòu)建

注意:

① Creator 有 4 個(gè) 內(nèi)置AB包,包括 resources、internal、main、start-scene,在設(shè)置 Bundle 名稱 時(shí)請(qǐng)不要使用這四個(gè)名稱

② 小游戲分包只能放在本地,不能配置為遠(yuǎn)程包,所以當(dāng) 壓縮類型 設(shè)置為 小游戲分包 時(shí),配置為遠(yuǎn)程包 項(xiàng)不可勾選

③ Zip 壓縮類型主要是為了降低網(wǎng)絡(luò)請(qǐng)求數(shù)量,如果放在本地,不用網(wǎng)絡(luò)請(qǐng)求,則沒什么必要,所以要求與 配置為遠(yuǎn)程包 搭配使用

// 內(nèi)置AB包 //

構(gòu)建后,項(xiàng)目中所有的資源都會(huì)被分類放到AB包中, 其中自定義AB包中的資源放到對(duì)應(yīng)的AB包中,其他的資源則會(huì)被分類放到 4 個(gè)內(nèi)置AB包中

| 內(nèi)置AB包 | 功能說明 | 優(yōu)先級(jí) |
| internal | 存放所有內(nèi)置資源以及其依賴資源 | 11
|
| main | 存放所有在** 構(gòu)建發(fā)布** 面板的 參與構(gòu)建場(chǎng)景 中勾選的場(chǎng)景以及其依賴資源 | 7
|
| resources | 存放 **resources **目錄下的所有資源以及其依賴資源 | 8
|
| start-scene | 如果在 構(gòu)建發(fā)布 面板中勾選了 初始場(chǎng)景分包,則首場(chǎng)景將會(huì)被構(gòu)建到 start-scene 中 | 9
|

注意:

start-scene 目前僅支持小游戲平臺(tái),如果在 構(gòu)建發(fā)布 面板中勾選 初始場(chǎng)景分包,則首場(chǎng)景會(huì)被放到內(nèi)置AB包的 start-scene 中,從而實(shí)現(xiàn)分離首場(chǎng)景

// 構(gòu)建AB包 //

在構(gòu)建時(shí),配置為AB包的 文件夾中****的資源(包含場(chǎng)景、代碼和其他資源)以及 **文件夾外的相關(guān)依賴資源 **都會(huì)被合并到同一個(gè)AB包中

構(gòu)建完成后,該文件夾會(huì)被打包到對(duì)應(yīng)平臺(tái)發(fā)布包目錄下的 **assets **文件夾中

但有以下兩種特殊情況:

① 配置AB包時(shí),若勾選了 配置為遠(yuǎn)程包,則這個(gè)文件夾會(huì)被打包到對(duì)應(yīng)平臺(tái)發(fā)布包目錄下的 remote 文件夾中

圖片
圖片

② 配置AB包時(shí),若設(shè)置了 壓縮類型小游戲分包,則這個(gè)文件夾會(huì)被打包到對(duì)應(yīng)平臺(tái)發(fā)布包目錄下的 **subpackages **文件夾中

圖片
圖片

assets、remote、subpackages 這三個(gè)文件夾中包含的每個(gè)文件夾都是一個(gè)AB包

assets:

圖片

remote:

圖片

// AB包的構(gòu)造 //

在構(gòu)建時(shí),配置為AB包的文件夾中的所有 代碼資源,會(huì)進(jìn)行以下處理:

  • 代碼:文件夾中的所有代碼會(huì)根據(jù)發(fā)布平臺(tái)合并成一個(gè) index.js 或 game.js 的入口腳本文件,并從主包中剔除

  • 資源:文件夾中的所有資源以及文件夾外的相關(guān)依賴資源都會(huì)放到 import 或 native 目錄下

  • 資源配置:所有資源的配置信息包括路徑、類型、版本信息都會(huì)被合并成一個(gè) config.json 文件

構(gòu)建后生成的AB包目錄結(jié)構(gòu)如下圖所示:

圖片
  • import: 資源描述 json 的存放目錄

  • **native **:資源文件的存放目錄

  • config.json:所有資源的配置信息,包括路徑、類型、版本信息

  • index.js:文件夾中的所有代碼

// AB包的優(yōu)先級(jí)和資源引用關(guān)系 //

當(dāng)文件夾設(shè)置為AB包后,構(gòu)建后 Creator 會(huì)將 **文件夾中的資源 **以及 **文件夾外的相關(guān)依賴資源 **都合并到同一個(gè)AB包中

假設(shè)AB包 A(也可以是內(nèi)置的AB包)中的 asset X 同時(shí)被AB包 B、C、D 引用, 按照剛才所說,構(gòu)建后,AB包 A、B、C、D 中會(huì)分別存放一份 asset X,這明顯違背減小包體的原則,那么構(gòu)建后 asset X 究竟該放到哪個(gè)AB包中呢?

圖片

此時(shí)就需要通過調(diào)整AB包的優(yōu)先級(jí)來決定資源的存放位置

圖片

Creator 開放了 10 個(gè)可供配置的優(yōu)先級(jí),編輯器在構(gòu)建時(shí)將會(huì)按照優(yōu)先級(jí) 從大到小 的順序?qū)B包依次進(jìn)行構(gòu)建(別忘了 Creator 的內(nèi)置AB包)

了解了AB包的優(yōu)先級(jí)后我們?cè)賮碛懻撓?asset X 的存放情況

① AB包 **優(yōu)先級(jí)相同 **的情況下,引用外部資源時(shí),構(gòu)建后,該資源會(huì)在每個(gè)AB包中復(fù)制一份,此時(shí)不同的AB包之間沒有依賴關(guān)系,可按任意順序加載,但由于該資源會(huì)被復(fù)制N份,這樣會(huì)引起包體的增大

② AB包 **優(yōu)先級(jí)不同 **的情況下,引用外部資源時(shí),構(gòu)建后,該資源會(huì)放在 **優(yōu)先級(jí)高 的AB包(包括內(nèi)置AB包)中,優(yōu)先級(jí)低 **的AB包只會(huì)存儲(chǔ)一條記錄信息。此時(shí)優(yōu)先級(jí)低的AB包會(huì) **依賴 **優(yōu)先級(jí)高的AB包。如果想在優(yōu)先級(jí)低的AB包中加載此資源,必須在加載優(yōu)先級(jí)低的AB包 之前 先加載(loadBundle)優(yōu)先級(jí)高的AB包

圖片

因此項(xiàng)目中那些頻繁被其他AB包使用的資源,應(yīng)該放置在優(yōu)先級(jí)較高的AB包中,比如 內(nèi)置AB包 main,或者為了減少首包的大小,可以放到 自定義AB包中,然后修改該AB包的 Bundle 優(yōu)先級(jí)為較高的值,在合適的時(shí)機(jī)調(diào)用 cc.assetManager.loadBundle

// AB包的腳本 //

Creator 支持腳本分包,如果AB包中包含腳本文件,則所有腳本會(huì)被合并為一個(gè) js 文件,并從主包中剔除,在加載AB包時(shí),就會(huì)去加載這個(gè) js 文件

注意:

  1. 有些平臺(tái)不允許加載遠(yuǎn)程的腳本文件,例如微信小游戲,在這些平臺(tái)上,Creator 會(huì)將AB包中的代碼拷貝到** src/scripts** 目錄下,從而保證正常加載

  2. 不同AB包中的腳本建議最好不要互相引用,否則可能會(huì)導(dǎo)致在運(yùn)行時(shí)找不到對(duì)應(yīng)腳本,如果需要引用某些類或變量,可以將該類和變量暴露在一個(gè)你自己的全局命名空間中,從而實(shí)現(xiàn)共享,類似:cc["MyBundle"] = MyBundle;

注意:
雖然腳本文件也是資源的一種,但是腳本文件只會(huì)合并到本AB包中的 index.js,并不會(huì)像其他資源一樣復(fù)制到其他AB包中,無論優(yōu)先級(jí)是多少

node_modules中的第三方腳本文件只會(huì)合并到 **內(nèi)置AB包 main **中的 index.js

注意:

在通過 API(loadBundle)加載AB包時(shí),就會(huì)加載AB包中的 index.js,一旦加載后,就會(huì)一直存在內(nèi)存中,移除AB包(removeBundle)也不會(huì)釋放,再次加載AB包時(shí)也不會(huì)重新加載腳本

// 加載AB包 //

1加載AB包

引擎提供了一個(gè)統(tǒng)一的 API cc.assetManager.loadBundle 來加載AB包,加載時(shí)需要傳入AB包在配置面板中的 Bundle 名稱 或者AB包的 url

但當(dāng)你復(fù)用其他項(xiàng)目的AB包時(shí),則只能通過 url 進(jìn)行加載

使用方法如下:

cc.assetManager.loadBundle("MyBundle", (err: Error, bundle: cc.AssetManager.Bundle) => {

通過 url 加載AB包,其過程和 cc.assetManager.loadRemote 相同,加載成功后,該AB包會(huì)以 **文件夾 **的形式保存在本地緩存目錄,如 win32 模擬器:

圖片

loadBundle 后,只是將該AB包中的 **資源清單 **和 腳本文件 緩存到本地,只有在 bundle 調(diào)用 load 或者 preload 時(shí),才會(huì)緩存對(duì)應(yīng)的資源

**cacheList.json **文件中以 { url: object } 的形式記錄遠(yuǎn)程資源信息,以后再次加載該遠(yuǎn)程資源時(shí)則直接使用緩存中的資源文件

圖片

cc.assetManager.loadBundle 還支持傳入用戶空間中的路徑來加載用戶空間中的AB包

通過對(duì)應(yīng)平臺(tái)提供的 **下載 **接口將AB包提前下載到用戶空間中,然后再使用 loadBundle 進(jìn)行加載,開發(fā)者就可以完全自己管理AB包的下載與緩存過程,更加靈活

// 提前下載某個(gè) Asset Bundle 到用戶空間 pathToBundle 目錄下。需要保證用戶空間下的 Asset Bundle 和對(duì)應(yīng)原始 Asset Bundle 的結(jié)構(gòu)和內(nèi)容完全一樣

注意:

在配置AB包時(shí),若勾選了 配置為遠(yuǎn)程包,那么構(gòu)建時(shí)請(qǐng)?jiān)?構(gòu)建發(fā)布 面板中填寫 資源服務(wù)器地址

通過 cc.assetManager.bundles 可以看到當(dāng)前內(nèi)存中已加載 bundle 的集合以及 bundle 的具體信息

console.log(cc.assetManager.bundles);
圖片

2AB包的版本

AB包在更新上延續(xù)了 Creator 的 MD5 方案

當(dāng)你需要更新遠(yuǎn)程服務(wù)器上的AB包時(shí),請(qǐng)?jiān)?構(gòu)建發(fā)布 面板中勾選 MD5 Cache 選項(xiàng),此時(shí)構(gòu)建出來的AB包中的 config.json 文件名會(huì)附帶 Hash 值

如圖所示:

圖片

在加載AB包時(shí) 不需要 額外提供對(duì)應(yīng)的 Hash 值,Creator 會(huì)在 **settings.js **中查詢對(duì)應(yīng)的 Hash 值,并自動(dòng)做出調(diào)整
但如果你想要將相關(guān)版本配置信息存儲(chǔ)在服務(wù)器上,啟動(dòng)時(shí)動(dòng)態(tài)獲取版本信息以實(shí)現(xiàn)熱更新,你也可以手動(dòng)指定一個(gè)版本 Hash 值并傳入 loadBundle 中,此時(shí)將會(huì)以傳入的 Hash 值為準(zhǔn):

cc.assetManager.loadBundle("MyBundle", { version: "fbc07" }, (err: Error, bundle: cc.AssetManager.Bundle) => {

這樣就能繞過緩存中的老版本文件,重新下載最新版本的AB包

3加載AB包中的資源

在通過 API(loadBundle )加載AB包時(shí),引擎并 **沒有加載 **AB包中的所有資源,而是只 **加載 **AB包的 資源清單(config.json),以及包含的 所有腳本(index.js)

即AB包中的腳本會(huì)被加載到內(nèi)存中,但是AB包中的資源并不會(huì)加載到內(nèi)存中,如果需要加載其中的資源,還需要 bundle.load("prefab")

當(dāng)AB包加載完成后,會(huì)返回一個(gè) cc.AssetManager.Bundle 類的實(shí)例,這個(gè)實(shí)例就是AB包 API 的主要入口,我們可以通過實(shí)例上的 load 方法來加載AB包中的資源,此方法的參數(shù)與 cc.resources.load 相同,只需要傳入資源相對(duì)AB包的路徑即可,但需要注意的是,路徑的結(jié)尾處 不能 包含文件擴(kuò)展名

圖片
// 加載 prefab

AB包還提供了 loadDir 方法來批量加載相同目錄下的多個(gè)資源,此方法的參數(shù)與 cc.resources.loadDir 相似,只需要傳入該目錄相對(duì)AB包的路徑即可

// 加載 textures 目錄下的所有資源

注意:

cc.resources 和 cc.AssetManager.Bundle 分別提供了 load 和 loadDir 接口,且加載后的資源都需要我們手動(dòng)管理

load 后資源的引用計(jì)數(shù)為 0,而 loadDir 后資源的引用計(jì)數(shù)要視情況而定

① loadDir 不指定資源類型時(shí),會(huì)加載文件夾內(nèi)的所有資源

cc.resources.loadDir("dir", (err, assets) => { });

我們以圖片資源為例:

API 將 Texture2D 和 SpriteFrame 一起加載出來,因此 Texture2D 的資源會(huì)被 SpriteFrame 引用到,其引用計(jì)數(shù)成為 1,而 SpriteFrame 未被其他資源引用,其引用計(jì)數(shù)依然是 0

② loadDir 指定資源類型時(shí),只加載文件夾內(nèi)該類型的資源

cc.resources.loadDir("dir", cc.SpriteFrame, (err, spriteFrames) => { });

此時(shí)加載的資源之間不存在相互引用關(guān)系,所以其引用計(jì)數(shù)都是 0

· Texture 和 SpriteFrame 資源類型

在 資源管理器 中,圖像資源的左邊會(huì)顯示一個(gè)和文件夾類似的三角圖標(biāo),點(diǎn)擊就可以展開看到它的子資源(sub asset),每個(gè)圖像資源導(dǎo)入后編輯器會(huì)自動(dòng)在它下面創(chuàng)建同名的 SpriteFrame 資源

圖片

SpriteFrame 是核心渲染組件 Sprite 所使用的資源,設(shè)置或替換 Sprite 組件中的 spriteFrame 屬性,就可以切換顯示的圖像

為什么會(huì)有 SpriteFrame 這種資源?Texture 是保存在 GPU 緩沖中的一張紋理,是原始的圖像資源。而 SpriteFrame 包含兩部分內(nèi)容:記錄了 Texture 及其相關(guān)屬性的 Texture2D 對(duì)象和紋理的矩形區(qū)域,對(duì)于相同的 Texture 可以進(jìn)行不同的紋理矩形區(qū)域設(shè)置,然后根據(jù) Sprite 的填充類型,如 SIMPLE、SLICED、TILED 等進(jìn)行不同的頂點(diǎn)數(shù)據(jù)填充,從而滿足 Texture 填充圖像精靈的多樣化需求。而 SpriteFrame 記錄的紋理矩形區(qū)域數(shù)據(jù)又可以在資源的屬性檢查器中根據(jù)需求自由定義,這樣的設(shè)置讓資源的開發(fā)更為高效和便利。除了每個(gè)文件會(huì)產(chǎn)生一個(gè) SpriteFrame 的圖像資源(Texture)之外,我們還有包含多個(gè) SpriteFrame 的圖集資源(Atlas)類型

4預(yù)加載資源

為了盡可能縮短下載時(shí)間,我們可以使用預(yù)加載

Asset Manager 中的大部分加載接口包括 load、loadDir、loadScene 都有其對(duì)應(yīng)的預(yù)加載版本

圖片
圖片
cc.assetManager.loadBundle("MyBundle", (err: Error, bundle: cc.AssetManager.Bundle) => {

加載接口與預(yù)加載接口所用的參數(shù)是完全一樣的,兩者的區(qū)別在于:

  1. 預(yù)加載只會(huì)下載資源,不會(huì)對(duì)資源進(jìn)行解析和初始化操作

  2. 預(yù)加載在加載過程中會(huì)受到更多限制,例如最大下載并發(fā)數(shù)會(huì)更小

  3. 預(yù)加載的下載優(yōu)先級(jí)更低,當(dāng)多個(gè)資源在等待下載時(shí),預(yù)加載的資源會(huì)放在最后下載

  4. 因?yàn)?預(yù)加載沒有做任何解析操作,所以當(dāng)所有的預(yù)加載完成時(shí),不會(huì)返回任何可用資源

以上優(yōu)化手段充分 降低了預(yù)加載的性能損耗,確保了游戲體驗(yàn)順暢,開發(fā)者可以充分利用游戲過程中的網(wǎng)絡(luò)帶寬縮短后續(xù)資源的加載時(shí)間

因?yàn)轭A(yù)加載沒有去解析資源,所以需要在預(yù)加載完成后配合加載接口進(jìn)行資源的解析和初始化,來完成資源加載

注意:

加載不需要等到預(yù)加載完成后再調(diào)用,開發(fā)者可以在任何時(shí)候進(jìn)行加載。正常加載接口會(huì)直接復(fù)用預(yù)加載過程中已經(jīng)下載好的內(nèi)容,縮短加載時(shí)間

預(yù)加載只會(huì)去 **下載 **必要的資源,并 不會(huì)進(jìn)行資源的反序列化和初始化工作,也就不會(huì)將資源放入內(nèi)存(cc.assetManager.assets)中,所以性能消耗更小,確保了游戲體驗(yàn)流暢

5加載場(chǎng)景

AB包提供了 loadScene 方法用于加載指定 bundle 中的場(chǎng)景,你只需要傳入 場(chǎng)景名 即可

loadScene 與 cc.director.loadScene 不同的地方在于 loadScene 只會(huì)加載指定 AB包中的場(chǎng)景,而不會(huì)運(yùn)行場(chǎng)景,你還需要使用 cc.director.runScene 來運(yùn)行場(chǎng)景

// 加載場(chǎng)景

6獲取AB包

當(dāng)AB包被加載過之后,會(huì)被緩存下來,此時(shí)開發(fā)者可以使用AB包名稱來獲取該 bundle

let bundle = cc.assetManager.getBundle("MyBundle");

// 釋放AB包 //

1釋放AB包中的資源

在資源加載完成后,所有的資源都會(huì)被臨時(shí)緩存到 cc.assetManager 中,以避免重復(fù)加載。當(dāng)然,緩存中的資源也會(huì)占用內(nèi)存,有些資源如果不再需要用到,可以通過以下三種方式進(jìn)行釋放:

① 使用常規(guī)的 cc.assetManager.releaseAsset 方法進(jìn)行釋放

bundle.load("image", cc.SpriteFrame, function (err, spriteFrame) {

② 使用AB包提供的 release 方法,通過傳入路徑和類型進(jìn)行釋放,只能釋放在AB包中的單個(gè)資源,參數(shù)可以與 AB包的 load 方法中使用的參數(shù)一致

bundle.load("image", cc.SpriteFrame, function (err, spriteFrame) {

③使用AB包提供的 releaseAll 方法,此方法與 cc.assetManager.releaseAll 相似,releaseAll 方法會(huì)釋放所有屬于該 bundle 的資源(包括在AB包中的資源以及其外部的相關(guān)依賴資源),請(qǐng)慎重使用

bundle.load("image", cc.SpriteFrame, function (err, spriteFrame) {

意:

在釋放資源時(shí),Creator 會(huì)自動(dòng)處理該資源的依賴資源,開發(fā)者不需要對(duì)其依賴資源進(jìn)行管理

2移除AB包

在加載了AB包之后,此 bundle 會(huì)一直存在整個(gè)游戲過程中,除非開發(fā)者手動(dòng)移除

當(dāng)手動(dòng)移除了某個(gè)不需要的 bundle,那么此 bundle 的緩存也會(huì)被移除,如果需要再次使用,則必須再重新加載一次

let bundle = cc.assetManager.getBundle("MyBundle");

注意:

在移除AB包時(shí),并不會(huì)釋放該 bundle 中加載過的資源

如果需要釋放,請(qǐng)先使用AB包的 release / releaseAll 方法:

let bundle = cc.assetManager.getBundle("MyBundle");
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容