赞
踩
要将资源打包成一个asset bundle之前要给对应资源添加asset bundle标签。一般标签创建规则为:文件名+unity3d/ab/assetbundle 后缀。资源有了标签之后,等会我们在代码中调用打AB包的API时,unity就会在整个项目资源文件里面查找拥有标签需要打包的文件,并根据标签打出ab包。注意:Resources/StreamingAssets 文件夹中的内容不会被Unity打包。
对AB包的操作主要用到的是在UnityEditor里面的API。创建则主要是BuildPipeline 这个类。这个方法有很多重载,这里我们用比较简单的一种来进行尝试:
[MenuItem("Assets/Build All AssetBundles)]
private static void BuildAll()
{
BuildPipeline.BuildAssetBundles();
}
首先,顺便复习一下MenuItem这个特性吧。它的作用是能够把拥有该特性的静态 方法(千万注意必须是静态方法)添加到Unity窗口上端的菜单栏里头。根据你传入的字符串在对应的位置创建出该方法的点击按钮,可以让你通过点击触发该方法。
然后就会看到这个效果
然后,我们点击这个方法,如果没有意外就会开始读条打包啦。要注意的是,我这里填写的是相对路径:“AssetBundles”。即会在这个项目根目录下为根节点,往后查找路径。同时,别忘了要保证填写的文件夹已经存在于对应路径中,因为这个方法是不会自动帮你创建的。如果不存在该文件夹就会报“错误路径”之类的错误。为了保险起见,也可以在代码中用IO里面的Directory类去判定一下路径的准确性,并作出对应的处理(比如如果查无此文件夹,则用IO去创建一个该文件夹,保证路径的正确性)。
本地加载的话,可以直接使用AssetBundle类中的LoadFromFile方法:
private void LoadLocalAB()
{
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/scene/wall.unity3d");
//指定名字一个一个加载:注意这里填的是你场景中创建的预设体的名字,不是包名
GameObject wallPrefab = ab.LoadAsset<GameObject>("Floor");
Instantiate(wallPrefab);
//也可以一次性load所有的资源
Object[] objs = ab.LoadAllAssets();
foreach(Object o in objes)
{
Instantiate(o);
}
}
也可以使用AssetBundle.LoadFromMemoryAsync,这里直接上官方代码了:
IEnumerator LoadFromMemoryAsync(string path)
{
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
var prefab = bundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);
}
从官方下载可以用WWW.LoadFromCacheOrDownload或者UnityWebRequest去做。这里我只记录UnityWebRequest。
再次懒惰的搬运一下官网的代码:
IEnumerator InstantiateObject() { string uri = "file:///"+Application.dataPath+"/AssetBundles/"+assetBundleName; UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri,0); //在调用requet.Send()之后才真正开始向服务器发起了请求 yield return request.Send(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); //这里还有另外一种方法送request 里面获取到加载的AssetBundle //AssetBundle bundle = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; GameObject cube = bundle.LoadAsset<GameObject>("Cube"); GameObject sprite = bundle.LoadAsset<GameObject>("Sprite"); Instantiate(cube); Instantiate(sprite); //这里如果需要把加载到的内容保存到本地硬盘,可以参考以下代码:这里的path为想保存到的路径 //File.WriteAllBytes(path, request.downloadHandler.data); }
上面打包的时候有说过依赖打包,为了避免资源丢失(比如材质,贴图等),我们就需要加载使用某个AB包的时候,把它依赖的资源包也加载出来。因此,获取到包与包之间依赖关系就有意义了。而打包时候生成的manifest文件就保存着这个信息。我们可以在加载的时候也把当前保存这些AB包的文件夹的manifest文件加载出来从而获得总的依赖关系。
代码如下,同样来自官方文档:
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestPath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("ManifestName");
string [] dependencies = manifest.GetAllDependencies("assetBundle"); //这里传入你想获取依赖关系的AB包包名
//然后根据依赖关系,一一对应先把依赖资源包加载进来
foreach(string dependency in dependencies)
{
//因为这些依赖资源只需要加载就可以了,所以我们不需要保存返回值
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath,dependency));
}
有加载自然就有卸载。将不用的包从内存中清楚的作用这里就不赘述的。卸载主要是通过AssetBundle自带的一个方法——Unload()。需要传一个bool指来指定是否卸载正在使用中的资源。
AssetBundle.Unload(true); //传入true的时候,无视包中是否有资源正在被使用,全部卸载掉
AssetBundle.Unload(false); //false时,只会卸载掉包中没有正在被使用的资源;仍有引用的资源会被保留
另外,如果想卸载残留的资源。但是此时之前的那些包又已经被卸载过了,这时候就可以用Resources.UnloadUnusedAssets。不过务必保证在场景中和代码中没有对这些资源的引用。非叠加性的场景切换也会自动调用这个方法去卸载残留的无用资源。
这个问题我觉得前面已经说得比较详细了。这里稍微再总结一下:资源如果同时被多个包依赖,那在没有把这些共享资源单独打成一个包的情况下,每一个依赖于这个资源的AB包都会把这个被依赖的资源打一份给自己,因此就造成了重复打包。
解决方法之一就是把共享资源单独打一个包出来。
Unity会在打包的时候自动把图片资源打进一个图集里面。如果有资源依赖于这个图集的其中一个图片,那个AB包就会把整个图集打包一份进去,造成该图集被重复打包。
解决方法就是在手动把每张图片的Packing Tag参数设置一下,保证共享的图片打成一个AB包。有点类似依赖资源重复打包的解决思路(Packing Tag 参数在图片Inspector中,注意把图片格式设成Sprite)。
Unity 官方有在他们GitHub上放了一个AssetBundle的浏览工具。大家有兴趣的可以自行去搜索一下,叫做Asset Bundle Browser tool.
希望这篇文章能够帮到各位的复习和基础的巩固。谢谢阅读!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。