首页 > 代码库 > 【转】Unity3D AssetBundles 动态加载游戏资源
【转】Unity3D AssetBundles 动态加载游戏资源
AssetBundles are files which you can export from Unity to contain assets of your choice. These files use a proprietary compressed format and can be loaded on demand in your application. This allows you to stream content like models, textures, audio clips and even entire scenes separately from the scene in which they will be used. They have been designed to make it easy to download content to your application.
在一些大型的网络游戏,或者加载比较多的一些场景时,如果要等待所有模型,贴图等各种资源文件加载完毕才能执行游戏,对用户将会是一个很头大的事情。所以就需要用到动态加载,即AssetBundles。比如玩家在进入游戏时先加载一些周围的场景文件,这样不仅可以提高速度还可以减少内存资源的消耗。
AssetBundles是可以把unity3d中你所创建的文件或任何资源导出的一种文件格式,这些文件导出后使用的是一种特定的文件格式(.Unity3d),这些特定格式的文件能在需要的时候加载到场景中。而这些特定的文件格式可以是模型,贴图,声音文件甚至是场景文件,它们是先前就被设计好的文件,所以很容易就可以被下载到你所建立的游戏或场景中来。
AssetBundles can contain any kind of asset type recognized by Unity, as determined by the filename extension. If you want to include files with custom binary data, then you must rename those files to have ".bytes" as the extension. Unity will import these files as TextAssets.
AssetBundles 可以是任意类型的文件只要是unity3d能识别的资源文件格式,识别主要是以文件扩展名为准,比如.prefab等等。当然如果你想包含自定义的二进制文件,需要命名这些文件为以".bytes"为后缀,Unity将会把这些文件导成TextAssets。
For some sample code showing how to use asset bundles, see the AssetBundles Example Project
如果想了解更多关于代码如何实现,可以参考 AssetBundles Example Project。
Creating AssetBundles 创建资源包
There are three class methods you can use to build AssetBundles: BuildPipeline.BuildAssetBundle, BuildPipeline.BuildStreamedSceneAssetBundleand BuildPipeline.BuildAssetBundleExplicitAssetNames..
有三个类方法可以用来构建资源包:BuildPipeline.BuildAssetBundle, BuildPipeline.BuildStreamedSceneAssetBundle 和BuildPipeline.BuildAssetBundleExplicitAssetNames.
- BuildPipeline.BuildAssetBundle allows you to build AssetBundles of any type of asset.
可以构建任意类型资源的资源包。【创建一个压缩好的包含Assets下的所有资源文件.unity3d。可以包含是项目目录下的任意资源,而把所有的资源压缩成一个.unity3d的文件,这个文件包括所有的预置物体(prefabs),纹理贴图(textures),模型,动画。使用AssetBundle.mainAsset这个函数可以很方便的让你指定一个定义好的物体。被压缩的资源储存在pathName. Options,会自动的允许用户包含相关的或者需要用到的】 - BuildPipeline.BuildStreamedSceneAssetBundle is used when you want to include only scenes to be streamed and loaded as the data becomes available.
用来当你希望只包括流场景,使数据加载变为可用。【建立一个或多个场景,这些场景所需要的所有资源将被压缩入资源包,即asset bundle里面。资源包中的场景文件可以在任何平台上建立,而且这些文件往往是以一个单一的unity3d文件格式被用来创建。使用WWW类可以下载场景文件。当场景文件被下载好可以使用WWW.LoadFromCacheOrDownload来加载下载好的场景文件。】 - BuildPipeline.BuildAssetBundleExplicitAssetNames is the same as BuildPipeline.BuildAssetBundle but has an extra parameter to specify a custom string identifier (name) for each object.
和BuildPipeline.BuildAssetBundle类似,但是有额外的参数来指定每个物体的自定义的字符串(名字)。【建立一个自定义名字的资源包方法: 创建一个包含所有资源的一个压缩好的unity3d文件。AssetBundle可以包括任何项目目录下的资源文件。在assetNames参数中提供一个与资源数目相同大小的字符串数组。在资源数组中存储的信息与资源文件名相同,可以传递AssetBundle.Load去加载一个指定的资源。使用BuildAssetBundle时,只需使用资源的路径名。压缩好的资源包文件将会被储存在pathName. Options,允许用户自动包含与之相关的或者总是包含完整的资源去代替确切的参照物体。】
当使用上面的三个函数做完准备工作,我们现在就需要去下载这些资源然后加载它们。
Downloading AssetBundles 下载资源包
The recommended way to download AssetBundles is to use WWW.LoadFromCacheOrDownload. Once the download is complete you can retrieve the AssetBundle with the assetBundle property. For example:
推荐使用WWW.LoadFromCacheOrDownload方法用来下载资源。当下载完成你就可以重新得到该资源的相关属性,例如:
string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d"; IEnumerator Start () { // Start a download of the given URL // 从指定路径下载 WWW www = WWW.LoadFromCacheOrDownload (url, 1); // Wait for download to complete // 等待下载完成 yield return www; // Load and retrieve the AssetBundle // 加载并取回资源包 AssetBundle bundle = www.assetBundle; }
When you access the .assetBundle property the downloaded data is extracted and the AssetBundle object is created. At this point, you are ready to load the objects contained in the bundle. The second parameter passed to LoadFromCacheOrDownload specifies which version of the AssetBundle to download. LoadFromCacheOrDownload will download the AssetBundle if it doesn‘t exist in the cache or if it exists but is associated with a version that is lower than the one requested. Otherwise the AssetBundle will be loaded from cache.
以上代码中的url是资源路径的下载地址,首先使用LoadFromCacheOrDownload下载资源,括号中的url是路径,1代表版本号,当等待下载完成后即可加载相关资源。
当资源包中的对象创建好之后,就可以激活下载好的资源被解压。到这里,就需要用户去加载包含在资源包器中的对象。LoadFromCacheOrDownload函数中的第二个参数是指定下载资源的版本号。如果在存储器中的资源没有被下载或者资源下载了但是比需要资源的版本号低,LoadFromCacheOrDownload函数将会下载资源到资源包中。否则资源包其将会直接从存储器中加载。
Loading and unloading objects from an AssetBundle 从资源包加载和卸载对象
Having created an AssetBundle object from the downloaded data, you can load the objects contained in it using three different methods:AssetBundle.Load, AssetBundle.LoadAsync and AssetBundle.LoadAll.
如果下载数据已经建立了资源包,你就可以使用这三个不同的函数来加载物体了: AssetBundle.Load, AssetBundle.LoadAsync 和 AssetBundle.LoadAll.
- AssetBundle.Load will load an object using its name identifier as a parameter. The name is the one visible in the Project view. You can optionally pass an object type as an argument to the Load method to make sure the object loaded is of a specific type.
AssetBundle.Load会加载物体,使用该物体的名字作为识别的参数。名字可以在Unity3d中Project view看到。你可以自由选择去传递对象变量的类型来确认该加载对象是否是被指定的类型。 - AssetBundle.LoadAsync works the same as the Load method described above but it will not block the main thread while the asset is loaded. This is useful when loading large assets or many assets at once to avoid pauses in your application.
AssetBundle.LoadAsync 与AssetBundle.Load 函数相类似,但是当资源加载时它不会阻碍主线程。如果同时要加载比较大的资源或者很多资源时,这个函数会比较有用,它可以避免程序的暂停,因为它会同步加载。 - AssetBundle.LoadAll will load all the objects contained in your AssetBundle. As with AssetBundle.Load, you can optionally filter objects by their type.
AssetBundle.LoadAll 顾名思义这个函数是用来加载所有在你资源包中的对象。作为AssetBundle.Load这个函数,你可以随意用该对象的类型去过滤。
To unload assets you need to use AssetBundle.Unload. This method takes a boolean parameter which tells Unity whether to unload all data (including the loaded asset objects) or only the compressed data from the downloaded bundle. If your application is using some objects from the AssetBundle and you want to free some memory you can pass false to unload the compressed data from memory. If you want to completely unload everything from the AssetBundle you should pass true which will destroy the Assets loaded from the AssetBundle.
使用AssetBundle.Unload这个函数可以卸载加载好的资源,这个函数有一个布尔值的参数是用来告诉Unity是否要卸载所有的数据(包含加载的资源对象)或者只是已经下载过的被压缩好的资源数据。如果要在资源包中从你的应用程序要使用一些对象或者你想释放一些内存,可以传递false这个值来卸载已经压缩好了的文件从你的内存中(即.unity3d文件)。如果要完全卸载所有的资源数据,需要传递true这个值,这将会销毁所有的资源包器中加载的资源。
Listing objects in an AssetBundle 从资源包列表物体
You can use AssetBundle.LoadAll to retrieve an array containing all objects from the AssetBundle. It is not possible to get a list of the identifiers directly. A common workaround is to keep a separate TextAsset with a known name to hold the names of the assets in the AssetBundle.
可以使用AssetBundle.LoadAll 去取回资源包中包含的所有对象的数组。不可能直接就取回一个列表。通常的做法是保持一个单独的TextAsset,这个TextAsset是控制资源包中资源的名称。
Instantiating objects from AssetBundles 从资源包实例化物体
Once you have loaded an object from your AssetBundle, you can instantiate it in your scene with the Instantiate function.
下载了资源,也加载好了,那么就该在场景中使用Instantiate函数去实例化它了。
string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d"; IEnumerator Start () { // Start a download of the given URL // 开始从指定路径下载 WWW www = WWW.LoadFromCacheOrDownload (url, 1); // Wait for download to complete // 等待下载完成 yield return www; // Load and retrieve the AssetBundle // 加载并取回资源包 AssetBundle bundle = www.assetBundle; // Load the TextAsset object // 加载文本资源对象 GameObject go = bundle.Load("myGameObject", typeof(GameObject)) as GameObject; // Instantiate the GameObject // 实例化该对象 Instantiate(go); }
Keeping track of downloaded AssetBundles 保持跟踪下载的资源包
Unity doesn‘t provide an automatic way to retrieve a list of AssetBundles that have been downloaded. You can keep track of this information from a script by storing references to the AssetBundle objects and their URLs, say.
Unity不提供自动的一个可以取回所有被下载资源的列表。所以需要我们在脚本中要建立这些资源对象的信息和它们的路径以便我们去查找。
Storing and loading binary data in an AssetBundle 在资源包储存和加载二进制数据
The first step is to save your binary data file with the ".bytes" extension. Unity will treat this file as a TextAsset. As a TextAsset the file can be included when you build your AssetBundle. Once you have downloaded the AssetBundle in your application and loaded the TextAsset object, you can use the .bytes property of the TextAsset to retrieve your binary data.
第一步要储存文件的扩展名以" .bytes"为结尾的二进制数据。Unity会把它们看成TextAsset来使用。当你建立资源包就会包含一个TestAsset的文件。当在资源包应用程序中下载好这个二进制数据后也需要加载它们,可以使用TextAsset的.bytes的属性去取回你的二进制数据。
string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d"; IEnumerator Start () { // Start a download of the given URL WWW www = WWW.LoadFromCacheOrDownload (url, 1); // Wait for download to complete yield return www; // Load and retrieve the AssetBundle AssetBundle bundle = www.assetBundle; // Load the TextAsset object TextAsset txt = bundle.Load("myBinaryAsText", typeof(TextAsset)) as TextAsset; // Retrieve the binary data as an array of bytes byte[] bytes = txt.bytes; }
Including scripts in AssetBundles 在资源包中包含脚本
AssetBundles can contain scripts as TextAssets but as such they will not be actual executable code. If you want to include code in your AssetBundles that can be executed in your application it needs to be pre-compiled into an assembly and loaded using the Mono Reflection class (Note: Reflection is not available on iOS). You can create your assemblies in any normal C# IDE (e.g. Monodevelop, Visual Studio) or any text editor using the mono/.net compilers.
资源包可以作为TextAssets包含脚本但是不会实际执行代码。如果想要在资源包包含用来执行应用程序的代码,需要预先编译,然后使用Mono Reflection class来加载(注意:Reflection在iOS平台不可用)。可以在任何版本的C#IDE编辑器(如:Monodevelop, Visual Studio)或者使用mono/.net 文档编辑器。
string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d"; IEnumerator Start () { // Start a download of the given URL WWW www = WWW.LoadFromCacheOrDownload (url, 1); // Wait for download to complete yield return www; // Load and retrieve the AssetBundle AssetBundle bundle = www.assetBundle; // Load the TextAsset object TextAsset txt = bundle.Load("myBinaryAsText", typeof(TextAsset)) as TextAsset; // Load the assembly and get a type (class) from it var assembly = System.Reflection.Assembly.Load(txt.bytes); var type = assembly.GetType("MyClassDerivedFromMonoBehaviour"); // Instantiate a GameObject and add a component with the loaded class GameObject go = new GameObject(); go.AddComponent(type); }
Managing asset dependencies 管理相关资源
Any given asset in a bundle may depend on other assets. For example, a model may incorporate materials which in turn make use of textures and shaders. It is possible to include all an asset‘s dependencies along with it in its bundle. However, several assets from different bundles may all depend on a common set of other assets (eg, several different models of buildings may use the same brick texture). If a separate copy of a shared dependency is included in each bundle that has objects using it, then redundant instances of the assets will be created when the bundles are loaded. This will result in wasted memory.
任何在资源包中给出的资源可能主要依赖于其它的资源。举个例子,一个模型可能混合多种纹理和材质球中的材质,可能包括所有的某个资源相关的东西,然而,一些从不同资源包文件夹中的资源或许都要用到相同的一些其它资源(比如说一些不同的建筑模型会用到相同的纹理贴图)。如果在每个资源文件夹中包含一个被共享的资源要被某个对象用到,当资源文件加载的时候那就不需要去实例化多余的对象,换句话说就是只要把共享的资源实例化一次就可以了,这样就可以大大减少内存的占用。
To avoid such wastage, it is possible to separate shared dependencies out into a separate bundle and simply reference them from any bundles with assets that need them. First, the referencing feature needs to be enabled with a call to BuildPipeline.PushAssetDependencies. Then, the bundle containing the referenced dependencies needs to be built. Next, another call to PushAssetDependencies should be made before building the bundles that reference the assets from the first bundle. Additional levels of dependency can be introduced using further calls to PushAssetDependencies. The levels of reference are stored on a stack, so it is possible to go back a level using the correspondingBuildPipeline.PopAssetDependencies function. The push and pop calls need to be balanced including the initial push that happens before building.
要避免资源的浪费,可以单独建立一个专门的文件夹用来放要被共享的资源或者所有需要被加载的对象,官方提供的AssetBundles Example Project中的AssetBundles这个文件夹就是一个不错的选择,要查看该文件夹需要进入unity3D->在Project中右击会看到一个下拉菜单有Auto Build Rescources Files的选项,点击它,然后进入AsssetBundles资源目录下你会发现多了一个名为AssetBundles的文件夹,所有的资源已经被转化成.unity3d的格式存储在这里了。要把这些资源存储在指定的AssetBundles这个文件夹下,需要用到BuildPipeline.PushAssetDependencies和BuildPipeline.PopAssetDependencies这两个函数。首先需要BuildPipeline.PushAssetDependencies来激活将要使用到的资源文件夹(比如AssetBundles Example Project中的AssetBundles这个文件夹)。然后就需要建立你想在这个文件夹中存放什么资源了,比如说一个纹理贴图。接着再次呼叫BuildPipeline.PushAssetDependencies来激活下一个将要使用到的和先前存放资源相关的其它资源。比如说有一个角色的模型,这个角色模型有它自己的纹理贴图,那就需要先激活它的贴图然后激活模型本身。如此反复直到把所有与需要加载资源的其它相关资源都使用BuildPipeline.PushAssetDependencies加载完毕,最后再使用BuildPipeline.PopAssetDependencies来告诉程序这个模型所有的相关资源已经全部加载完成。
At runtime, you need to load a bundle containing dependencies before any other bundle that references them. For example, you would need to load a bundle of shared textures before loading a separate bundle of materials that reference those textures.
在运行时,需要考虑到优先加载资源的顺序。举个例子,现在有一张纹理贴图,这张贴图可能被很多个模型需要用到,而这个贴图又有很多不同的材质,那就得先加载纹理贴图,然后材质,最后才是模型。
具体的代码实现过程可参考AssetBundles Example Project中的AutoExportRescources这个脚本。
Can I reuse my AssetBundles in another game?
可以在其他的游戏场景重新使用我的资源包吗?
AssetBundles allow you to share content between different games. The requirement is that any Assets which are referenced by GameObjects in your AssetBundle must either be included in the AssetBundle or exist in the application (loaded in the current scene). To make sure the referenced Assets are included in the AssetBundle when they are built you can pass the BuildAssetBundleOptions.CollectDependencies option.
资源包允许用户去共享资源包中的内容到其它不同的游戏。如果需要共享,前提需要你已经建立好这个资源包共享文件。可以使用BuildAssetBundleOptions.CollectDependencies这个函数来检测是否已经建立好资源包文件。
How are assets in AssetBundles identified?
资源包中的资源如何被识别?
When you build AssetBundles the assets are identified internally by their filename without the extension. For example a Texture located in your Project folder at "Assets/Textures/myTexture.jpg" is identified and loaded using "myTexture" if you use the default method. You can have more control over this by supplying your own array of ids (strings) for each object when Building your AssetBundle withBuildPipeline.BuildAssetBundleExplicitAssetNames.
当在内部建立资源包时,它们的文件名不包含扩展名就会被识别。举个例子,一个在你项目工程目录下比如说"Assets/Textures/myTextures.jpg"的贴图是用"myTexture"来识别。如果使用默认的方法,当你用BuildPipeline.BuildAssetBundleExplicitAssetNames这个函数建立你的资源包,那你可以有很多你自己编号的数组上的控制。
Loading objects from an AssetBundles asynchronously
从资源包异步加载对象
You can use the AssetBundle.LoadAsync method to load objects Asynchronously and reduce the likelihood of having hiccups in your application.
可以使用AssetBundle.LoadAsync方法来异步加载对象,这样一来可以减少在应用程序中被打断的可能性。
using UnityEngine; IEnumerator Start () { // Start a download of the given URL WWW www = WWW.LoadFromCacheOrDownload (url, 1); // Wait for download to complete yield return www; // Load and retrieve the AssetBundle AssetBundle bundle = www.assetBundle; // Load the object asynchronously AssetBundleRequest request = bundle.LoadAsync ("myObject", typeof(GameObject)); // Wait for completion yield return request; // Get the reference to the loaded object GameObject obj = request.asset as GameObject; }
Are AssetBundles cross-platform?
资源包可以跨平台吗?
AssetBundles are compatible between some platforms. Use the following table as a guideline.
在有一些平台中是可以兼容的,可以参考下面的这个表格。
Platform compatibility for AssetBundles 资源包在平台的兼容性 | ||||
Standalone | Webplayer | iOS | Android | |
Editor | Y | Y | Y | Y |
Standalone | Y | Y | ||
Webplayer | Y | Y | ||
iOS | Y | |||
Android | Y |
How do I cache AssetBundles? 如何缓存资源包?
You can use WWW.LoadFromCacheOrDownload which automatically takes care of saving your AssetBundles to disk. Be aware that on the Webplayer you are limited to 50MB in total (shared between all webplayers). You can buy a separate caching license for your game if you require more space.
使用WWW.LoadFromCacheOrDownload就可以自动在你的硬盘上存储资源包文件。但是说明一下Webplayer被限制50MB的存储空间。如果需要更多空间则可以单独为游戏购买缓存许可证。
Protecting content 保护内容
Unity allows you to create an AssetBundle object from a byte[] array with AssetBundle.CreateFromMemory. You can use this as a way to enhance the security by encrypting your assetbundles before transmission and decrypt them at runtime.
Unity允许用户使用AssetBundle.CreateFromMemory从一个 byte[]数组中建立一个AssetBundle的对象。在运行时传输和解密之前,可以用这种加密方法来提高安全性和保护用户建立的资源包中内容。
string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d"; IEnumerator Start () { // Start a download of the given URL WWW www = new WWW (url); // Wait for download to complete yield return www; // Get the byte data byte[] encryptedData = www.bytes; // Load the TextAsset object byte[] decryptedData = YourDecryptionMethod(encryptedData); // Create an AssetBundle from the bytes array AssetBundle bundle = AssetBundle.CreateFromMemory(decryptedData); // You can now use your AssetBundle }