import { Asset, AssetManager, Font, ImageAsset, JsonAsset, Label, SceneAsset, Sprite, SpriteFrame, Texture2D, TextureCube, _decorator, assetManager, isValid, path, sp } from 'cc'; import { MINIGAME } from 'cc/env'; import BaseManager from '../../base/BaseManager'; import Core from '../../Core'; const { ccclass } = _decorator; const REGEX = /^https?:\/\/.*/; class Command { private static cache: Command[] = []; static create(onComplete: (items: unknown) => void, onProgress: (finish: number, total: number, item: AssetManager.RequestItem) => void = null) { const command = Command.cache.pop() || new Command(); onProgress && command.onProgress.push(onProgress); onComplete && command.onComplete.push(onComplete); return command; } static put(command: Command) { command.onProgress.length = 0; command.onComplete.length = 0; Command.cache.push(command); } onProgress: Array<(finish: number, total: number, item: AssetManager.RequestItem) => void> = []; onComplete: Array<(items: unknown) => void> = []; private constructor() { } } class Loader { private assetMap = new Map(); private loadingMap = new Map(); /** * 预加载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public preload(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: AssetManager.RequestItem[] | null) => void }) { return Core.inst.manager.loader.preload(params); } /** * 预加载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public preloadDir(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: AssetManager.RequestItem[] | null) => void }) { return Core.inst.manager.loader.preloadDir(params); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundel名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public load(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType | null) => void }) { const key = `${params.bundle || 'resources'}-${params.type.name}-${params.path}-${params.version || ''}`; if (this.loadingMap.has(key)) { const command = this.loadingMap.get(key); params.onProgress && command.onProgress.push(params.onProgress); params.onComplete && command.onComplete.push(params.onComplete); return; } // 加载中 const command = Command.create(params.onComplete, params.onProgress); this.loadingMap.set(key, command); // 有缓存 if (this.assetMap.has(key)) { const asset = this.assetMap.get(key); // 有缓存的情况下不触发onProgress回调 setTimeout(() => { // 加载无效 if (!this.loadingMap.has(key)) return; this.loadingMap.delete(key); command.onComplete.forEach(cb => cb(asset)); Command.put(command); }, 0); return; } Core.inst.manager.loader.load({ ...params, onProgress: (finish, total, item) => { if (!this.loadingMap.has(key)) return; command.onProgress.forEach(cb => cb(finish, total, item)); }, onComplete: (asset) => { // 加载无效 if (!this.loadingMap.has(key)) { asset.addRef(); asset.decRef(); return; } this.loadingMap.delete(key); if (asset) { asset.addRef(); this.assetMap.set(key, asset); } command.onComplete.forEach(cb => cb(asset)); Command.put(command); } }); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadAsync(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise | null> { return new Promise((resolve) => { this.load({ ...params, onComplete: resolve }); }); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundel名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadDir(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType[] | null) => void }) { const key = `${params.bundle || 'resources'}-${params.type.name}-${params.path}-${params.version || ''}:`; if (this.loadingMap.has(key)) { const command = this.loadingMap.get(key); params.onProgress && command.onProgress.push(params.onProgress); params.onComplete && command.onComplete.push(params.onComplete); return; } // 加载中 const command = Command.create(params.onComplete, params.onProgress); this.loadingMap.set(key, command); const results = [] as InstanceType[]; this.assetMap.forEach((asset, path) => { if (path.indexOf(key) === 0) { results.push(asset as InstanceType); } }); // 有缓存 if (results.length) { // 有缓存的情况下不触发onProgress回调 setTimeout(() => { // 加载无效 if (!this.loadingMap.has(key)) return; this.loadingMap.delete(key); command.onComplete.forEach(cb => cb(results)); Command.put(command); }, 0); return; } Core.inst.manager.loader.loadDir({ ...params, onProgress: (finish, total, item) => { if (!this.loadingMap.has(key)) return; command.onProgress.forEach(cb => cb(finish, total, item)); }, onComplete: (assets) => { // 加载无效 if (!this.loadingMap.has(key)) { assets?.forEach((asset) => { asset.addRef(); asset.decRef(); }); return; } this.loadingMap.delete(key); assets?.forEach((asset) => { asset.addRef(); this.assetMap.set(key + asset.uuid, asset); }); command.onComplete.forEach(cb => cb(assets)); Command.put(command); } }); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadDirAsync(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise[] | null> { return new Promise((resolve) => { this.loadDir({ ...params, onComplete: resolve }); }); } /** * 加载远程资源 * @example * loadRemote({url:'', ext:'.png', onComplete:(result){ }}) */ public loadRemote({ url, ext, onComplete }: { url: string, ext?: string, onComplete?: (result: Asset | null) => void }) { if (this.loadingMap.has(url)) { const command = this.loadingMap.get(url); onComplete && command.onComplete.push(onComplete); return; } // 加载中 const command = Command.create(onComplete); this.loadingMap.set(url, command); // 有缓存 if (this.assetMap.has(url)) { const asset = this.assetMap.get(url); // 有缓存的情况下不触发onProgress回调 setTimeout(() => { // 加载无效 if (!this.loadingMap.has(url)) return; this.loadingMap.delete(url); command.onComplete.forEach(cb => cb(asset)); Command.put(command); }, 0); return; } Core.inst.manager.loader.loadRemote({ url, ext, onComplete: (asset) => { // 加载无效 if (!this.loadingMap.has(url)) { asset.addRef(); asset.decRef(); return; } this.loadingMap.delete(url); if (asset) { asset.addRef(); this.assetMap.set(url, asset); } command.onComplete.forEach(cb => cb(asset)); Command.put(command); } }); } /** * 加载远程资源 * @example * await loadRemoteAsync({url:'', ext:'.png'}) */ public loadRemoteAsync(params: { url: string, ext?: string }): Promise { return new Promise((resolve) => { this.loadRemote({ ...params, onComplete: resolve }); }); } /** * 设置字体资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setFont({target:label, path:'font/num', bundle:'resources', onComplete:(succ)=>{}}) * setFont({target:label, url:'http://img/a/font',ext:'.ttf', onComplete:(succ)=>{}}) */ public setFont(params: { target: Label, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setFont(params: { target: Label, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setFont(params: { target: Label, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { if (params.url) { this.loadRemote({ url: params.url, ext: params.ext, onComplete: (font: Font) => { if (!font || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.font = font; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } else { this.load({ path: params.path, bundle: params.bundle, type: Font, onComplete: (font) => { if (!font || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.font = font; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } } /** * 设置Spine资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setSpine({target:spine, path:'spine/role', bundle:'resources', onComplete:(succ)=>{}}) */ public setSpine(params: { target: sp.Skeleton, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { this.load({ path: params.path, bundle: params.bundle, type: sp.SkeletonData, onComplete: (skeletonData) => { if (!skeletonData || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.skeletonData = skeletonData; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } /** * 设置图片资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setSprite({target:sprite, path:'img/a/spriteFrame', bundle:'resources', onComplete:(succ)=>{}}) * setSprite({target:sprite, url:'http://img/a/avatar',ext:'.png', onComplete:(succ)=>{}}) */ public setSprite(params: { target: Sprite, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setSprite(params: { target: Sprite, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setSprite(params: { target: Sprite, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { if (params.url) { this.loadRemote({ url: params.url, ext: params.ext, onComplete: (imageAsset: ImageAsset) => { if (!imageAsset || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } const spriteFrame = SpriteFrame.createWithImage(imageAsset); params.target.spriteFrame = spriteFrame; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } else { this.load({ path: params.path, bundle: params.bundle, type: SpriteFrame, onComplete: (spriteFrame) => { if (!spriteFrame || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.spriteFrame = spriteFrame; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } } /** * 释放所有资源 */ public releaseAll() { const assetList: Asset[] = []; this.assetMap.forEach(asset => assetList.push(asset)); this.assetMap.clear(); this.loadingMap.clear(); // 延迟一秒释放资源 setTimeout(() => { assetList.forEach(asset => asset.decRef()); }, 1000); } } @ccclass('LoaderManager') export default class LoaderManager extends BaseManager { /** * `Loader`的目的是对资源加载进行分组引用计数管理。比如两个`Loader`实例都加载了同一个资源,当某个实例执行releaseAll后,并不会让引擎资源释放资源,只有两个实例都执行了释放资源后,才会让引擎资源释放资源。 * @example * // 创建Loader实例 * const loader = new LoaderManager.Loader(); * // 加载资源 * loader.load({path:'img/a/spriteFrame', bundle:'resources', type:SpriteFrame, onComplete:(spriteFrame)=>{}}) * // 加载远程图片资源 * loader.loadRemote({url:'http://img/a/avatar',ext:'.png', onComplete:(imageAsset)=>{}}) * // 释放所有资源 * loader.releaseAll(); */ static Loader = Loader; private handle(handle: string, { bundle, version, path, type, onProgress, onComplete }: { bundle?: string, version?: string, path: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (result: unknown | null) => void }) { if (!handle) { this.error('handle is empty'); return onComplete && onComplete(null); } if (!path) { this.error(`${handle} fail. path is empty`); return onComplete && onComplete(null); } if (!bundle) bundle = 'resources'; const args: any[] = [path]; if (type) args.push(type); if (onProgress) args.push(onProgress); args.push((err: string, res: any) => { if (err) { this.error(`${handle} "${path}" fail`, err); if (type === SpriteFrame && path.slice(-12) !== '/spriteFrame') { this.warn(`加载SpriteFrame类型的资源, 路径可能需要以/spriteFrame结尾, 如: 「${path}」 -> 「${path}/spriteFrame」`); } else if (type === Texture2D && path.slice(-8) !== '/texture') { this.warn(`加载Texture2D类型的资源, 路径可能需要以/texture结尾, 如: 「${path}」 -> 「${path}/texture」`); } else if (type === TextureCube && path.slice(-12) !== '/textureCube') { this.warn(`加载TextureCube类型的资源, 路径可能需要以/textureCube结尾, 如: 「${path}」 -> 「${path}/textureCube」`); } onComplete && onComplete(null); } else { onComplete && onComplete(res); } }); this.loadBundle({ bundle, version, onComplete(bundle) { if (!bundle) return onComplete && onComplete(null); bundle[handle](args[0], args[1], args[2], args[3]); }, }); } /** * 预加载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public preload(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: AssetManager.RequestItem[] | null) => void }) { if (SceneAsset === params.type as typeof Asset) { this.handle('preloadScene', { path: params.path, bundle: params.bundle, version: params.version, onProgress: params.onProgress, onComplete: params.onComplete }); } else { this.handle('preload', params); } } /** * 预加载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public preloadDir(params: { path: string, bundle?: string, version?: string, type?: typeof Asset, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: AssetManager.RequestItem[] | null) => void }) { this.handle('preloadDir', params); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public load(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType | null) => void }) { if (SceneAsset === params.type as typeof Asset) { this.handle('loadScene', { path: params.path, bundle: params.bundle, version: params.version, onProgress: params.onProgress, onComplete: params.onComplete }); } else { this.handle('load', params); } } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadAsync(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise | null> { return new Promise((resolve) => { this.load({ ...params, onComplete: resolve }); }); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadDir(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType[] | null) => void }) { this.handle('loadDir', params); } /** * 加载bundle下的资源 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public loadDirAsync(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise[] | null> { return new Promise((resolve) => { this.loadDir({ ...params, onComplete: resolve }); }); } /** * 销毁一个bundle中对应path和type的资源 * @param params.bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 * @param params.path bundle下的相对路径 * @param params.type 资源类型 */ public release({ path, bundle, type }: { path: string, bundle?: string, type?: typeof Asset }) { if (!bundle) bundle = 'resources'; assetManager.getBundle(bundle)?.release(path, type); } /** * 销毁一个bundle中所有的资源 * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 */ public releaseAll(bundle?: string) { if (!bundle) bundle = 'resources'; const _bundle = assetManager.getBundle(bundle); if (!_bundle) return; // 只释放自己内部的资源,依赖的资源只减少引用计数 _bundle.getDirWithPath('/', Asset).forEach((asset) => { _bundle.release(asset.path, asset.ctor); }); // cocos提供的方法会将依赖的资源也卸载(这个设计很奇怪) // _bundle?.releaseAll(); } /** * 销毁一个bundle中未使用的资源 * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 */ public releaseUnused(bundle?: string) { if (!bundle) bundle = 'resources'; //@ts-ignore assetManager.getBundle(bundle)?.releaseUnusedAssets(); } /** * 加载一个bundle * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public loadBundle({ bundle, version, onComplete }: { bundle?: string, version?: string, onComplete?: (bundle: AssetManager.Bundle | null) => any }) { if (!bundle) bundle = 'resources'; if (MINIGAME) { if (REGEX.test(bundle)) { this.warn('小游戏环境下只支持加载远程Bundle的资源数据, 不会加载脚本'); this.reloadBundle({ bundle, version, onComplete }); return; } if (version && assetManager.downloader.bundleVers[bundle] !== version) { this.warn('小游戏环境下只支持更新Bundle的远程资源数据, 不会更新脚本'); // 先加载本地bundle运行脚本 assetManager.loadBundle(bundle, (err: Error, b: AssetManager.Bundle) => { if (err || !b) return onComplete?.(null); // 然后再走重载逻辑更新资源 this.reloadBundle({ bundle, version, onComplete }); }); } else { assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => { onComplete && onComplete(err ? null : bundle); }); } return; } if (version) { assetManager.loadBundle(bundle, { version }, (err: Error, bundle: AssetManager.Bundle) => { onComplete && onComplete(err ? null : bundle); }); } else { assetManager.loadBundle(bundle, (err: Error, bundle: AssetManager.Bundle) => { onComplete && onComplete(err ? null : bundle); }); } } /** * 加载一个bundle * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public loadBundleAsync(params: { bundle?: string, version?: string }): Promise { return new Promise((resolve) => { this.loadBundle({ ...params, onComplete: resolve }); }); } /** * 获取一个已经加载的bundle * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 */ public getBundle(bundle?: string) { if (!bundle) bundle = 'resources'; return assetManager.getBundle(bundle); } /** * 移除一个已经加载的bundle * @param bundle 默认为resources,如果是远程bundle,则使用url末位作为bundle名 */ public removeBundle(bundle?: string) { if (!bundle) bundle = 'resources'; const b = assetManager.getBundle(bundle); if (b) assetManager.removeBundle(b); } /** * 重载一个bundle(只重载资源列表) * - 只有远程bundle支持重载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public reloadBundle({ bundle, version, onComplete }: { bundle?: string, version?: string, onComplete?: (bundle: AssetManager.Bundle | null) => any }) { if (!bundle) bundle = 'resources'; let baseUrl = ''; let configUrl = ''; if (REGEX.test(bundle)) { baseUrl = bundle; const suffix = version ? `${version}.` : ''; configUrl = `${baseUrl}config.${suffix}json`; } else { baseUrl = `${assetManager.downloader.remoteServerAddress}remote/${bundle}/`; const suffix = version ? `${version}.` : ''; configUrl = `${baseUrl}config.${suffix}json`; } // 清除可能存在的config缓存 assetManager.cacheManager?.removeCache(configUrl); assetManager.loadRemote(configUrl, (err: Error, data: JsonAsset) => { if (err) { this.error(`下载Bundle配置失败: ${configUrl}`); onComplete?.(null); return; } this.releaseAll(path.basename(bundle)); this.removeBundle(path.basename(bundle)); const ab = new AssetManager.Bundle(); const config = data.json as any; config.base = baseUrl; ab.init(config); onComplete?.(ab); }); } /** * 重载一个bundle(只重载资源列表) * - 只有远程bundle支持重载 * @param params.bundle 默认为resources, 可以是项目中的bundle名,也可以是远程bundle的url(url末位作为bundle名),参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#%E5%8A%A0%E8%BD%BD-asset-bundle * @param params.version 远程bundle的版本,参考https://docs.cocos.com/creator/manual/zh/asset/bundle.html#asset-bundle-%E7%9A%84%E7%89%88%E6%9C%AC */ public reloadBundleAsync(params: { bundle?: string, version?: string }): Promise { return new Promise((resolve) => { this.reloadBundle({ ...params, onComplete: resolve }); }); } /** * 加载远程资源 * @example * loadRemote({url:'', ext:'.png', onComplete:(result){ }}) */ public loadRemote({ url, ext, onComplete }: { url: string, ext?: string, onComplete?: (result: Asset | null) => void }) { if (ext) { assetManager.loadRemote(url, { ext }, (error, res) => { if (error) { this.error(`loadRemote ${url} fail`); return onComplete && onComplete(null); } onComplete && onComplete(res); }); } else { assetManager.loadRemote(url, (error, res) => { if (error) { this.error(`loadRemote ${url} fail`); return onComplete && onComplete(null); } onComplete && onComplete(res); }); } } /** * 加载远程资源 * @example * await loadRemoteAsync({url:'', ext:'.png'}) */ public loadRemoteAsync(params: { url: string, ext?: string }): Promise { return new Promise((resolve) => { this.loadRemote({ ...params, onComplete: resolve }); }); } /** * 设置字体资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setFont({target:label, path:'font/num', bundle:'resources', onComplete:(succ)=>{}}) * setFont({target:label, url:'http://img/a/font',ext:'.ttf', onComplete:(succ)=>{}}) */ public setFont(params: { target: Label, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setFont(params: { target: Label, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setFont(params: { target: Label, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { if (params.url) { this.loadRemote({ url: params.url, ext: params.ext, onComplete: (font: Font) => { if (!font || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.font = font; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } else { this.load({ path: params.path, bundle: params.bundle, type: Font, onComplete: (font) => { if (!font || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.font = font; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } } /** * 设置Spine资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setSpine({target:spine, path:'spine/role', bundle:'resources', onComplete:(succ)=>{}}) */ public setSpine(params: { target: sp.Skeleton, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { this.load({ path: params.path, bundle: params.bundle, type: sp.SkeletonData, onComplete: (skeletonData) => { if (!skeletonData || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.skeletonData = skeletonData; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } /** * 设置图片资源 * @param params.bundle 默认为resources * @param params.path bundle下的相对路径 * * @example * setSprite({target:sprite, path:'img/a/spriteFrame', bundle:'resources', onComplete:(succ)=>{}}) * setSprite({target:sprite, url:'http://img/a/avatar',ext:'.png', onComplete:(succ)=>{}}) */ public setSprite(params: { target: Sprite, url: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setSprite(params: { target: Sprite, path: string, bundle?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }): void; public setSprite(params: { target: Sprite, path?: string, bundle?: string, url?: string, ext?: string, onComplete?: (success: boolean) => any, onSuccess?: () => void, onFail?: () => void }) { if (params.url) { this.loadRemote({ url: params.url, ext: params.ext, onComplete: (imageAsset: ImageAsset) => { if (!imageAsset || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } const spriteFrame = SpriteFrame.createWithImage(imageAsset); params.target.spriteFrame = spriteFrame; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } else { this.load({ path: params.path, bundle: params.bundle, type: SpriteFrame, onComplete: (spriteFrame) => { if (!spriteFrame || !isValid(params.target)) { params.onFail && params.onFail(); params.onComplete && params.onComplete(false); return; } params.target.spriteFrame = spriteFrame; params.onSuccess && params.onSuccess(); params.onComplete && params.onComplete(true); } }); } } }