Cocos Creator之AssetBundle(基础)

一, 前言

     从CC 2.4开始,官方提出了一个新的概念AssetBundle,他兼容resources.从字面上来讲, 他就叫做"资源包". 他的意义很像Egret的资源包管理. 既是对资源进行包级别的管理.

二, 准备

    1, 准备声音资源Shot.mp3 , 资源目录及配置如下:

Cocos Creator之AssetBundle(基础)

 2, 准备资源加载信息, 如下

let resPkg: Map<string,IResPkgModel> = new Map<string, IResPkgModel>( [
    [
        "Sounds",
        {
            assetType: AudioClip,
            urls: [
                "Shot" ,
                //"Open"
            ]
        }
    ]
] );

注: ①,Sounds为包名称

     ②, assetType: 包中资源的类型

     ③, urls : 包中各资源的路径(从Sounds开始)

附: IResPkgModel接口如下:

export interface IResPkgModel{
    /**资源类型*/
    assetType: Asset;
    /**资源地址*/
    urls: Array<string>;
}

三, 需要用到的API

      step1 : cc.assetManager.loadBundle 加载包体对应的 AssetManager.Bundle , 得到bundle句柄 . 如Sounds胞体

      step2: 使用得到的bundle句柄使用bundle.load加载包体中对应的资源

四, 包加载器设计方案

      ①, 解析加载资源信息, 得到各个包的bundle句柄

            public preloadResPkg( resPkg:Map<string,IResPkgModel>, progressFunc: ( now: number, total: number ) => void, endFunc : () => void): void

      ②,根据 包体名称及 其中的url得到对用的资源

            public getAsset<T extends Asset>( abName: string, url: string): T|null

具体代码如下:

import { Component, assetManager, Asset, AssetManager } from "cc";
import {IResPkgModel} from "./IResPkgModel";

/**
 * 资源加载器
 */
export class ResMGr extends Component {
    public static instance: ResMGr;
    private total: number = 0;
    private now: number = 0;
    private totalAb: number = 0;
    private nowAb: number = 0;

    // @ts-ignore
    private progressFunc:  ( now: number, total: number ) => void;
    // @ts-ignore
    private endFunc: () => void;
    // @ts-ignore
    private abBunds: Map<string, AssetManager.Bundle> = new Map<string,  AssetManager.Bundle>();
    protected onLoad(): void{
        if( !ResMGr.instance ){
            ResMGr.instance = this;
        }else{
            this.destroy();
            return;
        }
    }

    public preloadResPkg( resPkg:Map<string,IResPkgModel>, progressFunc: ( now: number, total: number ) => void, endFunc : () => void): void{
        // step1: 加载我们的ab包进来
        this.total = 0;
        this.now = 0;
        this.totalAb = 0;
        this.nowAb = 0;

        this.progressFunc = progressFunc;//进度函数
        this.endFunc = endFunc;//结束函数

        let keys: IterableIterator<string> = resPkg.keys();
        let resultKey: IteratorResult<string> = keys.next();
        while( resultKey.done == false ){
            this.totalAb ++;//统计本次需要加载的Ab包
            this.total += (resPkg.get( resultKey.value ) as IResPkgModel).urls.length;
            resultKey = keys.next();
        }

        keys = resPkg.keys();
        resultKey = keys.next();
        while ( resultKey.done == false ){
            this.loadAssetsBundle( resultKey.value , () => {
                this.nowAb ++;
                if( this.nowAb === this.totalAb){
                    this.loadAssetsInAssetsBundle( resPkg );
                }
            } );
            resultKey = keys.next();
        }
        //end
    }

    private loadAssetsBundle( abName: string, endFunc: () => void ): void{
        assetManager.loadBundle( abName, ( err, bundle ) => {
            if( err != null ){
                console.log(`[ResMgr]: Load AssetsBundle Error: ${abName}`);
                if( this.abBunds.has( abName ) ){
                    this.abBunds.delete( abName );
                }
            }else{
                console.log(`[ResMgr]: Load AssetsBundle Success: ${abName}`);
                this.abBunds.set( abName, bundle );
            }
            if( endFunc ){
                endFunc();
            }
        } );
    }

    private loadAssetsInAssetsBundle( resPkg: Map<string,IResPkgModel> ): void{
        let  keys: IterableIterator<string> = resPkg.keys();
        let resultKey: IteratorResult<string> = keys.next();
        let resultValue: IResPkgModel;
        let urlSet: Array<string>;
        let typeClass: Asset;
        while ( resultKey.done == false ){
            resultValue = resPkg.get( resultKey.value ) as IResPkgModel;
            urlSet = resultValue.urls;
            typeClass = resultValue.assetType;
            for ( let i: number = 0; i < urlSet.length; i ++ ){
                if( this.abBunds.has(resultKey.value) ){
                    this.loadRes( this.abBunds.get(resultKey.value) as AssetManager.Bundle, urlSet[i], typeClass );
                }
            }
            resultKey = keys.next();
        }
    }

    private loadRes( abBundle: AssetManager.Bundle, url: string, typeClass: Asset ): void{
        //@ts-ignore
        abBundle.load( url, typeClass,(error, asset) => {
            this.now++;
            if (error) {
                console.log(`load Res ${url} error : ${error}`);
            } else {
                console.log(`load Res ${url}  sucess!`);
            }
            this.progressFunc && this.progressFunc(this.now, this.total);
            if (this.now >= this.total) {
                this.endFunc && this.endFunc();
            }
        } );
    }

    /**
     * 获取包内资源
     * @param abName
     * @param url
     */
    public getAsset<T extends Asset>( abName: string, url: string): T|null{
        let bondule: AssetManager.Bundle|null = assetManager.getBundle(abName);
        if( bondule == null ){
            console.log( `[error]: ${abName} AssetsBundle not loaded!!!` );
            return null;
        }
        return bondule.get( url );
    }
}

五: 使用

    ①, 先解析配置资源信息  ResMGr.instance.preloadResPkg

    ②, 解析完毕后可以得到相应的资源

/**
 * 开始游戏
 */
public startGame(): void{
    //预加载资源
    // @ts-ignore
    ResMGr.instance.preloadResPkg( resPkg, null, ()=> {
        this.enterGameScene();
    } );
    //end
}
//进入游戏
public enterGameScene(): void{
    let sound: AudioClip|null = ResMGr.instance.getAsset<AudioClip>( "Sounds",  "Shot" );
    if(sound != null){
        sound.play();
    }
}


使用 CC 3.0.1 版本

上一篇:Unity内存管理你应该知道的底层原理


下一篇:AssetBundle机制和内存管理(转)