AssetBundle技术的概念
Unity的AssetBundle是一个资源压缩包,包含模型、贴图、预制体、声音甚至整个场景,可以在游戏运行时被加载。
AssetBundle自身保存着相互的依赖关系,压缩包可以使用LZMA和LZ4压缩算法,减少包大小,更快的进行网络传输,把一些可以下载的内容放在AssetBundle里面,可以减少安装包的大小。
AssetBundle的本质
AssetBundle是一个存于硬盘上的文件,压缩包中包含了多个文件,如各种图片、声音等等,方便快速加载,可以在Editor上读取,方便查看,可以通过代码从一个特定的压缩包中加载出来对象,这个对象包含了所有添加到这个压缩包里面的内容,可以通过这个对象加载出来
AssetBundle的作用
AssetBundle的主要作用为优化安装包的大小,如比较耗费资源的模型、场景、预制体等资源都可以放到AssetBundle包中,在运行时动态下载后加载,安装包就不会那么大。
打包(资源分包)的策略
生成AssetBundle包是不能将所有的资源都生成到一个包中,因为这样会导致AssetBundle包的体积过大,造成下载 加载过慢 会影响用户体验 ,也不能为每个资源生成一个包,这样会耗费大量的加载时间,也会影响用户体验。
常用的打包分组策略
- 安装逻辑实体分组
一个UI界面或者所有UI界面为一个包
一个角色或者所有角色为一个包
所有场景共享的部分为一个包 - 按照类型分组
所有声音资源为一个包
所有Shader为一个包
所有模型为一个包
所有材质为一个包 - 按照使用分组
把在某个时间内使用的所有资源打包成一个包
按照关卡分 一个关卡需要的所有资源 包含角色 贴图 声音等为一个包
按照场景分 一个场景需要的资源为一个包
打包注意事项
把经常更新的资源放在一个单独包内 与不经常更新的包分离
把需要同时加载的资源放在一个包内
可以把其他包共享的资源放在一个单独的包中
把一些需要同时加载的小资源打包成一个包
Unity中AssetBundle的打包和分包策略主要针对资源的整理和分组,以方便管理,又能有效优化资源的加载和内存使用。
资源管理的途径:
- 打包资源的预处理:为了减少AssetBundle的大小和数量,可以先对资源进行预处理,如压缩纹理、优化模型、剪辑音频等
- 资源的分组打包
- 公共资源的处理
- 加载依赖的管理
- 版本管理
热更新的步骤
- 总体步骤:
资源打包(开发者)
上传服务器(开发者)
从服务器下载(客户端)
资源包解包并加载数据到程序中(客户端)
资源打包成AssetBundle:
对场景的资源做一个标记,该标记用于确定哪些资源需要打包,已经打包时的分类
[MenuItem("AssetBundle/Build")]
public void Build()
{BuildPipeline.BuildAssetBundles(Application.dataPath+"/AssetBundles");
}
注意点:
要打包的资源的Asset Label必须全部小写(大写会被自动转换为小写),可以带扩展名、版本名
形如“xxx/yyy”的Asset Label 会转化为xxx文件夹下的yyy资源包
面试场景:如何按照路径去打包资源
答:给每一个资源添加一个路径(Asset Label) + / 资源包的名称
Unity会自动处理依赖关系:
比如一个模型依赖于一个材质,如果模型和材质分属不同AssetBundle,则材质中的贴图文件变化时,只需要更新材质的AssetBundle即可,从而减少资源打包的时间和数量。
Unity能够支持任意一种该引擎能够识别的资源类型的打包:
除了脚本资源,移动平台不支持网络下载脚本并执行,因为可能会存在安全隐患,从而导致游戏上架审核失败。
脚本资源的热更新需要额外处理:方法就是利用C#反射机制绑定UnityAPI,在上层进行调用(此外,你无法调用你通过审核以外的API功能)
Unity支持txt、xml、json格式的文件打包,预制体、模型、音频、贴图等都可以打包,但无法打包脚本。
关于二进制资源的打包:
- Unity支持二进制资源的打包
- 扩展名以.bytes结尾。
- Unity会将其识别为TextAsset类型。
- 在将二进制文件下载到本地后,程序员按TextAsset类型对其进行解包。
- 也可以将其他Unity不能识别的格式的资源改名为.bytes进行打包。
手机上需要发布到Application.streamingAssetsPath下,因为只有该目录下的内容会被发布到真机上(首先需要新建一个Assets/Streaming Assets文件夹)
打包场景需要调用BuildPipeline.BuildPlayer();方法
热更新时序图
动态加载的好处:
如果游戏场景资源比较多,按需加载可以降低消耗
比如大地图的MMORPG这样的类型的游戏,更是要降低消耗,按需加载
3、资源包上传到服务器
4、从服务器下载资源到本地
非缓存方式:
string url="file:///"+Application.dataPath+"/AssetBundles/XXX";
WWW www=new WWW(url);
yield return www;file///:这是表示紧跟其后的路径指向本地文件系统的协议前缀。在file:后面的三斜杠是标准用法,用于表示一个不含主机消息(因为我们处理的事本地文件)的绝对文件路径。
缓存方式:
WWW www=WWW.LoadFromCacheOrDownload(url,version);//这里的版本可以从服务器的version.cfg文件下载。
yield return wwww;
WWW www;public Text text;void Start(){//完善的热更新解决方案需要以下步骤//启动程序时,将StreamingAsset中的资源包移动到PersistantDataPath//检查服务器上资源有没有发生更新,有的话下载更新//简单学习,只要从服务器下载资源并载入数据即可StartCoroutine(DownLoadAB());//启动携程}public IEnumerator DownLoadAB(){//print("DownLoadAB");//yield return null;//yield break;//中断协程www = new WWW("http://localhost:6688/AssetBundles/my");yield return www;//等待下载结束print("Download complete");GameObject cubePrefab= www.assetBundle.LoadAsset<GameObject>("MyCube.prefab");Instantiate(cubePrefab);}private void Update(){text.text = string.Format("{0}%", www.progress * 100);}
总资源加载
一般不能直接在Update中下载资源,而是由主线程显示下载的进度,开启携程下载数据
当不清楚资源的依赖关系时,可以先将其总资源包加载,取得相关的依赖项进行加载
//下载总资源包www = new WWW("http://localhost:6688/AssetBundles/AssetBundles");yield return www;AssetBundleManifest mainfest = www.assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//取得相关的依赖项string[] dep = mainfest.GetAllDependencies("我的预制体");//将相关的依赖项全部加载foreach (string s in dep){//print(s);www=new WWW("http://localhost:6688/AssetBundles/"+s);yield return www;www.assetBundle.LoadAllAssets();}
注意
1、www的资源是直接加载到内存中的,所以用完就要即时释放
//未用资源的加载
www.assetBundle.Unload(false);
2、www的资源不能缓存,需要我们手动实现数据缓存已经和服务器版本比较功能。
卸载AssetBundle:
Asset加载进来后就可以释放AssetBundle了,调用AssetBundle.Unload(unloadAllLoadedObjects)即可。
- 传入true:卸载该bundle中所有的Asset
- 传入false:卸载当前未被使用的所有的Asset
5、解包AssetBundle
AssetBundle的使用
AssetBundle工作流程
主要作用:在游戏运行过程中对资源进行动态的下载和加载。
通过AssetBundle将游戏中所需的各种资源打包压缩并上传到网络服务器上
在运行时游戏可以从服务器上下载该资源,从而实现资源的动态加载。
AssetBundle的工作流程
1、开发过程中,开发者创建AssetBundle文件并上传到服务器上。
创建AssetBundle:开发者可以在Unity编辑器中通过脚本来创建满足要求的AssetBundle文件夹。
上传至服务器:开发者创建好AssetBundle文件后,即可通过上传工具(比如FTP等)将其上传至服务器中,从而使游戏通过访问服务器来获取所需的资源。
游戏运行时,客户端会根据实际需求从服务器上将适当的AssetBundle下载到本地机器,再通过加载模块将其资源加载到游戏当中。
下载AssetBundle:Unity提供了一套完整的API 供开发者使用,从而完成从服务端下载AssetBundle文件。
www = new WWW("http://localhost:6688/AssetBundles/my");yield return www;//等待下载结束GameObject cubePrefab= www.assetBundle.LoadAsset<GameObject>("MyCube.prefab");
加载AssetBundle中的Asset:AssetBundle文件一旦下载完成,开发者即可通过特定的API来加载其所包含的Assets,包括模型、纹理、动画片段等。
Unity5对Unity4的AssetBundle系统进一步简化,提供了更简单的UI和更简单的脚本,Unity在Build时会自动处理依赖关系,并生成一个.manifest文件,以及AssetBundle的增量式发布。
AssetBundle在Unity中的使用
AssetBundle是Unity引擎提供的一种用于存储资源的文件格式,它可以存储任意一种Unity引擎能够识别的资源,例如模型、纹理、音频、动画片段甚至整个场景。
同时,AssetBundle也可以包含开发者自定义的二进制文件,只需将二进制文件的后缀名改为".bytes",Unity引擎即可将其识别为TextAsset,进而可以被打包到AssetBundle文件中
用户可以通过AssetBundle的UI和简单的脚本在Unity中创建AssetBundle文件,Unity引擎提供了二中创建AssetBundle的API
BuildPipeline.BuildAssetBundles()
通过该接口,开发者可以将编辑器中指定的Asset打包
BuildPipeline.BuildPlayer()
通过该接口,开发者可以讲场景编译成播放器
AssetBundle的UI
Unity5提供了一个简单AssetBundle的UI,可以让用户简单快速的将Asset标记到AssetBundle
在资源的Inspector视图中的下方会出现一个AssetBundle的UI,其中第一个选项表示该资源所标记的AssetBundle名称(即该资源会打包到这个名称的AssetBundle中),第二个参数用于设置AssetBundle Variant,主要应用在不同分辨率资源的使用和动态替换AssetBundle。
当AssetBundle标记的对象很多时,想要看到某个AssetBundle中的资源时,可以单击AssetBundle的名称选项,在弹出的菜单中选择”Filter Selected Name”项,或者在Project视图中搜索”b:AssetBundle名称”,即可查找该AssetBundle里包含的所有资源
AssetBundle的标记名称要小写,可以有后缀(如:a.assetbundle/a.unity3d),Script(脚本)不能标记AssetBundle。
BuildPineline.BuildAssetBundles
public static AssetBundleMainfest BuildAssetBundles(string outputPath ,BuildAssetBundleOPtions assetBundleOptions=BuildAssetBundleOptions.None ,BuildTarget targetPlatform=BuildTarget.WebPlayer)
如果要将多个资源一起打包,只要将它们的AssetBundle名称设置成一样就可以了
AssetBundle的增量式打包仅重新打包发生变化的AssetBundle,当Asset文件发生变化或者TypeTree发生变化时才会重新打包。
BuildPileline.BuildPlayer()
BuildPlaye()
public static string BuildPlayer(string[] levels,string locationPathName , BuildTarget target ,BuildOptions options)levels :包括在build里的场景,如果空,当前打开的场景将被编译。路径是相对项目文件夹(Assets/MyLevels/MyScene.unity)
locationPathName:将被编译应用程序的路径,必须包括所有必要的文件扩展名
target:用于指定要编译的BuildTarget
options:额外的编译选项,多个选项可以组合在一起,例如是否运行那种播放器[MenuItem("Build/BuildWebplayer")]
static function MyBuild()
{var levels: String[]=["Asset/Scene1.unity","Asset/Scene2.unity"];BuildPipeline.BuildPlayer(levels,"WebPlayerBuild" , BuildTarget.WebPlayer,BuildOptions.None);
}
BuildAssetBundleOption选项
在创建AssetBundle文件的时候,Unity提供了若干个创建选项,每个选项的作用
None:
构建AssetBundle没有任何特殊的选项。
DisableWriteTypeTree:
在AssetBundle中不包含类型信息。但需要注意的事,如果将AssetBundle发布到Web平台上,则不能使用该选项
DeterministicAssetBundle
使每个Object具有唯一不变的hash ID ,可用于赠量式发布AssetBundle。
UncompressedAssetBundle
不进行数据压缩。如果使用该选项,因为没有压缩/解压缩的过程,AssetBundle的发布和加载会更快,但是AssetBundle也会更大,导致下载速度变慢
ForceRebuildAssetBundle
强制重新Build所有的AssetBundle
IngoreTypeTreeChanges
忽略TypeTree的变化,不能与DisableTypeTree同时使用。
AppendHashToAssetBundleName
附加Hash到AssetBundle名称中。
BuildTarget选项
目标的构建平台
AssetBundle在不同平台之间是不完全兼容的,在多个独立平台构建的AssetBundle可以再这些平台上加载,但并不能在IOS和Android上加载,这需要单独指定他们的BuildTarget。此外,Android和IOS之间也不能相互兼容。
Unity处理Asset之间的依赖关系
Unity会自动处理Asset之间的依赖关系,并把这种依赖关系Build到AssetBundle之中,不再需要通过PopAssetDependencies()/PushAssetDenpencies()来处理依赖关系。
Unity提供了Manifest文件向用户展示这些依赖关系,在处理Assets的依赖关系时不再需要重新打包整个依赖链
1、Cube>Material>Texture
2、Cube.unity3d>material.unity3d
3、更新Material的Texture
4、只需重新打包,material.unity3d即可
在Editor模式下,Unity为每个AssetBundle都会生成一个Manifest文件,在Manifest文件中包含:
1、CRC
2、所包含的Assets
3、所依赖的AssetBundles
4、Hash
5、ClassTypes
AssetBundle Manifest提供了以下访问接口:
GetAllAssetBundles()
获得所有的AssetBundle的Manifest。
GetAllAssetBundlesWithVariant()
获取所有Variant的AssetBundles的Manifest
GetDependencies(string)
获取给定AssetBundle所依赖的AssetBundles。
GetAssetBundleHash(string)
获取给定AssetBundle的Hash。
GetDirectDependencies(string)
获取给定AssetBundle直接依赖的AssetBundles。
WWW wwwManifest = new WWW(manifestPath);
yield return wwwManifest;
AssetBundle manifestBundle = wwwManifest.assetBundle;
AssetBundleManifest manifest = (AssetBundleManifest)manifestBundle.LoadAsset("AssetBundleManifest");
manifestBundle.Unload(false);
string[] allAssetBundles = manifest.GetAllAssetBundles();
//assetBundleName为assetbundle名称.
string[] depedentAssetBundles = manifest.GetAllDependencies(assetBundleName);
在对资源打包后,在输出的路径文件下会有一个总的manifest文件,文件名与所在的文件夹名称相同,然后每一个大包的资源分别会有一个自己的manifest文件。
Name:表示AssetBundle的名称
Dependencies:表示该AssetBundle所依赖的AssetBundle,如果内容为空,则说明该AssetBundle没有依赖的AssetBundle
如何下载AssetBundle
Unity引擎提供了两种方式来从服务器上动态下载AssetBundle文件
1、非缓存机制
通过创建一个WWW实例来对AssetBundle文件进行下载。下载后的AssetBundle文件将不会进如Unity引擎特定的缓存区
IEnumerator Start()
{//开始从指定的URL下载WWW www=new WWW(url);yield return www;
}
2、缓存机制
通过WWW.LoadfromCacheorDownload接口来下载AssetBundle文件。下载后的AssetBundle文件将自动被存放在Unity引擎特定的缓存区,该方法是Unity推荐的AssetBundle文件下载方式。
在下载AssetBundle文件时,该接口会先根据版本号在本地缓存区中查找该文件,看其之前是否被下载过。
如果下载AssetBundle文件时,该接口会先根据版本号在本地缓存区中查找该文件,看其之前是否被下载过。如果下载过,则直接从缓存区将其读入进来;
如果没有,则从服务器下进行下载。这中做法的好处是,可以节省AssetBundle文件的下载时间,从而提高游戏资源的载入速度
需要注意的是:
Unity提供的缺省缓存大小事根据发布平台的不同而不同的。
目前,对于发布到Web Player上的网页游戏,缓存缺省大小为50M
对于发布到PC上的客户端游戏和发布到IOS/Android上的移动游戏,缓存缺省大小为4G
开发商可以通过购买有Unity提供的Caching license来增大网页平台上的缓存大小,该授权可以极大地提供网页游戏的运行流畅度。
IEnumerator Start()
{//开始从指定的URL下载WWW www=WWW.LoadFromCacheOrDownload(url,5);//等待下载完成yield return www;}
AssetBundle的加载与卸载
如果加载AssetBundle
当AssetBundle文件从服务器下载到本地之后,需要将其加载到内存中并创建AssetBundle文件内存对象。
Unity提供了四种方式来加载AssetBundle文件:
1、WWW.asssetbundle属性
可以通WWW.asssetbundle属性来创建一个AssetBundle文件的内存对象。
IEnumerator Start()
{WWW www=WWW.LoadFromCacheOrDownload(url,5);yield return www;if(www.error!=null){Debug.Log(www.error);return;}AssetBundle myLoadedAssetBundle=www.assetBundle;
}
2、AssetBundle.CreateFormFile()
完整定义
public static AssetBundle CreateFromFile(string path)
通过该接口可以从磁盘文件创建一个AssetBundle文件的内存对象。需要注意的是,该方法仅支持非压缩格式的AssetBundle,即在创建AssetBundle时必须使用UncompressedAssetBundle选项。
3、AssetBundle.CreateFromMemory()
public static AssetBundleCreateRequest CreateFromMemory(byte[] binary)
通过该接口可以创建一个异步AssetBundle内存区域。
比如当用户对AssetBundle进行加密时,可以先调用解密算法返回解密后的数据流,然后再通过AssetBundle.CreateFromMemory从数据流创建AssetBundle对象。
IEnumerator Start()
{//开始下载AssetBundlevar www=WWW.LoadFromCacheOrDownload(url ,5);//等待下载完成yield return www;//从bytes数组中创建AssetBundleAssetBundleCreateRequest assetBundleCreateRequest=AssetBundle.CreateFromMemory(www.bytes);yield return assetBundleCreateRequest;AssetBundle assetBundle=assetBundleCreateRequest.assetBundle;
}
4、AssetBundle.CreateFromMemoryImmediate
public static AssetBundle CreateFromMemoryImmediate(byte[] binary)
通过该接口可以同步创建一个AssetBundle内存区域
IEnumerator Start()
{WWW www = new WWW("http://myserver/myBundle.unity3d");yield return www;AssetBundle assetBundle = AssetBundle.CreateFromMemoryImmediate(www.bytes);
}
需要注意的是:如果AssetBundle之间存在依赖关系,要先加载总的manifest文件,通过manifest文件加载对应的依赖文件,然后再加载要加载的AssetBundle。
public class LoadAssets:MonoBehavior
{//总的manifest文件名称public string manifestName="AssetBundles";//要加载的AssetBundle名称public string assetBundleName="test";IEnumerator Start(){//assetbundle所在的路径string assetBundlePath="file://"+Application.dataPath+"/AssetBundles";//manifest文件所在路径string manifestPath=AssetBundlePath+manifestName;//首先加载manifest文件WWW wwwManifest=WWW.LoadFromCacheOrDownload(manifestPath,0);yield return wwwManifest;if(wwwManifest.error==null){AssetBundle manifestBundle=wwwManifest.assetBundle;AssetBundleManifest manifest=(AssetBundleManifest)manifestBundle.LoadAsset("AsssetBundleManifest");manifestBundle.Unload(false);//获取依赖文件列表string[] depedentAssetBundles=manifest.GetAllDependenies(assetBundleName);AssetBundle[] abs=new AssetBundle[depedentAssetBundles.Length];for(int i=0;i<depedentAssetBundles.Length;i++){//加载所有的依赖文件WWW www=WWW.LoadFromCacheOrDownload(assetBundlePath+depedentAssetBundles[i],0);yield return www;abs[i]=www.assetBundle;}//加载需要的文件WWW www2=WWW.LoadFromCacheOrDownload(assetBundlePath+assetBundleName,0);yield return www2;AssetBundle assetBundle=www2.assetBundle;}else{Debug.Log(wwwManifest.error);}}
}
如何从AssetBundle中加载Assets
当AssetBundle文件加载完成后,就可以将所包含的Assets加载到内存中,Unity提供了六种加载API进行开发
AssetBundle.LoadAsset()
该接口可以通过名字来将AssetBundle文件中包含的对应Asset名称同步加载到内存中,也可以通过参数来指定加载Asset的类型
AssetBundle.LoadAllAsync()
该接口的作用与AssetBundle.LoadAsset相同,不同的是该接口是对Asset进行异步加载,即加载时主线程可以继续执行
AssetBundle.LoadAllAssets()
该接口用来一次性同步加载AssetBundle文件中的所有Assets,同AssetBundle.LoadAsset一样,可以通过指定加载Asset的类型来选择性地加载Assets。
AssetBundle.LoadAllAseetAsync()
该接口用来一次性异步加载AssetBundle文件中的所有Assets
AssetBundle.LoadAllAssetWithSubAssets()
该接口可以通过名字同步加载AssetBundle文件中的Assets的子Assets,同AssetBundle.LoadAsset一样,可以通过指定加载Asset的类型来选择性地加载Assets。
在Unity5后,不再支持直接Load组件类型,先试用Load游戏对象然后查找组件对象
IEnumerator Start()
{WWW www=WWW.LoadFromCacheOrDownload(url,1);yield return www;//取得AssetBundleAssetBundle bundle=www.assetBundle;//异步加载对象AssetBundleRequest request=bundle.LoadAssetAsync("myObject",typeof(GameObject));//等待异步完成yield return request;//获得加载对象的引用GameObject obj=request.asset as GameObject;//从内存中卸载AssetBundlebundle.Unload(false);//将没有使用的资源卸载
}
如何从场景AssetBundle中加载Assets
Application.LoadLevel
该接口可以通过名字或者索引载入AssetBundle文件中包含的对应场景。当加载新场景中,所有之前加载的GameObject都会被销毁
Application.LoadLevelAsync
该接口的作用与Application.LoadLevel相同,不同的是该接口是对场景进行异步加载,即加载时主线程可以继续执行
Application.LoadLevelAdditive
不同于Application.LoadLevel的是,它并不销毁之前加载的GameObject。
Application.LoadLevelAdditiveAsync
该接口的作用与Application.LoadLevelAdditive相同,不同的是该接口是对场景进行异步加载,即加载时主线程可以继续执行。
Application.LoadLevelAsync接口异步加载场景
public class LoadScenes:MonoBehaviour{//场景文件名字public string sceneAssetBundle;//场景名称public string sceneName;IEnumerator Start(){//场景AssetBundle路径string path="file://"+Application.dataPath+"/AssetBundles/"+sceneAssetBundle;WWW www=WWW.LoadFromCacheOrDownload(path,0);yield return www;if(www.error==null){AssetBundle ab=www.assetBundle;AsyncOperation async=Application.LoadLevelAsync(sceneName);yield return async;}else{Debug.Log(www.error);}}
}
AssetBundle Variant的使用
AssetBundle Variant通过AssetBundle用来实现Virtual Asset,和不同分辨资源的使用,最终达到在运行时动态替换AssetBundle。
AssetBundle名称相同但不同AssetBundle Variant的AssetBundle之间将有共同的内部id,所以它们之间可以任意切换。
用户可以通过AssetBundle的UI来设置
AssetImporter.assetBundleVariant:
AssetImporter assetImporter=new AssetImporter();
assetImporter.assetBundleVariant="hd";
AssetBundleBuild.assetBundleVariant:
AssetBundleBuild assetBundleBuild =new AssetBundleBuild();
assetBundleBuild.assetBundleVariant="sd";
AssetBundle Variant会用于AssetBundle的后缀名中
AssetImporter.assetBundleName="myassets";
assetBundleBuild.assetBundleVariant="hd";
最终AssetBundle name为"myassets.hd"
AssetBundle的内存管理
内存管理是游戏制作中非常重要的一步。
下载和加载AssetBundle时对内存的影响
1、AssetBundle文件的下载和加载
在下载时可通过WWW或WWW.LoadfromCacheOrDownload方法从服务端或本地缓存区获得AssetBundle文件,并通过WWW.assetbundle属性加载AssetBundle对象。
Unity引擎在使用WWW方法时会分配一系列的内存空间来存放WWW实例以及Web stream数据。
该数据包括原始的AssetBundle数据、解压后的AssetBundle数据以及一个用于解压的Decompression Buffer。
一般情况下,Decompression Buffer会在原始的AssetBundle完成解压后自动销毁。但需要主要的是,Unity内部会自动保留一个Decompression Buffer,使其不被系统回收。
例如当同时加载3个AssetBundle时,那么系统会生成3个Decompression Buffer并同时对每个AssetBundle进行解压,当解压完成后,系统会自动销毁其中的2个Decompression Buffer,而留下一个Decompression Buffer,以备下次解压缩AssetBundle时复用。这样的做的好处是,不用过于频繁地开辟和销毁解压Buffer,从而可以在一定程序上降低CPU的小孩
2、资源转换和实例化
当把AssetBundle解压并加载到内存后,开发者可以通过从WWW.assetbundle属性所获得的AssetBundle对象来得到各种Asset,并对这些Assets进行加载或实例化操作。
加载过程中,Unity会将AssetBundle中的数据流转变成引擎可识别的信息类型(纹理、材质、对象等)。加载完成后,开发者即可对其进一步的操作,比如对象的实例化、纹理和材质的复制、替换等。
AssetBundle以及Asset的卸载
无论是在下载和加载过程中,还是在Asset加载和实例化过程中,AssetBundle以及由其加载的Assets均会占用内存。
1、AssetBundle的卸载
通过AssetBundle.Unload接口卸载AssetBundle自身
2、从AssetBundle加载的Assets的卸载
对于从AssetBundle加载的Asset,比如纹理、材质、音频片段和动画片段等,有两种方式进行卸载
Assetbundle.Unload(true)
该接口会强行卸载掉所有AssetBundle中加载的Assets,不推荐使用这种方式卸载Assets
Resources.UnloadUnusedAssets()
该接口会卸载掉所有没有使用的Assets,需要强调的是,它的作用范围不仅是当前的AssetBundle,而是整个系统,而且它也不能卸载掉从当前AssetBundle文件中加载的仍在使用的Assets
对于由GameObject.Instantiate接口实例化除的GameObject,Unity提供了GameObject.Destory或GameObject.DestroyImmediate接口来讲其卸载。Unity推荐使用GameObject.Destroy接口,如果使用该接口,Unity会将真正的删除操作延后到一个合适的时机统一进行处理,但是会在渲染之前进行。
WWW、AssetBundle以及Asset的关系
WWW对象和通过WWW.assetbundle属性所加载的AssetBundle对象会对Web Stream数据持有引用,同时AssetBundle也会引用到从它所加载的所有Asset。
值得一提的事,真正的数据都是存放Web Stream数据中,例如纹理、模型等,而WWW和AssetBundle对象只是一个结构指向了Web Stream数据
对于WWW对象本身,可以使用www=null或WWW.dispose来进行卸载。二者的效果一样,只是www=null
不会及时引起内存释放操作,而是在系统自动垃圾回收时进行释放;而WWW.dispose会及时调用系统的垃圾回收操作来进行内存释放。
WWW对象释放后,其对于Web Stream数据的引用计数也会相应减1.
对于Web Stream数据,其所占用的内存会在其引用计数为0时,自动被系统进行释放
AssetBundle对象以及WWW对象被释放后,Web Stream数据所占用的内存也会被系统自动回收