欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530
使用过CocosCreator开发的小伙伴都知道,动态加载是用起来容易却不好管理的一大功能,稍微处理不当就很容易出现资源没有释放造成资源浪费,甚至内存泄露等问题.我写了一个管理动态加载资源的管理类,专门解决这类问题!如果哪里不对,欢迎指出,一起探讨
直接进主题:
/**
* 动态资源加载管理类
*/
class DynamicAssetManager {
private _assetMap: Map<string, cc.Asset[]> = null;
private _assetRefCountMap: Map<string, number> = null;
/**
* 初始化
*/
init() {
this._assetMap = new Map<string, cc.Asset[]>();
this._assetRefCountMap = new Map<string, number>();
cc.log("动态资源管理器初始化完成");
}
/**
* 动态加载资源(可以同时加载多个资源)
* @param node
* @param url
* @param callBackFun
*/
load(node: cc.Node, url: string | string[], assetType: typeof cc.Asset, callBackFun: Function) {
if (node && url) {
if (Array.isArray(url))
cc.resources.load(url, assetType, (err, assets: cc.Asset[]) => {
if (err) {
cc.error(err.message || err);
return;
}
if (this.pushAsset(node, assets)) {
callBackFun(assets)
}
});
else
cc.resources.load(url, assetType, (err, asset: cc.Asset) => {
if (err) {
cc.error(err.message || err);
return;
}
if (this.pushAsset(node, asset)) {
callBackFun(asset)
}
});
}
}
/**
* 动态加载目录全部资源
* @param node
* @param url
* @param callBackFun
*/
loadDir(node: cc.Node, url: string, assetType: typeof cc.Asset, callBackFun: Function) {
if (node && url) {
cc.resources.loadDir(url, assetType, (err, assets: cc.Asset[]) => {
if (err) {
cc.error(err.message || err);
return;
}
if (this.pushAsset(node, assets)) {
callBackFun(assets)
}
});
}
}
/**
* 托管资源
* @param node
* @param asset
*/
private pushAsset(node: cc.Node, asset: cc.Asset | cc.Asset[]): boolean {
if (node && node.isValid) {
let nodeId: string = node.uuid;
if (!nodeId || !asset) {
cc.log(`pushAsset参数不正确:nodeId:${nodeId},asset:${asset}`);
return false;
}
if (asset instanceof Array) {
for (let as of asset) {
this.extracted(as, nodeId);
}
} else {
this.extracted(asset, nodeId);
}
return true;
} else {
if (asset) {
cc.log(`老子还没加载完就被干掉了`);
if (asset instanceof Array) {
for (let as of asset) {
as.decRef();
}
} else {
asset.decRef();
}
}
return false;
}
}
private extracted(asset: cc.Asset, nodeId: string) {
let assetArray: cc.Asset[] = this._assetMap.get(nodeId);
if (!assetArray) {
assetArray = [];
}
//同一个节点只增加一次计数
if (assetArray.indexOf(asset) < 0) {
asset.addRef();
assetArray.push(asset);
this._assetMap.set(nodeId, assetArray);
}
}
/**
* 释放资源
* @param node
* @param source
*/
pullAsset(node: cc.Node, source: string) {
if (node && node.isValid) {
let nodeId: string = node.uuid;
if (this._assetMap.has(nodeId)) {
let assetArray: cc.Asset[] = this._assetMap.get(nodeId);
for (let as of assetArray) {
cc.log(`释放资源:${as.name}`);
as.decRef();
}
this._assetMap.delete(nodeId);
}
} else {
cc.error(`老子无法释放资源:传了个null(寂寞),源头:${source}`);
}
}
/**
* 当前资源种类数量
*/
getSize() {
return this._assetMap.size;
}
/**
* 资源keys
*/
getKeys() {
return this._assetMap.keys();
}
}
export default new DynamicAssetManager();
使用前先进行初始化(我这里用一个常驻节点统一进行处理)
import ResourceManager from "./ResourceManager";
import HeartBeatManager from "../network/HeartBeatManager";
import NetworkManager from "../network/NetworkManager";
import DynamicAssetManager from "./DynamicAssetManager";
import SoundManager from "./SoundManager";
const {ccclass} = cc._decorator;
/**
* 游戏全局管理器
*/
@ccclass
export default class GameManager extends cc.Component {
onLoad() {
cc.game.addPersistRootNode(this.node);
//cc.macro.ENABLE_MULTI_TOUCH = false;
GameManager.initMgr(); //注册所有管理器
}
static initMgr() {
DynamicAssetManager.init();//动态资源管理器
SoundManager.init();//音频管理器
ResourceManager.init(); //Protobuf资源管理器
NetworkManager.init();//网路管理器
HeartBeatManager.init(); //心跳管理器
}
}
如何使用呢?
import DynamicAssetManager from "../manager/DynamicAssetManager";
const {ccclass, property} = cc._decorator;
@ccclass
export default class XXXXX extends cc.Component {
onLoad() {
//动态加载你要的资源可以是预制体,图片,音频等等
DynamicAssetManager.load(this.node, "你的资源路径",cc.Prefab,
(prefab: cc.Prefab) => {//根据你要的类型,自行修改,我这里是预制体
//资源拿到了,你要干嘛该干嘛
}
);
}
onDestroy() {
//动态释放(无论上面用到多少次load,这里只要写一次释放就可以了)
DynamicAssetManager.pullAsset(this.node, this.constructor.name);
}
}