CocosCreator之AssetBundle使用方案分享

53次阅读

共计 4286 个字符,预计需要花费 11 分钟才能阅读完成。

前言

Creator2.4 推出了 AssetBundle,使得所有平台都有了分包的能力。那么该如何应用这个弱小的性能呢?上面介绍一下我集体的用法,仅供参考,程度无限,非喜勿喷。
依据官网文档 指出,之前的 cc.loader 须要用 cc.resource 替换

而 cc.resource 自身就是一个 Bundle

也就是说,2.4 之前,整个引擎只有一个 cc.loader 治理资源,2.4 之后采纳了多个 Bundle 来治理资源,只是 cc.resource 这个 Bundle 负责管理 resource 目录下的动静资源,曾经是引擎规定好的了。而自定义的 Bundle 其实是与 cc.resource 平级的。那么只有咱们给资源管理器传入不同的 Bunlde,天然就是加载不同 Bundle 中的内容了。

概念

在介绍我的用法之前,先阐明以下几个概念

  1. 分包 AssetBundle

一个 Bundle 就是一个分包,cc.resource 就是引擎默认的 Bundle。

  1. 模块 Module

将游戏依照性能划分为不同的模块。能够多个模块共用一个分包。也能够一个模块应用一个分包。不应用分包的模块默认应用 cc.resource。这个模块类跟游戏中你定义的零碎,例如,设置,大厅,战斗,背包等等并不是非要一一对应的,并不是有多少个零碎就须要创立多少个 Module. 这个依据你的需要而定。然而我的资源清理都是依据不同的 Module 来做的,划分的越多,清理就越粗疏。

import ResLoader from "../../cfw/res/ResLoader";
import AudioManager from "../../cfw/audio/AudioManager";
import ResHelper from "../../engine/ResHelper";
import {isNull} from "../../cfw/tools/Define";
export default class Module {
    private loader: ResLoader;
    protected audio: AudioManager;
    protected moduleName: string = ''protected projectName: string =''
    constructor(projectName: string, moduleName: string) {
        this.projectName = projectName;
        this.moduleName = moduleName;
        // 有名称的模块,须要在适当的时候加载 bundle 并设置。if (!this.moduleName) {//name != '' 的 阐明是自定义的 bundle。this.setLoader(new ResLoader(new ResHelper(cc.resources)))
        }

    }
    setAudio() {if (this.loader) {this.audio = new AudioManager(this.projectName, this.loader)
        }
    }
    hasLoader() {return !isNull(this.loader)
    }
    setLoader(loader: ResLoader) {this.loader = loader}
    setLoaderByBundle(bundle: cc.AssetManager.Bundle) {this.setLoader(new ResLoader(new ResHelper(bundle, this.moduleName)))
    }
    getName() {return this.moduleName;}
    getLoader() {return this.loader;}
    getAudio() {return this.audio;}
}
  1. 模块管理器 ModuleManager

负责初始化模块,取得和设置模块。设置模块 ID 对应的 Bundle 名称,不应用分包的模块名称设置为空字符串。

import Module from "./Module";
import XlsxDataManager from "../../cfw/xlsx/XlsxDataManager";
import {ResType} from "../../cfw/res/ResInterface";
import ResItem from "../../cfw/res/ResItem";


// 模块 id
export enum ModuleID {
    PUBLIC,
    LOGIN,
    BATTLE,
    LOBBY,
    MAX
}

// 有分包的模块就有 bundle 的名字,没有就是应用 cc.resource 
let moduleName: string[] = ['','', 'battle', '']

export default class ModuleManager {private static mgrMap: Module[] = []

    private static moduleID: ModuleID = ModuleID.PUBLIC;


    static dataManager: XlsxDataManager;

    static init(projectName: string) {for (let index = 0; index < moduleName.length; index++) {this.mgrMap[index] = new Module(projectName, moduleName[index]);
        }
        this.mgrMap[ModuleID.PUBLIC].setAudio()
        this.dataManager = new XlsxDataManager()}

    static getAudio(id: ModuleID = this.moduleID) {return this.mgrMap[id].getAudio()}

    static publicAudio() {return this.mgrMap[ModuleID.PUBLIC].getAudio()}

    static publicLoader() {return this.mgrMap[ModuleID.PUBLIC].getLoader()}


    static loadRes(url: string, type: ResType, callback: (err: string, res: ResItem) => void) {this.getLoader().loadRes(url, type, callback)
    }

    static getName(id: ModuleID = this.moduleID) {return this.mgrMap[id].getName()}

    static setLoaderByBundle(id: ModuleID = this.moduleID, bundle: cc.AssetManager.Bundle) {this.mgrMap[id].setLoaderByBundle(bundle)
    }

    static setModuleID(id: ModuleID) {this.moduleID = id;}

    static getLoader(id: ModuleID = this.moduleID) {return this.mgrMap[id].getLoader()}

    static hasLoader(id: ModuleID = this.moduleID) {return this.mgrMap[id].hasLoader()}


    /**
     * 为某个模块设置 bundle
     * @param moduleID 
     * @param callback 
     */
    static loadBundle(moduleID: ModuleID, callback: Function) {this.publicLoader().loadRes(ModuleManager.getName(moduleID),
         ResType.AssetBundle, (err, item: ResItem) => {if (err) {return;}
            ModuleManager.setLoaderByBundle(moduleID, item.getRes())
            callback();})
    }

}
  1. 资源管理器 ResLoader

负责加载资源

  1. 资源项:ResItem 负责管理资源的援用计数

  1. ResHelper

为了跨引擎应用 ResLoader, 我自定义了 ResType 枚举、ResInterface 接口和这个 Creator 加载辅助类 ResHelper,如果不波及到跨引擎的话,能够去掉这几个货色,而后间接把 AssetBundle 传到 ResLoader 中应用。

应用

  1. 将一个模块的资源放入一个目录中,将这个目录设置为 Bundle

  1. 在 ModuleManger 中设置 bundle 的名字

  1. 点击进入时或者在 loading 的时候设置对应模块的 Bundle

  1. 进入模块之前设置为此模块对应的模块 ID, 那么此时取得的 ResLoader 天然就是 battle 模块的 ResLoader 了, 并且 ResLoader 应用的 Bundle 是名称为 battle 的 Bunlde。

  1. 清理资源,这里须要指定模块 ID。因为引擎做了资源援用计数的事件,所以咱们不必再去治理依赖关系,省了很多事件。

注意事项

  1. 如果是 resource 中的资源作为 bundle 之后,须要从 resource 中拿到外边。

  1. 公布小游戏时压缩类型设置为小游戏子包,官网文档已阐明

资源管理的一些问题

看到论坛对于 2.4 的援用计数问题探讨的十分强烈。
https://forum.cocos.org/t/2-4-0/95076/32
我做的资源管理器有如下规定

  1. 所有资源必须通过资源项 ResItem 的 getRes 接口取得,保障应用一次计数减少一次,不参加引擎提供的计数治理。因为引擎本人治理的依赖关系,所以 2.4 之后不再须要保护资源的依赖关系。只保护好本人的应用次数就好。


  1. ResLoader 提供删除某个资源的性能,应答 ui 清理需要。如果删除整个模块,调用 release 接口即可。

  1. ResLoader 反对间接缓存一个挂载到场景中的资源,比方初始场景会间接挂载一个预制体,防止黑屏。如果不想清理场景只想清理这个预制体就能够这样应用。



  1. 理论利用可能简单的多,每个人的用法不同,不保障没有问题。

结语

  1. 以上各种计划仅是我集体的应用形式,并非最佳计划,可能会有 bug,仅供大家参考,谁有更好的形式,也分享进去,互相学习。
  2. demo 在我的框架我的项目中,曾经更新到 2.4。因为之前疏忽文件的起因,请应用空文件夹从新 clone。

欢送扫码关注公众号《微笑游戏》,浏览更多内容。

欢送扫码关注公众号《微笑游戏》,浏览更多内容。

正文完
 0