AssetBundle杂谈

前端之家收集整理的这篇文章主要介绍了AssetBundle杂谈前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

首先我们来看一下官方Build AssetBundle的示例代码

// C# Example
	// Builds an asset bundle from the selected objects in the project view.
	// Once compiled go to "Menu" -> "Assets" and select one of the choices
	// to build the Asset Bundle
	
	using UnityEngine;
	using UnityEditor;
	public class ExportAssetBundles {
		[MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
		static void ExportResource () {
			// Bring up save panel
			string path = EditorUtility.SaveFilePanel ("Save Resource","","New Resource","unity3d");
			if (path.Length != 0) {
				// Build the resource file from the active selection.
				Object[] selection = Selection.GetFiltered(typeof(Object),SelectionMode.DeepAssets);
				BuildPipeline.BuildAssetBundle(Selection.activeObject,selection,path,BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets);
				Selection.objects = selection;
			}
		}
		[MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")]
		static void ExportResourceNoTrack () {
			// Bring up save panel
			string path = EditorUtility.SaveFilePanel ("Save Resource","unity3d");
			if (path.Length != 0) {
				// Build the resource file from the active selection.
				BuildPipeline.BuildAssetBundle(Selection.activeObject,Selection.objects,path);
			}
		}
	}
再来看看官方的解释:
  1. uild AssetBundle From Selection - Track dependencies. This will build the current object into an asset bundle and include all of its dependencies. For example if you have a prefab that consists of several hierarchical layers then it will recursively add all the child objects and components to the asset bundle.

  2. Build AssetBundle From Selection - No dependency tracking. This is the opposite of the prevIoUs method and will only include the single asset you have selected.

其实官方的这个示例代码中No dependency tracking 这一段是有问题的,如果使用官方的这段代码,不管是否选择自动打包依赖,依赖的相关内容都会打包到最终的assetBundle中去。这是因为BuildPipleLine.BuildAssetBundle接口的默认的BuildAssetBundleOptions参数为:BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets。 我们看官方的定义:

static functionBuildAssetBundle(mainAsset:Object,assets: Object[],sans-serif; font-size:14px; line-height:21.6000003814697px">pathName: string,sans-serif; font-size:14px; line-height:21.6000003814697px">assetBundleOptions:BuildAssetBundleOptions= BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets,sans-serif; font-size:14px; line-height:21.6000003814697px">targetPlatform:BuildTarget= BuildTarget.WebPlayer): bool;

所以我们如果不想自动打包依赖项,需要手动传入不带BuildAssetBundleOptions.CollectDependencies的BuildAssetBundleOptions,如

BuildPipeline.BuildAssetBundle(Selection.activeObject,BuildAssetBundleOptions.CompleteAssets);

我们顺便看一下官方关于BuildAssetBundleOptions.CollectDependencies 和 BuildAssetBundleOptions.CompleteAssets的定义

BuildAssetBundleOptions.CollectDependencies

Includes all dependencies.

This follows all references to any assets,game objects or components and includes them in the build.

BuildAssetBundleOptions.CompleteAssets

Forces inclusion of the entire asset.

For example if you pass a Mesh into the BuildPipeline.BuildAssetBundle function and use CompleteAssets it would also include the game object and any animation clips in the same asse

这两个参数很容易混淆,这里的CollectDependencies主要是指打包的对象所依赖的东西,比如说一个Prefab中可能还有对另外一个prefab的引用,对资源(如图片,shader等)的依赖也是。而CompleteAssets是指打包assetbundle的时候,打包整个asset,这里是个什么概念呢?比如说我们有一个Fbx的模型,里面包含了mesh,动画等,如果我们打包assetbundle的时候,选择的是mesh的话,如果传入CompleteAssets的话,就会打包整个Fbx文件,如果不传入,则只打包Mesh。

一般我们打包assetbundle的时候,会选择自动打包依赖文件。但是这样会有一个问题,也就是说如果多个对象存在对同一个依赖对象的引用的话,会导致存在多份依赖对象,对包体和内存都有影响。这里我们可以使用Unity提供的依赖管理来进行打包,我们来看下官方的示例代码

using UnityEngine;
using UnityEditor;

public class Exporter : MonoBehavIoUr {
    
    [MenuItem("Assets/Export all asset bundles")]
    static void Export() {
        BuildAssetBundleOptions options = 
            BuildAssetBundleOptions.CollectDependencies | 
            BuildAssetBundleOptions.CompleteAssets | 
            BuildAssetBundleOptions.DeterministicAssetBundle;
        
        BuildPipeline.PushAssetDependencies();
        BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/ShadersList.prefab"),null,"WebPlayer/ShadersList.unity3d",options);
            
        BuildPipeline.PushAssetDependencies();
        BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Scene1.prefab"),"WebPlayer/Scene1.unity3d",options);
        BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Scene2.prefab"),"WebPlayer/Scene2.unity3d",options);       
        
        BuildPipeline.PopAssetDependencies();
        BuildPipeline.PopAssetDependencies();
    }
    
    [MenuItem("Assets/Update shader bundle")]
    static void ExportShaders() {
        BuildAssetBundleOptions options = 
            BuildAssetBundleOptions.CollectDependencies | 
            BuildAssetBundleOptions.CompleteAssets | 
            BuildAssetBundleOptions.DeterministicAssetBundle;
        
        BuildPipeline.PushAssetDependencies();
        BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/ShadersList.prefab"),options);
        
        BuildPipeline.PopAssetDependencies();
    }
}

然后我们在加载的时候,需要先加载被依赖的assetbundle,然后再加载其他的assetbundle。但是这里又一些坑。特别是当我们被依赖的assetbundle是资源文件的话,因为是资源文件,所以我们并不会显式的Load(一般的对象比如说prefab,我们在加载assetbundle之后一般会手动Load然后再Instantiate),对于资源文件我们只需要将assetbundle加载到内存,然后后面加载其他的assetbundle的时候,系统会自动去load。这里会有两个问题,

1、对于资源文件,其实我们光加载到内存还不行(类似:WWW asset = new WWW(BundleURL + "res.assetbundle")),我们需要手动调用:AssetBundle bundle = asset.assetBundle;否者后续加载其他的assetbundle会资源丢失,这是为什么呢?我们再来看看官方关于www.assetBundle的定义:

Streams an AssetBundle that can contain any kind of asset from the project folder.

这里解释的其实不好理解,这里应该是通过调用www.assetbundle来将assetbundle的assetbundle的映射结构加载到内存。(WebStream中一共分为三块,Assetbundle原始压缩数据、Decompression Buffer以及Assetbundle的解压数据。WebStream中的仅是数据流,而Assets中的资源则是通过Assetbundle.Load加载出来的真正可被引擎识别的资源,比如Texture2D、Mesh等,所以我们需要将webStrem转换成能够识别的资源,以供系统后续load)。

2、对于一般的Assetbundle,我们通常会调用assetBundle.Unload(false)来释放内存中的AssetBundle的原始数据。但是对于资源文件,我们又不能这样操作了。我们先来看下官方关于Unload的定义:

AssetBundle.Unload
Unload( unloadAllLoadedObjects: bool): void;

Unloads all assets in the bundle.

Unload frees all the memory associated with the objects inside the bundle.

When unloadAllLoadedObjectsis false,compressed file data for assets inside the bundle will be unloaded,but any actual objects already loaded from this bundle will be kept intact. Of course you won't be able to load any more objects from this bundle.

When unloadAllLoadedObjectsis true,all objects that were loaded from this bundle will be destroyed as well. If there are game objects in your scene referencing those assets,the references to them will become missing.

按描述来说, assetBundle.Unload(false)之后,已经加载到内存中的内容是不会被unload的,但是这里的问题的原理其实还是跟第一点一样,unload之后,内存中的assetbundle的映射结构的会被卸载,所以后续系统对资源的load会出问题。


参考:

http://docs.unity3d.com/Manual/AssetBundlesIntro.html

http://forum.china.unity3d.com/thread-379-1-1.html

猜你在找的设计模式相关文章