cocoscreator2.4資源加載流程

creator2.4.x 使用cc.AssetsManager管理資源辩恼,使用Asset Bundle 作為資源模塊化工具选侨。
默認(rèn)bundle有internal锐想、main融蹂,分別是引擎內(nèi)置bundle旺订,以及用戶默認(rèn)bundle;
如果主動創(chuàng)建了assets/resources文件夾超燃,還會多一個(gè)resources bundle区拳。

資源加載分為靜態(tài)加載和動態(tài)加載
直接被編輯器節(jié)點(diǎn)樹引用的資源,實(shí)例化時(shí)淋纲,引擎會自動靜態(tài)加載劳闹;
需要動態(tài)加載的資源,一般放在resources目錄下洽瞬,使用cc.resources.loadAny加載本涕;
但是家在資源本質(zhì)上都是通過cc.AssetsManager的pipeline加載管線進(jìn)行加載的

以下是一個(gè)已經(jīng)配置為bundle的文件夾,以及其構(gòu)建目錄


test_bundle

構(gòu)建目錄結(jié)構(gòu)

import文件夾里都是json文件伙窃,存放cc.Asset資源的序列化信息菩颖,bundle所有的資源如cc.SceneAsset、cc.SpriteFrame为障、cc.Texture2D晦闰、cc.TextAsset等都會存在這里。Text或Json這類文本資源直接就內(nèi)容填在這里了鳍怨,非文本資源存則會聲明依賴的是的哪些文件呻右。
native文件夾是真正的資源文件目錄,存放所有非文本資源鞋喇,如圖片声滥、字體等文件。
config.json文件是上面所有這些資源的關(guān)聯(lián)配置描述侦香,存放資源路徑和uuid的對應(yīng)關(guān)系落塑。
以下是uuid解碼后的config.json:

image.png

調(diào)用loadBundle實(shí)際上就是緩存這個(gè)config配置纽疟,并不會緩存相應(yīng)的資源,用到資源前憾赁,必須調(diào)用bundle.load開啟加載流程污朽。

cc.assetManager.loadBundle("test_bundle",(err,bundle:cc.AssetManager.Bundle)=>{
    if(err){
        return;
    }
    bundle.load("xxx/aa",cc.SpriteFrame,(err,frame:cc.SpriteFrame)=>{
        if(err){
            return;
        }
        cc.log("---------frame加載成功")
    });
})

我們今天主要研究的就是this.test_bundle.load("xxx/aa",cc.SpriteFrame)的內(nèi)容了。

簡單描述一下加載流程:

1. parse龙考,根據(jù)path找到uuid蟆肆,"xxx/aa"=>"4ea351e4-bb94-40e1-9aca-d7fd555ba770"
2. combine,根據(jù)uuid找到import下的配置文件路徑 “assets/test_bundle/import/4e/4ea351e4-bb94-40e1-9aca-d7fd555ba770.json”

image.png

3. downloader下載該json配置文件洲愤,parse解析颓芭、反序列化該文件,生成一個(gè)cc.SpriteFrame對象柬赐,但是該對象暫無texture資源亡问。此時(shí)本次加載管線執(zhí)行完畢。
4. 再看看有沒有要加載的依賴肛宋,有的話開啟新的加載管線進(jìn)行加載州藕。此次依賴一個(gè)texture資源,uuid="fd96705f-ace9-45ca-8f15-58f0cf33f12b"
image.png

也是先加載import下的json配置酝陈,然后創(chuàng)建cc.Texture2D對象床玻,它依賴的就是native下的同名文件:
"assets/test_bundle/native/fd/fd96705f-ace9-45ca-8f15-58f0cf33f12b.png"
5. texture資源也加載完成以后,通過setProperties設(shè)置關(guān)聯(lián)沉帮,將該texture賦值給第三步生成的cc.SpriteFrame
6. 加載完成锈死,回調(diào)

畫了個(gè)加載管線的簡易流程圖:
cocoscreator資源加載流程.jpg

下面,我們跟著代碼一步步走

bundle.load調(diào)用cc.AssetManager.loadAny,傳入初始化的options參數(shù)

load (paths, type, onProgress, onComplete) {
    var { type, onProgress, onComplete } = parseLoadResArgs(type, onProgress, onComplete);
    cc.assetManager.loadAny(paths, { __requestType__: RequestType.PATH, type: type, bundle: this.name, __outputAsArray__: Array.isArray(paths) }, onProgress, onComplete);
},

loadAny對參數(shù)進(jìn)行校驗(yàn)穆壕,以適配不同的重載待牵,然后創(chuàng)建Task,開啟一個(gè)管線加載任務(wù)

loadAny (requests, options, onProgress, onComplete) {
    var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
    
    options.preset = options.preset || 'default';
    requests = Array.isArray(requests) ? requests.concat() : requests;
    let task = new Task({input: requests, onProgress, onComplete: asyncify(onComplete), options});
    pipeline.async(task);
}

pipeline是在AssetManager初始化的:

    //this.pipeline管線分為preprocess和load兩個(gè)管道
    this.pipeline = pipeline.append(preprocess).append(load);

pipeline.async即按管道順序異步執(zhí)行喇勋,先調(diào)用preprocess進(jìn)行預(yù)處理缨该,然后拿預(yù)處理后的結(jié)果再調(diào)用load。

preprocess函數(shù)在core/asset-manager/preprocess.js下:

function preprocess (task, done) {
    var options = task.options, subOptions = Object.create(null), leftOptions = Object.create(null);
    ///省略若干行川背,主要是剪裁options
    // transform url
    let subTask = Task.create({input: task.input, options: subOptions});
    var err = null;
    try {
        task.output = task.source = transformPipeline.sync(subTask);
    }
    catch (e) {
        err = e;
        for (var i = 0, l = subTask.output.length; i < l; i++) {
            subTask.output[i].recycle();
        }
    }
    subTask.recycle();
    done(err);
}

先對options參數(shù)進(jìn)行剪裁贰拿,創(chuàng)建一個(gè)新的Task,再調(diào)用transformPipeline管線真正進(jìn)行預(yù)處理熄云,
transformPipeline也是再AssetManager里初始化的膨更,分為parse和combine兩個(gè)管道,
這兩步?jīng)]有異步操作缴允,所以是同步執(zhí)行兩個(gè)管道询一,transformPipeline.sync

this.transformPipeline = transformPipeline.append(parse).append(combine);

parse和combine函數(shù)都在core/assets-manager/urlTransformer.js下,顧名思義,該文件的主要作用就是進(jìn)行url轉(zhuǎn)換健蕊,根據(jù)輸入的資源相對路徑,轉(zhuǎn)換為實(shí)際的資源url踢俄。
parse函數(shù)主要邏輯:

case RequestType.PATH: 
    if (bundles.has(item.bundle)) {
        var config = bundles.get(item.bundle)._config;
        var info = config.getInfoWithPath(item.path, item.type);
        
        if (info && info.redirect) {
            if (!bundles.has(info.redirect)) throw new Error(`you need to load bundle ${info.redirect} first`);
            config = bundles.get(info.redirect)._config;
            info = config.getAssetInfo(info.uuid);
        }

        if (!info) {
            out.recycle();
            throw new Error(`Bundle ${item.bundle} doesn't contain ${item.path}`);
        }
        out.config = config; 
        out.uuid = info.uuid;
        out.info = info;
    }
    out.ext = item.ext || '.json';
    break;

先找到對應(yīng)的bundle配置缩功,再根據(jù)目標(biāo)文件名、Asset類型查找對應(yīng)的資源info都办,然后保存資源uuid嫡锌。
這一操作引擎也有導(dǎo)出,如 var info = bundle.getInfoWithPath('image/a', cc.Texture2D);
本次調(diào)用后琳钉,查到uuid="4ea351e4-bb94-40e1-9aca-d7fd555ba770"

然后是combine

// ugly hack, WeChat does not support loading font likes 'myfont.dw213.ttf'. So append hash to directory
if (item.ext === '.ttf') {
    url = `${base}/${uuid.slice(0, 2)}/${uuid}${ver}/${item.options.__nativeName__}`;
}else {
    url = `${base}/${uuid.slice(0, 2)}/${uuid}${ver}${item.ext}`;
}

其實(shí)就是用資源的uuid拼湊出資源路徑
本次調(diào)用后势木,
base="assets/test_bundle/import",
uuid="4ea351e4-bb94-40e1-9aca-d7fd555ba770",
ver="",item.ext = ".json"
所以url = "assets/test_bundle/import/4e/4ea351e4-bb94-40e1-9aca-d7fd555ba770.json"
preprocess流程這就走完了。

然后是load管道歌懒,就是對得到的資源url進(jìn)行下載啦桌、解析,主要用到loadOneAssetPipeline

var loadOneAssetPipeline = new Pipeline('loadOneAsset', [
    function fetch (task, done) {
        var item = task.output = task.input;
        var { options, isNative, uuid, file } = item;
        var { reload } = options;
        if (file || (!reload && !isNative && assets.has(uuid))) return done();

        packManager.load(item, task.options, function (err, data) {
            item.file = data;
            done(err);
        });
    },

    function parse (task, done) {
        var item = task.output = task.input, progress = task.progress, exclude = task.options.__exclude__;
        var { id, file, options } = item;
        if (item.isNative) {
            parser.parse(id, file, item.ext, options, function (err, asset) {
                if (err) return done(err);
                item.content = asset;
                progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
                files.remove(id);
                parsed.remove(id);
                done();
            });
        }
        else {
            var { uuid } = item;
            if (uuid in exclude) {
                ///省略若干
            }
            else {
                if (!options.reload && assets.has(uuid)) {
                    ///省略若干
                }
                else {
                    parser.parse(id, file, 'import', options, function (err, asset) {
                        if (err) return done(err);
                        asset._uuid = uuid;
                        loadDepends(task, asset, done, true);
                    });
                }
            }
        }
    }
]);

第一步fetch及皂,調(diào)用packManager.load甫男,間接調(diào)用downloader.download,根據(jù)文件的后綴名來調(diào)用對應(yīng)的下載方法验烧,默認(rèn)下載方法映射如下(core/asset-manager/downloader.js):

// dafault handler map
var downloaders = {
    // Images
    '.png' : downloadImage,
    '.jpg' : downloadImage,
    '.bmp' : downloadImage,
    '.jpeg' : downloadImage,
    '.gif' : downloadImage,
    '.ico' : downloadImage,
    '.tiff' : downloadImage,
    '.webp' : downloadImage,
    '.image' : downloadImage,
    '.pvr': downloadArrayBuffer,
    '.pkm': downloadArrayBuffer,

    // Audio
    '.mp3' : downloadAudio,
    '.ogg' : downloadAudio,
    '.wav' : downloadAudio,
    '.m4a' : downloadAudio,

    // Txt
    '.txt' : downloadText,
    '.xml' : downloadText,
    '.vsh' : downloadText,
    '.fsh' : downloadText,
    '.atlas' : downloadText,

    '.tmx' : downloadText,
    '.tsx' : downloadText,

    '.json' : downloadJson,
    '.ExportJson' : downloadJson,
    '.plist' : downloadText,

    '.fnt' : downloadText,

    // font
    '.font' : loadFont,
    '.eot' : loadFont,
    '.ttf' : loadFont,
    '.woff' : loadFont,
    '.svg' : loadFont,
    '.ttc' : loadFont,

    // Video
    '.mp4': downloadVideo,
    '.avi': downloadVideo,
    '.mov': downloadVideo,
    '.mpg': downloadVideo,
    '.mpeg': downloadVideo,
    '.rm': downloadVideo,
    '.rmvb': downloadVideo,

    // Binary
    '.binary' : downloadArrayBuffer,
    '.bin': downloadArrayBuffer,
    '.dbbin': downloadArrayBuffer,
    '.skel': downloadArrayBuffer,

    '.js': downloadScript,

    'bundle': downloadBundle,

    'default': downloadText

};

我們也可以自定義下載功能,如:cc.assetManager.downloader.register(".png",func)

本次調(diào)用downloadJson方法板驳,回調(diào)一個(gè)json對象:

var downloadJson = function (url, options, onComplete) {
    options.responseType = "json";
    downloadFile(url, options, options.onFileProgress, function (err, data) {
        if (!err && typeof data === 'string') {
            try {
                data = JSON.parse(data);
            }
            catch (e) {
                err = e;
            }
        }
        onComplete && onComplete(err, data);
    });
};

然后存儲該json對象,

packManager.load(item, task.options, function (err, data) {
    item.file = data;
    done(err);
});

這就下載完了碍拆,然后調(diào)用parse.parse進(jìn)行解析若治,也是根據(jù)后綴名查找對應(yīng)的解析方法:

var parsers = {
    '.png' : parser.parseImage,
    '.jpg' : parser.parseImage,
    '.bmp' : parser.parseImage,
    '.jpeg' : parser.parseImage,
    '.gif' : parser.parseImage,
    '.ico' : parser.parseImage,
    '.tiff' : parser.parseImage,
    '.webp' : parser.parseImage,
    '.image' : parser.parseImage,
    '.pvr' : parser.parsePVRTex,
    '.pkm' : parser.parsePKMTex,
    // Audio
    '.mp3' : parser.parseAudio,
    '.ogg' : parser.parseAudio,
    '.wav' : parser.parseAudio,
    '.m4a' : parser.parseAudio,

    // plist
    '.plist' : parser.parsePlist,
    'import' : parser.parseImport
};

同樣,也可以自定義解析功能,如:cc.assetManager.parsers.register(".png",func)

這里用的是parseImport:

parseImport (file, options, onComplete) {
    if (!file) return onComplete && onComplete(new Error('Json is empty'));
    var result, err = null;
    try {
        result = deserialize(file, options);
    }
    catch (e) {
        err = e;
    }
    onComplete && onComplete(err, result);
}

其實(shí)就是反序列化感混,這里就是生成一個(gè)cc.SpriteFrame對象端幼。

parser.parse(id, file, 'import', options, function (err, asset) {
    if (err) return done(err);
    asset._uuid = uuid;
    loadDepends(task, asset, done, true);
});

到這里本次管線就到尾聲了,我們需要的cc.SpriteFrame對象也創(chuàng)建了浩习,但是是一個(gè)空的對象静暂,沒有綁定texture紋理,而loadDepends就是干這個(gè)的谱秽。
就是獲取到所有依賴資源洽蛀,開啟新的加載管線,加載完成以后調(diào)用setProperties進(jìn)行綁定疟赊。
var missingAsset = setProperties(uuid, asset, map);

image.png

如上圖調(diào)用關(guān)系郊供,最終會綁定依賴的紋理,至此加載真正完成近哟,完成回調(diào)驮审。

    bundle.load("xxx/aa",cc.SpriteFrame,(err,frame:cc.SpriteFrame)=>{
        if(err){
            return;
        }
        cc.log("---------frame加載成功")
    });
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疯淫,更是在濱河造成了極大的恐慌地来,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熙掺,死亡現(xiàn)場離奇詭異未斑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)币绩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門蜡秽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缆镣,你說我怎么就攤上這事芽突。” “怎么了董瞻?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵寞蚌,是天一觀的道長。 經(jīng)常有香客問我力细,道長睬澡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任眠蚂,我火速辦了婚禮煞聪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逝慧。我一直安慰自己昔脯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布笛臣。 她就那樣靜靜地躺著云稚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沈堡。 梳的紋絲不亂的頭發(fā)上静陈,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音诞丽,去河邊找鬼鲸拥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛僧免,可吹牛的內(nèi)容都是我干的刑赶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼懂衩,長吁一口氣:“原來是場噩夢啊……” “哼撞叨!你這毒婦竟也來了金踪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤牵敷,失蹤者是張志新(化名)和其女友劉穎胡岔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劣领,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姐军,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尖淘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡著觉,死狀恐怖村生,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饼丘,我是刑警寧澤趁桃,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站肄鸽,受9級特大地震影響卫病,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜典徘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一蟀苛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逮诲,春花似錦帜平、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至齐唆,卻和暖如春嗤栓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箍邮。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工茉帅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人媒殉。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓担敌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親廷蓉。 傳聞我的和親對象是個(gè)殘疾皇子全封,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容