天天看點

cocos creator主程入門教程(三)—— 資源管理

五邑隐俠,本名關健昌,10年遊戲生涯,現隐居五邑。本系列文章以TypeScript為介紹語言。

在初識篇,我介紹過怎樣加載prefab。cocos提供了一系列的加載接口,包括cc.loader.load,cc.loader.loadRes,cc.loader.loadResArray,cc.loader.loadResDir。

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,資源可以得到即時釋放。為了避免錯誤釋放資源,在資源管理子產品對加載的資源設定引用計數,引用計數為0時才實際銷毀。

下面說說這幾個接口的使用場景:

1.cc.loader.load用于加載第三方遠端資源,在遊戲中一般用于加載第三方平台的頭像資源,如果該資源的連結沒有檔案字尾名,需要加參數{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外的接口加載資源,簡化使用。

cocos creator主程入門教程(三)—— 資源管理

資源加載管理子產品,可以劃分為ResLoader、ViewLoader。其中ResLoader負責基礎資源加載,另外提供逾時、重試機制。ViewLoader負責對加載的prefab、重用界面的node進行緩存管理。這類工具性的類,我都習慣做成單例,一來遊戲裡隻需要一個對象,另外單例有利于這些對象可以全局通路。

ResLoader,封裝cc.loader上述幾個接口,以及對應的釋放接口。

逾時實作:設定回調控制變量,settimeout回調中設定變量,并調用逾時回調,在成功失敗進行中判斷變量是否觸發成功失敗的回調。由于cc.loader本身有做資源管理,是以下次調用加載時如果已經通過cc.loader正在加載和成功加載的資源不會重複加載。

重試實作:通過變量記錄加載次數,在失敗和逾時進行中判斷是否達到重試次數,未達到則重新加載。

/**
     * 加載 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可以通過兩個數組存放key-value封裝出來),重用界面的node存放在Dictionary<number,cc.Node>類型的nodeDict屬性中。 

  private static tag2prefabPathMap: Dictionary<number, string> = new Dictionary<number, string>();
    private static tag2prefabMap: Dictionary<number, cc.Prefab> = new Dictionary<number, cc.Prefab>();
    private static tag2nodeMap: Dictionary<number, cc.Node> = new Dictionary<number, cc.Node>();
      

通過ResLoader加載cc.Prefab。cc.instantiate執行個體出node節點

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);
        }      

資源加載管理先聊到這裡,下一篇我們将介紹下怎樣做網絡通信。

繼續閱讀