[CocosCreator]封装动态加载资源Ⅱ

        欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530

       使用过CocosCreator开发的小伙伴都知道,动态加载是用起来容易却不好管理的一大功能,稍微处理不当就很容易出现资源没有释放造成资源浪费,甚至内存泄露等问题.我写了一个管理动态加载资源的管理类,专门解决这类问题!如果哪里不对,欢迎指出,一起探讨

 直接进主题:
        相信小伙伴都有读我过一篇关于[CocosCreator]封装动态加载资源文章,他是我第一次对CCC资源加载的动态封装,虽然可以用但是还是有点美中不足.所以今天就给补上,来个锦上添花,嘿嘿......

        先看代码吧:

import Singleton from "../base/Singleton";

/**
 * 动态资源加载管理类
 */
export default class DynamicAssetManager extends Singleton {

    private _assetMap: Map<string, cc.Asset[]> = null;
    private _assetRefCountMap: Map<string, number> = null;

    constructor() {
        super();
        // cc.log(`初始化动态资源加载管理类`);
        this._assetMap = new Map<string, cc.Asset[]>();
        this._assetRefCountMap = new Map<string, number>();
    }

    static get instance() {
        return super.getInstance<DynamicAssetManager>();
    }


    /**
     * 动态加载单个资源
     */
    loadOne<T extends cc.Asset>(uuid: string, url: string, assetType: typeof cc.Asset): Promise<T> {
        if (!uuid || !url) {
            throw new Error(`uuid: ${uuid}, url: ${url} 不能为空`);
        }

        return new Promise<T>((resolve, reject) => {
            cc.resources.load<T>(url, assetType, (err, asset: T) => {
                if (err) {
                    reject(err)
                    return
                }
                this.pushAsset(uuid, asset)
                resolve(asset);
            });
        })
    }

    /**
     * 动态加载多个资源
     */
    loadArray<T extends cc.Asset>(uuid: string, url: string[], assetType: typeof cc.Asset): Promise<T[]> {
        if (!uuid || !url) {
            throw new Error(`uuid: ${uuid}, url: ${url} 不能为空`);
        }
        return new Promise<T[]>((resolve, reject) => {
            cc.resources.load(url, assetType, (err, assets: T[]) => {
                if (err) {
                    reject(err)
                    return
                }
                this.pushAsset(uuid, assets)
                resolve(assets);
            });
        })
    }


    /**
     * 动态加载目录全部资源
     * @param uuid
     * @param url
     */
    loadDir<T extends cc.Asset>(uuid: string, url: string, assetType: typeof cc.Asset): Promise<T[]> {
        if (uuid && url) {
            return new Promise<T[]>((resolve, reject) => {
                cc.resources.loadDir(url, assetType, (err, assets: T[]) => {
                    if (err) {
                        reject(err)
                        return
                    }
                    this.pushAsset(uuid, assets)
                    resolve(assets);
                });
            })
        }
    }

    /**
     * 托管资源
     * @param uuid
     * @param asset
     */
    private pushAsset(uuid: string, asset: cc.Asset | cc.Asset[]): boolean {
        if (asset instanceof Array) {
            for (let as of asset) {
                this.extracted(as, uuid);
            }
        } else {
            this.extracted(asset, uuid);
        }
        return true;

    }

    private extracted(asset: cc.Asset, uuid: string) {
        let assetArray: cc.Asset[] = this._assetMap.get(uuid);
        if (!assetArray) {
            assetArray = [];
        }
        //同一个节点只增加一次计数
        if (assetArray.indexOf(asset) < 0) {
            asset.addRef();
            assetArray.push(asset);
            this._assetMap.set(uuid, assetArray);
        }
    }

    /**
     * 释放资源
     * @param uuid
     * @param source
     */
    pullAsset(uuid: string, source: string) {
        if (uuid) {
            if (this._assetMap.has(uuid)) {
                let assetArray: cc.Asset[] = this._assetMap.get(uuid);
                for (let as of assetArray) {
                    // cc.log(`释放资源:${as.name}`);
                    as.decRef();
                }
                this._assetMap.delete(uuid);
            }
        } else {
            cc.error(`老子无法释放资源:传了个null(寂寞),源头:${source}`);
        }
    }

    /**
     * 当前资源种类数量
     */
    getSize() {
        return this._assetMap.size;
    }

    /**
     * 资源keys
     */
    getKeys() {
        return this._assetMap.keys();
    }

    log() {
        //string, cc.Asset[]
        this._assetMap.forEach((value: cc.Asset[], key: string) => {
            console.log(key, value);
        });

    }
}

这次重写封装是因为,我在敲代码是遇到了需要解决资源同步情况,其实代码与原来的并有多大的变化,首先是我继承单例基类(单例基类我已经在我别的文章发表过了,这里就不重复了),然后我在原先直接返回资源的函数上包装了一层Promise的返回,这个是解决同步的关键!然后是因为返回值Promise的原因,把原来合并在一起能够单个或多个拆分成两个函数了!

        好了这里简单的完善下如何调用以上方法:

首先是如果不系采用同步的方式,直接等到他返回在用的情况,我们直接使用Promise的提供的then函数,具体如下:

DynamicAssetManager.instance.loadOne(this.node.uuid, 预制体路径, cc.Prefab)
.then(
    (prefab: cc.Prefab) => {
        let ui: cc.Node = cc.instantiate(prefab);
        ui.parent = this.node;
    }
)

如果要同步方式如下:

let promise: Promise<cc.SpriteFrame> = DynamicAssetManager.instance.loadOne(this.node.uuid, 路劲, cc.SpriteFrame);
const spriteFrame: cc.SpriteFrame = await promise;

最后还是在onDestroy函数中调用一下释放资源函数就可以里

onDestroy(){
    DynamicAssetManager.instance.pullAsset(this.node.uuid, this.constructor.name);
}

猜你喜欢

转载自blog.csdn.net/qq183293/article/details/129826420