Assetbundle2

Assetbundle可以将Prefab封装起来,这是多么方便啊! 而且我也强烈建议大家将Prefab封装成Assetbundle,因为Prefab可以将游戏对象身上带的游戏游戏组件、游戏脚本、材质都封装在一起。当从服务器上将Assetbundle下载以后直接Instantiate就可以放入游戏中。

无论是模型资源还是UI资源,最好是先把他们放在Prefab中,然后在做成Assetbundle

如果打包场景的话那么内存与size就是 公用模型的size * N个场景,想想其实挺恐怖的。其实我们可以巧妙的使用,首先把场景中公用的部分和私有的部分统统放入Unity, 然后烘培整个场景。 当场景烘培完毕后把公用的模型部分在拿出去,场景只只保留私有的模型。还可以做一个工具将公用模型在场景中的坐标保存在XML中(每个场景文件会对应一个公用模型的XML信息),最后在将公用的模型分别封装在别的Assetbundle中。

       服务器上提供每个场景的Assetbundle ,和公用模型的Assetbundle,一般公用模型的Assetbundle可以放在常驻内存中(可能使用频繁、根据项目的不同而定)场景Assetbundle下载完毕后,现载入场景然后在根据场景对应的XML信息将公用模型部分动态的在添加到场景中,这样就完成了一个场景的构建。

Prefab打包技巧: Prefab打包时自身是不占多少空间的 <=1KB  但是Prefab上是可以关联  这五大部分 “界面,模型,特效,声音,场景,脚本”以及在Hierarchy视图中 坐标/缩放/旋转。 关联这些信息以后就会很大,所以为了避免资源的浪费尽量避免Prefab重复关联。

一个prefab下面可以同时关联多个游戏对象 ,这里举个例子如果你的 Prefab下面放了一个模型 它的大小可能是500k  ,在 Prefab下面放了十个完全相同模型 它的大小可能是501k 。 如果Prefab下面放了两个不同的模型,它的大小可能就会是 500k x 2 的size  ,也就是说Prefab与关联的数量是无关的 。 

加密部分: assetbundle 是可以转换成 字节数组 ,客户端与服务器约定一组解密 字节数组的算法就可以实现资源加密。

1、相同的模型尽量打包在一起,他们会公用一套资源文件。

2、相同模型具有不同的脚本、组件的话把他们放在不同的Prefab中,最后把这些Prefab一起打包在一个Assetbundle中

3、不相同的模型尽量分开打包

对在Project视图中选中的对象进行分开打包:

 [MenuItem("Custom Editor/Create AssetBunldes Main")]
    static void CreateAssetBunldesMain()
    {
        //获取在Project视图中选择的所有游戏对象
        Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
        //遍历所有的游戏对象
        foreach (Object obj in SelectedAsset)
        {
            string sourcePath = AssetDatabase.GetAssetPath(obj);
            //本地测试:建议最后将Assetbundle放在StreamingAssets文件夹下,如果没有就创建一个,因为移动平台下只能读取这个路径
            //StreamingAssets是只读路径,不能写入
            //服务器下载:就不需要放在这里,服务器上客户端用www类进行下载。
            string path = CreatePath();
            string targetPath = path + obj.name + ".assetbundle";
            if (BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies))
            {
                Debug.Log(obj.name + "资源打包成功");
            }
            else
            {
                Debug.Log(obj.name + "资源打包失败");
            }
        }
        //刷新编辑器
        AssetDatabase.Refresh();
    }

对在Project视图中选中的对象进行统一打包,打包成一个AB:

 [MenuItem("Custom Editor/Create AssetBunldes ALL")]
    static void CreateAssetBunldesALL()
    {
        Caching.CleanCache();

        string path = CreatePath();
        string Path = path + "ALL.assetbundle";

        Object[] SelectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);

        //这里注意第二个参数就行
        if (BuildPipeline.BuildAssetBundle(null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies))
        {
            AssetDatabase.Refresh();
        }
        else
        {

        }
    }

    private static string CreatePath()
    {
        string path = Application.dataPath + "/StreamingAssets/";
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        return path;
    }

BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)

参数1:它只能放一个对象,因为我们这里是分别打包,所以通过循环将每个对象分别放在了这里。

参数2:可以放入一个数组对象。

默认情况下打的包只能在电脑上用,如果要在手机上用就要添加一个参数。

Android上:BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies,BuildTarget.Android)

IOS上:BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies,BuildTarget.iPhone)

另外,电脑上和手机上打出来的Assetbundle不能混用,不同平台只能用自己的。

读取Assetbundle

然后我们来学习如何运行时读取Assetbundle,Assetbundle是可以同时放在服务器或者本地的,无论放在哪里两种下载读取的方式是完全一样的

 //不同平台下StreamingAssets的路径是不同的,这里需要注意一下。
    public static readonly string PathURL =
#if UNITY_ANDROID
        "jar:file://" + Application.dataPath + "!/assets/";
#elif UNITY_IPHONE
        Application.dataPath + "/Raw/";
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
 "file://" + Application.dataPath + "/StreamingAssets/";
#else
        string.Empty;
#endif

    void OnGUI()
    {
        //读取一个资源
        if (GUILayout.Button("Main Assetbundle"))
        {
            StartCoroutine(LoadMainGameObject(PathURL + "Cube1.assetbundle"));
            StartCoroutine(LoadMainGameObject(PathURL + "Cube2.assetbundle"));
        }
        //读取全部资源
        if (GUILayout.Button("ALL Assetbundle"))
        {
            StartCoroutine(LoadALLGameObject(PathURL + "ALL.assetbundle"));
        }

    }

    //读取一个资源
    private IEnumerator LoadMainGameObject(string path)
    {
        WWW bundle = new WWW(path);
        yield return bundle;

        //加载到游戏中   mainAsset只适用于ab里面只打包了一个对象
        yield return Instantiate(bundle.assetBundle.mainAsset);
        bundle.assetBundle.Unload(false);
    }

    //读取全部资源
    private IEnumerator LoadALLGameObject(string path)
    {
        WWW bundle = new WWW(path);
        yield return bundle;

        //通过Prefab的名称把他们都读取出来
        Object obj0 = bundle.assetBundle.Load("Cube1");
        Object obj1 = bundle.assetBundle.Load("Cube2");

        //加载到游戏中    
        yield return Instantiate(obj0);
        yield return Instantiate(obj1);
        bundle.assetBundle.Unload(false);
    }

这里我们详细的说说 下载类WWW

WWW bundle = new WWW(path);

这样的做法是通过一个路径进行下载(无论是服务器路径还是本地路径下载操作都一样)但是bundle只能保存在内存中,也就是退出游戏在进入还得重新下,很显然在游戏中我们不能使用这种方式。

private IEnumerator LoadMainCacheGameObject(string path)
    {
//如果缓存中没有此ab,就去网络上下载 WWW bundle
= WWW.LoadFromCacheOrDownload(path,5); yield return bundle; //加载到游戏中 yield return Instantiate(bundle.assetBundle.mainAsset); bundle.assetBundle.Unload(false); }

使用的方法是WWW.LoadFromCacheOrDownload(path,5);

参数1:服务器或者本地下载地址

参数2:版本号

         Unity会下载Assetbundle本地中,它的工作原理是先通过(下载地址和版本号)先在本地去找看有没有这个Assetbundle,如果有直接返回对象,如果没有的话,再根据这个下载地址重新从服务器或者本地下载。这里版本号起到了很重要的作用,举个例子,同一下载地址版本号为1的时候已经下载到本地,此时将版本号的参数改成2 那么它又会重新下载,如果还保持版本号为1那么它会从本地读取,因为本地已经有版本号为1的这个Assetbundle了。你不用担心你的资源本地下载过多,也不用自己手动删除他们,这一切的一切Unity会帮我们自动完成,它会自动删除掉下载后最不常用的Assetbundle ,如果下次需要使用的话只要提供下载地址和版本后它会重新下载。

我们再聊聊Assetbundle 中的脚本,在移动平台下Assetbundle里面放的脚本是不会被执行的,还记得我们打包前给两个Prefab挂上了脚本吗?在手机上将Assetbundle下载到本地后,加载进游戏中Prefab会自动在本地找它身上挂着的脚本,他是根据脚本的名来寻找,如果本地有这条脚本的话,Prefab会把这个脚本重新绑定在自身,并且会把打包前的参数传递进来。如果本地没有,身上挂的条脚本永远都不会被执行。

如果你的Assetbundle中的Prefab上引用的对象,那么这样做就会出错了,你需要设定他们的依赖关系。或者运行时通过脚本动态的载入对象。

http://docs.unity3d.com/Documentation/ScriptReference/BuildPipeline.PopAssetDependencies.html

http://docs.unity3d.com/Documentation/ScriptReference/BuildPipeline.PushAssetDependencies.html

像这样重新打包就可以。但是,Unity官方表示这些方法都过时了,推荐使用Unity5.0以上的资源打包API了!可以避免很多坑!

2014年10月补充

WWW.LoadFromCacheOrDownload 这个方法建议大家以后不要再用了

因为是异步方法,而且还占用内存。

强烈建议使用AssetBundle.CreateFromFile 它是一个同步方法。现在IOS 和 android 都支持了,强烈建议用。

打包的时候需要选择不压缩。

//打包场景
            BuildPipeline.BuildStreamedSceneAssetBundle(levels, path, target, BuildOptions.UncompressedAssetBundle))
//打包资源
            BuildPipeline.BuildAssetBundle(null, assets, path, BuildAssetBundleOptions.UncompressedAssetBundle | BuildAssetBundleOptions.CollectDependencies, target);

因为不压缩, 所以就需要我们自己来压缩资源了, 可以用LZMA 和 GZIP来进行压缩。

1.打包出来的Assetbundle我们自己用LZMA压缩,上传到服务器上。

2.IOS或者Android下载这些assetbundle

3.解压缩这些assetbundle并且保存在Application.persistentDataPath 目录下。

4.以后通过AssetBundle.CreatFromFile  读取assetbundle。

此法确实可行,我们已经在实际项目中轰轰烈烈的使用了。。

http://www.xuanyusong.com/archives/2405

原文地址:https://www.cnblogs.com/MrZivChu/p/assetbundle2.html