Files
Monopoly/extensions/app/assets/manager/loader/LoaderManager.ts
2026-03-30 09:39:59 +08:00

869 lines
40 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, Asset>();
private loadingMap = new Map<string, Command>();
/**
* 预加载
* @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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType<T> | 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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T> | 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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType<T>[] | 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<T>[];
this.assetMap.forEach((asset, path) => {
if (path.indexOf(key) === 0) {
results.push(asset as InstanceType<T>);
}
});
// 有缓存
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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T>[] | 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<Asset | null> {
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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (item: InstanceType<T> | 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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T> | 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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void, onComplete?: (items: InstanceType<T>[] | 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<T extends typeof Asset>(params: { path: string, bundle?: string, version?: string, type?: T, onProgress?: (finish: number, total: number, item: AssetManager.RequestItem) => void }): Promise<InstanceType<T>[] | 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<AssetManager.Bundle | null> {
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<AssetManager.Bundle | null> {
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<Asset | null> {
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);
}
});
}
}
}