Cocos Creator之資源管理
cocos提供了一系列的加載接口,包括
static load(resources: string|string[]|{uuid?: string, url?: string, type?: string}, completeCallback?: Function): void;
static loadRes(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any) => void): void;
static loadResArray(url: string[], type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[]) => void)|null): void;
static loadResDir(url: string, type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[], urls: string[]) => void)|null): void;
這些接口除了加載資源外,也負責資源管理。所以,在界面被銷毀時,如果在cc.loader里還有該資源,資源是不會釋放的。對于資源的管理,有兩種方式,一種是資源加載后,cc.loader不管理資源,通過界面的引用來確定是否銷毀資源。一種是cc.loader管理資源,界面使用資源,在模塊退出時,通過cc.loader銷毀資源。我偏向于第二種方式,這樣避免依賴內存gc,資源可以得到即時釋放。為了避免錯誤釋放資源,在資源管理模塊對加載的資源設置引用計數(shù),引用計數(shù)為0時才實際銷毀。
下面說說這幾個接口的使用場景:
1.cc.loader.load用于加載第三方遠程資源,在游戲中一般用于加載第三方平臺的頭像資源,如果該資源的鏈接沒有文件后綴名,需要加參數(shù){type:"png"}。
2.cc.loader.loadRes用于加載assets/resources目錄下單個資源
3.cc.loader.loadResArray用于批量加載assets/resources目錄下資源,比較適合于進度條加載界面,通過進度變化更新進度條。
4.cc.loader.loadResDir用于加載assets/resources目錄下單個目錄的資源,一般我會把單個spine骨骼動畫放在一個目錄,把一個界面的資源放在一個目錄。這樣就可以通過這個接口加載單個spine動畫或者一個界面的資源。
在初識篇提到,我們建立assets/resources目錄用于存放資源,目的是可以通過上述除了cc.loader.load外的接口加載資源,簡化使用。

資源加載管理模塊,可以劃分為ResLoader、ViewLoader。其中ResLoader負責基礎資源加載,另外提供超時、重試機制。ViewLoader負責對加載的prefab、重用界面的node進行緩存管理。這類工具性的類,我都習慣做成單例,一來游戲里只需要一個對象,另外單例有利于這些對象可以全局訪問。
ResLoader,封裝cc.loader上述幾個接口,以及對應的釋放接口。
超時實現(xiàn):設置回調控制變量,settimeout回調中設置變量,并調用超時回調,在成功失敗處理中判斷變量是否觸發(fā)成功失敗的回調。由于cc.loader本身有做資源管理,所以下次調用加載時如果已經通過cc.loader正在加載和成功加載的資源不會重復加載。
重試實現(xiàn):通過變量記錄加載次數(shù),在失敗和超時處理中判斷是否達到重試次數(shù),未達到則重新加載。
/**
* 加載 resources 目錄下單個資源
* @param url
* @param type
* @param progressCallback
* @param completeCallback
*/
public static loadRes(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any) => void): void {
let count = ResLoader.retryCount + 1;
let hasCb = false;
// timeout
let timer = setTimeout(() => {
hasCb = true;
completeCallback && completeCallback({
name: "timeout",
message: "timeout",
}, null);
}, ResLoader.timeout);
// real load
let realLoad = function() {
count--;
// load
cc.loader.loadRes(url, type, (err, result) => {
if (!err || count <= 0) {
clearTimeout(timer);
!hasCb && completeCallback && completeCallback(err, result);
return;
}
realLoad();
});
};
realLoad();
}
ViewLoader,負責prefab和重用界面node的緩存,所以每個prefab都設置一個對應的tag,加載的prefab存放在Dictionary<number,cc.Prefab>類型的prefabDict屬性中(Dictionary可以通過兩個數(shù)組存放key-value封裝出來),重用界面的node存放在Dictionary<number,cc.Node>類型的nodeDict屬性中。
private static tag2prefabPathMap: Dictionary<number, string> = newDictionary<number, string>();
private static tag2prefabMap: Dictionary<number, cc.Prefab> = newDictionary<number, cc.Prefab>();
private static tag2nodeMap: Dictionary<number, cc.Node> = new Dictionary<number, cc.Node>();
通過ResLoader加載cc.Prefab。cc.instantiate實例出node節(jié)點
let instantiatePrefab = function(prefab: cc.Prefab) {
let node = cc.instantiate(prefab);
if (reuse && !ViewLoader.tag2nodeMap.hasKey(tag)) {
ViewLoader.tag2nodeMap.set(tag, node);
}
cb && cb(node);
}
資源加載管理就先聊到這里。
更多資源請點擊:https://bycwedu.vipwan.cn/promotion_channels/630597732