Unity3D高级-AssetBundle使用

2020-06-02 11:49:58 浏览数 (1)

0、游戏套路

我们经常下载一些游戏App,如果Wifi情况下,App大小就无关紧要。但是如果是流量呢?一看到App的大小,直接就不下了。但是游戏公司怎么会不知道?所以采用热更新,或者直接下载完毕后,再进行加载的行为进行笼络用户。当然这也是产品要求的结果。这里不说游戏的好玩,只讨论这种套路是什么手段进行的。

红线框中的游戏你敢流量下载?

1、什么是AssetBundle?

资源需要打包发布,所以Unity提供的打包策略,也就是AssetBundle。这个的方式有自己的压缩格式(LZMA压缩),其实就是精简你的资源。简称AB,几乎所有的资源都可以打包程AB,AB可以存放Unity可识别的任何资源类型,具体取决于文件的扩展名。例如:导入的文件后缀为“.byte”,Unity会将这些文件作为文本(TextAssets)导入。导入的文件后缀为“.spine”,Unity会将这些文件作为动画导入。


可以通过官方的Demo来进行学习:Unity5 AssetBundle Demo


2、AssetBundle的作用

这就是打包并上传服务器的资源流程

如果将上述流程反过来,就是加载服务器数据包

加载服务器的资源流程

3、如何打包成AssetBundle

由于我们在打包的时候,可能会重复打包,造成资源很大。所以将资源拆分细致后,独立打包。比如:一个模型的网格Mesh,材质Material,贴图Texture.

3-1、手动打包:

图片展示比较简单

Paste_Image.png

做完上面图片的效果就执行这一步

  • Unity5.0之前旧版AB打包 本先扫描所有要打包的资源,然后AssetDatabase.GetDespendencie获得所有的依赖,自己记录起来,由于怕资源名称相同,所以使用AssetDatabase。AssetPathToGUID获得资源的唯一id,然后存起来。 获得所有依赖关系之后,再使用BuildPipeline.PushAssetDependenciesBuildPipeline.PopAssetDependencies按照层级和顺序来打包。这样打出来的资源就完全保留了依赖关系,文件会被拆散,不会重复打包。
  • Unity5.0之后,AB打包使用下面的Api
代码语言:javascript复制
BulidPipeline.BuildAssetBundles(outputPath)

然后为每一个资源可以设置一个assetBundleName。只要调用这个方法,那么所有已经设置过AB的资源,就会自动打包。

4、如何解压AssetBundle

Unity5.0之后,默认就有MainAsset(可以指定也可以不指定),5.0之前是没有的。 如果指定mainAsset那么加载后可以通过下面来获取

代码语言:javascript复制
AssetBundle.mainAsset

如果不指定那么可以通过

代码语言:javascript复制
AssetBundle.LoadAsset 指定名字读取出来

官方Demo 可以加群134688909 将Demo下载下来,因为接下来全部使用这个进行。

这就是Demo的东西,第一步找到加载场景,第二步进行依赖关系生成,运行后Unity会根据依赖关系进行加载

5、AssetBundle依赖关系

官方Demo稍微修改一下 可以加群134688909 将Demo下载下来,因为接下来全部使用这个进行。

Paste_Image.png

将材质球添加进预制物并且给预制物添加AB

设置UI的AB

打包资源

这就是生成的依赖文件

Paste_Image.png

相关依赖


6、案例介绍

案例介绍:官方Demo 可以加群134688909 将Demo下载下来,因为接下来全部使用这个进行。

我们可以测试一下打包成程序

选中Build Player

会输出一个文件,你可以选择一个文件夹进行输出。

Paste_Image.png

我们实际这样做了,发现输出控制台有如下输出

这个表示没有什么可以打包的

我们可以将寻找一下是那个代码中输出的这个话

Paste_Image.png

解释一下

顺藤摸瓜

顺藤摸瓜之后其实我们发现就是我们的当前项目中的场景没有加载进去

将场景拖拽进去

这个时候就进行打包了

打包后发现成为了一个PC客户端

运行后,发现与我们手动打包出来的程序一模一样,也就是说我们可以程序的手段进行编译打包。那么这个代码在哪?我们是不是可以直接使用呢?

这就是内部的判断平台,根据平台打包


打完包后,返回工程发现里面中多了一个文件夹

StreamingAssets它下面的所有资源不会被加密,然后是原封不动的打包到发布包中

然后我们就可以开始玩耍游戏中的所有场景了,因为资源已经OK了。可以寻找并加载了。

7、批量命名打包

项目中的资源涉及方方面面,我们不可能每个都进行修改名字并打包,所以需要一个工具,,

Paste_Image.png

这里是核心代码块,也是游戏核心开发人员的必备技能涉及自定义编辑器工具模块。如果你需要可以加群获取134688909。群文件下载就ok!

8、

由于我们要将模型资源放在远程的服务器端,但如果直接放fbx模型是不可以加载的,所以我们可以将fbx做成预设或者是直接将其打包成assetbundle格式的,然后通过www来加载获取。 说下使用方法:

1、把附件脚本放到工程文件夹下的...AssetsEditor文件夹下。

2、在工程的Project视图里点击想要保存的资源,网络上推荐的是Prefab,右键点击,选择菜单里最下面的两个选项任意一个都可以,第一个选项对应的自定义属性有一个过期了,但是不影响使用。

3、指定文件的构建路径和文件后缀,后缀无所谓。

4、推荐制造做法:

任何形式的资源都可以,包括集合资源,比如创建一个空的GameObject,把所有想要关联的其他GameObject都拖进去,然后在project视图里创建一个prefab,将这个集合资源GameObject拖进去作为一个prefab。执行上面的第2、3步骤。

官方的讲解 1.首先要讲一下不同平台下的一个StreamingAssets路径,这是不同的。

代码语言:javascript复制
 //不同平台下StreamingAssets的路径是不同的,这里需要注意一下。
        public static readonly string PathURL =
#if UNITY_ANDROID   //安卓
        "jar:file://"   Application.dataPath   "!/assets/";
#elif UNITY_IPHONE  //iPhone
        Application.dataPath   "/Raw/";
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR  //windows平台和web平台
    "file://"   Application.dataPath   "/StreamingAssets/";
#else
        string.Empty;
#endif

这就获取到了不同平台的一个路径,我们可以将打包的文件放在这些路径下,然后再从这路径去读取资源。 2.关于打包assetbundle的脚本

代码语言:javascript复制
using UnityEngine;
using System.Collections;
using UnityEditor;
 
public class Test : Editor
{
    //打包单个
    [MenuItem("Custom Editor/Create AssetBunldes Main")]
    static void CreateAssetBunldesMain ()
    {
        //获取在Project视图中选择的所有游戏对象
        Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
 
        //遍历所有的游戏对象
        foreach (Object obj in SelectedAsset) 
        {
            //本地测试:建议最后将Assetbundle放在StreamingAssets文件夹下,如果没有就创建一个,因为移动平台下只能读取这个路径
            //StreamingAssets是只读路径,不能写入
            //服务器下载:就不需要放在这里,服务器上客户端用www类进行下载。
            string targetPath = Application.dataPath   "/StreamingAssets/"   obj.name   ".assetbundle";
            if (BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)) {
                Debug.Log(obj.name  "资源打包成功");
            } 
            else 
            {
                Debug.Log(obj.name  "资源打包失败");
            }
        }
        //刷新编辑器
        AssetDatabase.Refresh ();   
        
    }
    
    [MenuItem("Custom Editor/Create AssetBunldes ALL")]
    static void CreateAssetBunldesALL ()
    {
        
        Caching.CleanCache ();
 
 
        string Path = Application.dataPath   "/StreamingAssets/ALL.assetbundle";
 
        
        Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);
 
        foreach (Object obj in SelectedAsset) 
        {
            Debug.Log ("Create AssetBunldes name :"   obj);
        }
        
        //这里注意第二个参数就行
        if (BuildPipeline.BuildAssetBundle (null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies)) {
            AssetDatabase.Refresh ();
        } else {
            
        }
    }
    
    [MenuItem("Custom Editor/Create Scene")]
    static void CreateSceneALL ()
    {
        //清空一下缓存
        Caching.CleanCache();
        string Path = Application.dataPath   "/MyScene.unity3d";
        string  []levels = {"Assets/Level.unity"};
        //打包场景
        BuildPipeline.BuildPlayer( levels, Path,BuildTarget.WebPlayer, BuildOptions.BuildAdditionalStreamedScenes);
        AssetDatabase.Refresh ();
    }
    
}

前提要新手动创建一个StreamingAssets文件夹

3.读取资源,这里只举例从本地读取,跟从网络读取是一样的,可以参考官方文档:

本地读取

代码语言:javascript复制
using UnityEngine;
using System.Collections;
 
public class RunScript : MonoBehaviour
{
    
 
        //不同平台下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   "Prefab0.assetbundle"));
            //StartCoroutine(LoadMainGameObject(PathURL    "Prefab1.assetbundle"));
        
            StartCoroutine(LoadMainCacheGameObject(PathURL   "Prefab0.assetbundle"));
            StartCoroutine(LoadMainCacheGameObject(PathURL    "Prefab1.assetbundle"));
        }
        
        if(GUILayout.Button("ALL Assetbundle"))
        {
            StartCoroutine(LoadALLGameObject(PathURL   "ALL.assetbundle"));
        }
        
        if(GUILayout.Button("Open Scene"))
        {
            StartCoroutine(LoadScene());
        }
        
    }
    
    //读取一个资源
    
    private IEnumerator LoadMainGameObject(string path)
    {
         WWW bundle = new WWW(path);
         
         yield return bundle;
         
         //加载到游戏中
         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("Prefab0");
         Object  obj1 =  bundle.assetBundle.Load("Prefab1");
        
         //加载到游戏中   
         yield return Instantiate(obj0);
         yield return Instantiate(obj1);
         bundle.assetBundle.Unload(false);
    }
    
    private IEnumerator LoadMainCacheGameObject(string path)
    {
         WWW bundle = WWW.LoadFromCacheOrDownload(path,5);
         
         yield return bundle;
         
         //加载到游戏中
         yield return Instantiate(bundle.assetBundle.mainAsset);
         
         bundle.assetBundle.Unload(false);
    }
    
    
    private IEnumerator LoadScene()
    {
         WWW download = WWW.LoadFromCacheOrDownload ("file://" Application.dataPath   "/MyScene.unity3d", 1);
            
          yield return download;
          var bundle = download.assetBundle;
          Application.LoadLevel ("Level");
    }
}

如果assetbundle文件放在服务器端,直接用www.loadfromcacheordownload()通过版本来控制是否从服务器下载并保存到本地。本人亲自测试,这个方法是能下载到本地的,存在沙盒文件下(移动开发者的朋友应该知道),当然也可以自己来做版本控制,那样更灵活,并且摆脱www.loadfromcacheordownload()方法的束缚,貌似这个方法存贮文件是有空间大小限制的,据说是50M,本人没有亲自考证,后期我会自己做一个版本控制!

9、使用UnityWebRequest加载AssetBundle

1.使用UnityWebRequest需要引用using UnityEngine.Networking. 2.UnityWebRequest中有几个方法,UnityWebRequest.GetAssetBundle(URL)获取assetBundle资源,有一个返回资源的函数SendWebRequest,用来下载资源,DownloadHandlerAssetBundle.GetContent()未获取assetBundle资源包,接着就是LoadAsset<>()加载,实例化。 3.代码如下

代码语言:javascript复制
void Start () { 
StartCoroutine(Load()); 
}
private IEnumerator Load()
{
    string url = "";//此为AssetBundle资源路径,可为本地,也可以是服务端
   UnityWebRequest request=  UnityWebRequest.GetAssetBundle(url);
    yield return request.SendWebRequest ();
    // AssetBundle ab = DownloadHandlerAssetBundle.GetContent (request );

    AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
    GameObject ao = ab.LoadAsset<GameObject>("Man1");
    Instantiate(ao);
}
代码语言:javascript复制
IEnumerator LoadABWeb()
    {
        UnityWebRequest request = UnityWebRequest.GetAssetBundle(path);
        yield return request.SendWebRequest();
        Debug.Log(request.error);
        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
        audio.clip = bundle.LoadAsset<AudioClip>("music");
        audio.Play();
    }

0 人点赞