buildAsset分析(三)——BuildAssetBundle

当然前面两篇的都是为这篇的代码服务的。
BuildAssetBundle.cs这个文件就是主要的打包assetbundle操作了。

首先是这些声明:

 //assetbundle资源
    static string assetFilePath = "/assetbundles";
    static string buildCfgFilePath = Application.dataPath + "/Editor/Build/build.txt";
    static string levelCfgFilePath = Application.dataPath + "/Editor/Build/level.txt";
    static string buildResourceCfg = Application.dataPath + "/Editor/Build/Resource.txt";

    //打包环境设置
    static BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle;
    static BuildTarget buildPlatform = BuildTarget.StandaloneWindows;

    //保存所有的scene信息
    static List<string> mScenes = new List<string>();

    //保存所有Resource信息
    static List<string> mResources = new List<string>();

    //保存所有的Asset信息 场景+Resource
    //保存所有要用到的资源,并按依赖数量分好了level
    //int:代表level
    //string:文件的路径,包括路径和文件名.
    //AssetUnit:string文件的各种信息存放在这里面
    static Dictionary<int, Dictionary<string, AssetUnit>> allLevelAssets = new Dictionary<int, Dictionary<string, AssetUnit>>();

然后接下来的3个函数不言而喻:
这三个函数都是放到editor中的,
显示到Editor中就是这样子的。
这里写图片描述

    //Andriod打包
    [MenuItem("Build/BuildAndriod")]
    public static void BuildAndriod()
    {
        buildPlatform = BuildTarget.Android;
        BuildResourceFromUnityRule();
    }

    //IOS打包
    [MenuItem("Build/BuildIOS")]
    public static void BuildIOS()
    {
        buildPlatform = BuildTarget.iPhone;
        BuildResourceFromUnityRule();
    }

    //Windows打包
    [MenuItem("Build/BuildWindows")]
    public static void BuildWindows()
    {
        buildPlatform = BuildTarget.StandaloneWindows;
        BuildResourceFromUnityRule();
    }
当然最主要的还是BuildResourceFromUnityRule()这个函数:
public static void BuildResourceFromUnityRule()
    {
        //清空数据
        //assetbundleFilePath:Application.dataPath + "/assetbundles/"
        if (Directory.Exists(ResourceCommon.assetbundleFilePath))
            Directory.Delete(ResourceCommon.assetbundleFilePath, true);

        //刷新数据
        Caching.CleanCache();
        AssetDatabase.Refresh();

        //获取所有Scene信息
        //通过自己定义的levelCfgFilePath目录的文件来获取所有Scene的路径,
        //然后存到mScenes里面
        GetBuildScenes();

        //获取所有需要build的文件,
        //然后保存到mResources中,当然保存的都是路径信息
        GetBuildResources();

        //get到所有mScenes和mResources中的
        //资源的所有依赖,这样所有要用到的资源都get到了,
        //然后按依赖个数把这些资源分等级。
        //就是填充allLevelAssets这个Dictionary
        GetAllAssets();

        //保存Asset资源信息
        //这个函数里面生成了所有文件(未打包)的XML信息,
        //并保存到Assets/assetbundles/AssetInfo.bytes文件中
        DumpAssetInfoFile();

        //打包asset
        //打包Assets/Resources下的所有资源(排除了无用资源)
        //打包到Assets/assetbundles目录下
        BuildResource();

        //保存资源配置assetbundle
        //所有打包好的资源的信息统一保存到
        //Assets/assetbundles/Resource.bytes文件中(XML格式)
        DumpResourceFile();

        //根据所有assetbundle文件生成版本信息
        //对每一个打包好的文件计算MD5值,
        //并把文件基本信息存成XML文件version.bytes
        //生成在Assets/assetbundles目录下,然后再复制到Assets/Resources目录下
        //这里面也顺便把文件size存到了对应的AssetUnit里面
        DumpVersionFile();

        //根据生成的assetbundle文件大小填充 assetInfo.byte文件
        //读取之前存到AssetUnit里面的的size,然后修改AssetInfo.bytes文件
        AddAssetSizeToAssetInfoFile();

        AssetDatabase.Refresh();
        DebugEx.LogError("BuildResource finish", "BuildResource", true);
    }

剩下的就是具体实现了,太长了,不过还是一下子贴出来好了:

//获取需要打包Resources目录下所有资源信息
    //Assets/Resources目录下所有文件存到mResources里,除了meta文件和Version.bytes文件
    [MenuItem("Build/Tools/GetBuildResources")]
    public static void GetBuildResources()
    {
        mResources.Clear();

        //Resource资源路径
        string resourcePath = Application.dataPath + "/Resources/";

        string[] files = Directory.GetFiles(resourcePath, "*.*", SearchOption.AllDirectories);
        foreach (string file in files)
        {
            string suffix = BuildCommon.getFileSuffix(file);
            if (suffix == "meta")
                continue;
            string realFile = file.Replace("\\", "/");
            realFile = realFile.Replace(Application.dataPath, "Assets");

            if (realFile == "Assets/Resources/Version.bytes")
                continue;

            mResources.Add(realFile);
        }
    }


    //获取选中需要打包的Resources资源信息
    [MenuItem("Build/Tools/GetSelectedBuildResources")]
    public static void GetSelectedBuildResources()
    {
        mResources.Clear();

        //Resource资源路径
        //string resourcePath = Application.dataPath + "/Resources/";

        //string[] files = Directory.GetFiles(resourcePath, "*.*", SearchOption.AllDirectories);
        //foreach (string file in files)
        //{
        //    string suffix = BuildCommon.getFileSuffix(file);
        //    if (suffix == "meta" || suffix == "cs")
        //        continue;
        //    string realFile = file.Replace("\\", "/");
        //    realFile = realFile.Replace(Application.dataPath, "Assets");
        //    mResources.Add(realFile);
        //}
        Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
        if (selection.Length == 0)
            return;

        foreach (Object obj in selection)
        {
            string assetPath = AssetDatabase.GetAssetPath(obj);
            mResources.Add(assetPath);


            Debug.Log("select:" + assetPath);
        }
    }



    //获取需要打包的Scene目录下的所有资源信息
    public static void GetBuildScenes()
    {
        mScenes.Clear();

        //获取场景路径
        FileStream fs = new FileStream(levelCfgFilePath, FileMode.Open);
        StreamReader sr = new StreamReader(fs);
        string path = "";
        while (path != null)
        {
            if (path != "")
            {
                mScenes.Add(path);
            }
            path = sr.ReadLine();
        }
        sr.Close();
        fs.Close();
    }


    [MenuItem("Build/Tools/TestSceneAssets")]
    public static void TestSceneAssets()
    {
        string sceneName = "Assets/Scenes/1.unity";

        string[] depends = AssetDatabase.GetDependencies(new string[] { sceneName });
        foreach (string str in depends)
        {
            Debug.Log("depency: " + str);
        }
    }

    //获取所有的Asset
    [MenuItem("Build/Tools/GetAllAssets")]
    public static void GetAllAssets()
    {
        //清空操作
        allLevelAssets.Clear();

        List<string> allAssetPath = new List<string>();
        //1添加场景路径

        foreach (string scene in mScenes)
        {
            allAssetPath.Add(scene);
        }

        //2添加Resource资源路径
        foreach(string resrPath in mResources)
        {
            allAssetPath.Add(resrPath);
        }

        //获取场景以及预制件中所有的资源信息
        //保存场景文件以及所有的resource文件的依赖
        string[] allExportAssets = AssetDatabase.GetDependencies(allAssetPath.ToArray());

        //这里allFiles好像声明了并没用过
        Dictionary<string, AssetUnit> allFiles = new Dictionary<string, AssetUnit>();
        foreach (string p in allExportAssets)
        {
            //if (p.Contains(".dll") || p.Contains(".cs"))
            if (p.Contains(".dll"))
                continue;

            AssetUnit unit = new AssetUnit(p);

            //Asset等级
            //level最低是1.
            int level = unit.mLevel;

            //存在
            if (allLevelAssets.ContainsKey(level))
            {
                allLevelAssets[level].Add(p, unit);
            }
            else 
            {
                //添加等级
                Dictionary<string, AssetUnit> levelAsset = new Dictionary<string, AssetUnit>();
                allLevelAssets.Add(level,levelAsset);
                //添加asset信息
                allLevelAssets[level].Add(p, unit);
            }
        }

        //创建Asset索引
        BuildAssetUnitIndex();
    }

    [MenuItem("Build/Tools/CheckResources")]
    public static void CheckResources()
    {
         AssetDatabase.Refresh();

        //获取资源信息
        GetBuildScenes();
        GetBuildResources();

        CheckAssetValid();

        Debug.Log("Check finised");
    }


    [MenuItem("Build/Tools/BuildSelected")]
    public static void BuildSelected()
    {
        //清空数据
        if (Directory.Exists(ResourceCommon.assetbundleFilePath))
            Directory.Delete(ResourceCommon.assetbundleFilePath, true);

        //刷新数据
        Caching.CleanCache();
        AssetDatabase.Refresh();

        //获取资源信息
        GetBuildScenes();
        GetSelectedBuildResources();

        //获取所有的打包asset
        GetAllAssets();

        //保存Asset资源信息
        DumpAssetInfoFile();

        //打包asset
        BuildResource();

        //保存资源配置assetbundle
        DumpResourceFile();

        //根据所有assetbundle文件生成版本信息
        DumpVersionFile();

        //根据生成的assetbundle文件大小填充 assetInfo.byte文件 
        AddAssetSizeToAssetInfoFile();

        AssetDatabase.Refresh();
        DebugEx.LogError("BuildResource finish", "BuildResource", true);
    }



    public static void BuildResource()
    {
        AssetDatabase.Refresh();

        //执行依赖性打包

        //资源最大等级
        int maxLevel = allLevelAssets.Count;
        if (maxLevel == 0)
            return;

        //从最低等级开始打包
        for (int level = 1; level <= maxLevel; level++)
        {
            //启用以下所有资源的交叉引用,
            //直到调用PopAssetDependencies为止
            //--这算是打包的必要步骤,
            BuildPipeline.PushAssetDependencies();

            //获取不同等级的aaset
            Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level];

            //遍历该等级的所有asset打包
            foreach(KeyValuePair<string, AssetUnit> pair in levelAssets)
            {
                //根据路径获取asset资源
                Object asset = AssetDatabase.LoadMainAssetAtPath(pair.Value.mPath);
                if (null == asset)
                    DebugEx.LogError("load " + pair.Value.mPath + " failed!!!", "BuildResource", true);

                //生成打包保存路径
                //assetbundleFileSuffix = ".bytes";
                string savePath = pair.Value.mPath.Insert(6, assetFilePath) + ResourceCommon.assetbundleFileSuffix;
                BuildCommon.CheckFolder(BuildCommon.getPath(savePath));

                //打包名称去Asset
                //场景资源(xxx.unity)和普通资源分开
                //普通资源
                //打包成xxx.bytes文件
                //可以到Assets/assetbundles这个目录下去看看
                if (pair.Value.mSuffix != "unity")//普通资源
                {
                    string assetName = pair.Value.mPath.Replace("Assets/", "");

                    //资源打包
                    //这函数过时了
                    if (!BuildPipeline.BuildAssetBundleExplicitAssetNames(
                               new Object[] { asset }, new string[] { assetName }, savePath, options, buildPlatform))
                        DebugEx.LogError("build assetbundle " + savePath + " failed!!!", "BuildResource", true);
                }
                //场景资源,没有依赖场景的
                else 
                {
                    AssetDatabase.Refresh();
                    BuildPipeline.PushAssetDependencies();
                    string error = BuildPipeline.BuildStreamedSceneAssetBundle(new string[]{pair.Value.mPath}, savePath, buildPlatform);
                    if (error != "")
                        DebugEx.LogError(error, "BuildResource", true);
                    BuildPipeline.PopAssetDependencies();

                    Debug.Log("scene path" + pair.Value.mPath);
                    //pair.Value.mPath
                }
            }   
        }

        //popdepency依赖
        for (int level = 1; level <= maxLevel; level++)
        {
            BuildPipeline.PopAssetDependencies();
        }
    }


    //创建Asset资源信息
    //把所有资源的信息保存到一个xml文件里面
    public static void DumpAssetInfoFile()
    {
        if(allLevelAssets.Count ==0)
            return;

        ////创建所有资源Asset列表
        XmlDocument doc = new XmlDocument();
        XmlElement root = doc.CreateElement("AllAssets");

        //遍历所有Asset数据
        for (int level = 1; level <= allLevelAssets.Count; level++)
        {
            Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level];
            foreach (KeyValuePair<string, AssetUnit> asset in levelAssets)
            {
                string assetName = asset.Key;
                AssetUnit assetUnit = asset.Value;

                XmlElement ele = doc.CreateElement("Asset");

                //设置路径名称
                assetName = assetName.Replace("Assets/", "");
                ele.SetAttribute("name", assetName);

                //设置asset索引
                ele.SetAttribute("index", assetUnit.mIndex.ToString());

                //设置等级
                ele.SetAttribute("level", level.ToString());

                List<AssetUnit> sortDepencys = new List<AssetUnit>();
                //获取AssetUnit所有依赖,并排序
                List<string> depencys = assetUnit.mAllDependencies;
                foreach (string depency in depencys)
                {
                    AssetUnit depencyUnit = GetAssetUnit(depency);
                    sortDepencys.Add(depencyUnit);
                }

                //排序
                sortDepencys.Sort(SortAssetUnit);
                //保存依赖索引
                string depencystr = "";
                for (int i = 0; i < sortDepencys.Count; i++)
                {
                    AssetUnit unit = sortDepencys[i];

                    if (i != sortDepencys.Count - 1)
                        depencystr += unit.mIndex + ",";
                    else
                        depencystr += unit.mIndex;
                }
                ele.SetAttribute("depency", depencystr.ToString());

                root.AppendChild(ele);
            }
        }

        doc.AppendChild(root);

        //这是assetbundleFilePath的声明:写在ResourceCommon里面
        //public static string assetbundleFilePath = Application.dataPath + "/assetbundles/";
        BuildCommon.CheckFolder(BuildCommon.getPath(ResourceCommon.assetbundleFilePath));
        doc.Save(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes");

        Debug.Log("CreateAssetInfo success!!!");
    }

    //创建资源索引
    public static void DumpResourceFile()
    {
        ////创建所有资源Asset列表
        XmlDocument doc = new XmlDocument();
        XmlElement root = doc.CreateElement("AllResources");

        XmlElement resource = doc.CreateElement("Resources");
        root.AppendChild(resource);
        foreach (string res in mResources)
        {
            string ex = BuildCommon.getFileSuffix(res);
            string path = res.Replace("Assets/", "");
            //去掉后缀
            path = path.Replace("." + ex, "");

            XmlElement ele = doc.CreateElement("file");
            ele.SetAttribute("name", path);
            ele.SetAttribute("type", ex);
            resource.AppendChild(ele);
        }

        //创建所有需要打包的Level列表
        XmlElement sceneRes = doc.CreateElement("Level");
        root.AppendChild(sceneRes);
        foreach (string scene in mScenes)
        {
            XmlElement ele = doc.CreateElement("file");

            string path = scene.Replace("Assets/", "");
            path = path.Replace(".unity", "");

            ele.SetAttribute("name", path);
            ele.SetAttribute("type", "unity");
            sceneRes.AppendChild(ele);
        }
        doc.AppendChild(root);
        BuildCommon.CheckFolder(BuildCommon.getPath(ResourceCommon.assetbundleFilePath));
        doc.Save(ResourceCommon.assetbundleFilePath + "Resource.bytes");

        Debug.Log("CreateResourceCfg success!!!");
    }


    [MenuItem("Build/Tools/DumpVersionFile")]
    public static void DumpVersionFile()
    {
        List<string> allFiles = new List<string>();
        BuildCommon.GetFiles(ResourceCommon.assetbundleFilePath, allFiles, true);

        XmlDocument doc = new XmlDocument(); 
        XmlElement root = doc.CreateElement("Version");
        root.SetAttribute("Number", "1.0.0");
        root.SetAttribute("Big", "false");

        foreach (string element in allFiles)
        {
            int size = 0;
            string md5 = GetFileMD5(element, ref size);
            XmlElement ele = doc.CreateElement("file");
            string relativePath = element.Replace(Application.dataPath + "/assetbundles/", "");          

            ele.SetAttribute("fpath", relativePath);
            ele.SetAttribute("size", size.ToString());
            ele.SetAttribute("md5", md5);
            root.AppendChild(ele);

            //保存大小信息到AssetUnit中
            //一开始初始化AssetUnit的时候,size为0,
            //在这里把size存进AssetUnit
            string assetName = "Assets/" + relativePath;
            assetName = assetName.Substring(0, assetName.Length - 6);
            AssetUnit assetUnit = GetAssetUnit(assetName);
            if(assetUnit != null)
                assetUnit.mAssetSize = size;
        }

        doc.AppendChild(root);
        string assetBundleVersionPath = ResourceCommon.assetbundleFilePath + "Version.bytes";
        doc.Save(assetBundleVersionPath);

        //拷贝到对应的Resource目录
        string resourceVersionPath = Application.dataPath + "/Resources/" + "Version.bytes";
        File.Copy(assetBundleVersionPath, resourceVersionPath, true);

        Debug.Log("Dump Version Success!!!");
    }

    //添加asset信息
    private static void AddAssetSizeToAssetInfoFile()
    {
        //添加asset信息
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes");

        XmlNodeList xnl = xmlDoc.SelectSingleNode("AllAssets").ChildNodes; 
        for (int i = 0; i < xnl.Count; i++)
        { 
            XmlElement xe = (XmlElement)xnl.Item(i);
            string assetName = xe.GetAttribute("name");

            //获取对应的AssetUnit
            AssetUnit unit = GetAssetUnit("Assets/" + assetName);

            xe.SetAttribute("bundlesize", unit.mAssetSize.ToString());
        }
        xmlDoc.Save(ResourceCommon.assetbundleFilePath + "AssetInfo.bytes");
    }

    //计算文件MD5值,顺便获得文件size
    //其实我觉得这函数写到BuildCommon里比较好
    private static string GetFileMD5(string fpath, ref int size)
    {
        FileStream fs = new FileStream(fpath, FileMode.Open);
        MD5 md5 = MD5.Create();
        byte[] vals = md5.ComputeHash(fs);
        string ret = BitConverter.ToString(vals);
        ret = ret.Replace("-", "");
        size = (int)fs.Length;
        fs.Close();
        return ret;
    }


    //按等级顺序创建AssetUnit的索引信息
    //给allLevelAssets中的所有level中的所有AssetUnit的mIndex赋上值
    //从level0的第一个AssetUnit开始,第一个AssetUnit的index为0,然后后面的AssetUnit依次index++
    private static void BuildAssetUnitIndex()
    {
        if (allLevelAssets.Count == 0)
            return;

        int index = 0;
        for (int level = 1; level <= allLevelAssets.Count; level++)
        {
            Dictionary<string, AssetUnit> levelAssets = allLevelAssets[level];
            foreach (KeyValuePair<string, AssetUnit> pair in levelAssets)
            {
                AssetUnit unit = pair.Value;
                unit.mIndex = index;

                index++;
            }
        }     
    }

    //根据Asset名称获取对应的AssetUnit
    private static AssetUnit GetAssetUnit(string name)
    {
        if (allLevelAssets.Count == 0)
            return null;

        for (int level = 1; level <= allLevelAssets.Count; level++)
        {
            Dictionary<string, AssetUnit> assetUnit = allLevelAssets[level];

            if (assetUnit.ContainsKey(name))
                return assetUnit[name];
        }

        return null;
    }


     //检查所有的打包资源
     private static void CheckAssetValid()
     {
          //清空操作
        allLevelAssets.Clear();

        List<string> allAssetPath = new List<string>();
        //1添加场景路径

        foreach (string scene in mScenes)
        {
            allAssetPath.Add(scene);
        }

        //2添加Resource资源路径
        foreach(string resrPath in mResources)
        {
            allAssetPath.Add(resrPath);
        }

        //获取场景以及预制件中所有的资源信息
        string[] allExportAssets = AssetDatabase.GetDependencies(allAssetPath.ToArray());

        List<string> checkAssets = new List<string>();

        foreach (string p in allExportAssets)
        {
            if (p.Contains(".dll"))
                continue;

            string assetName = Path.GetFileName(p);
            assetName = assetName.ToLower();

            if(checkAssets.Contains(assetName))
            {
                DebugEx.Log("the asset " + assetName + "has already exsited");
            }
            else
            {
                checkAssets.Add(assetName);
            }
        }

     }

    //对AssetUnit进行排序
    private static int SortAssetUnit(AssetUnit unit1, AssetUnit unit2)
    {
        int res = 0;
        if (unit1.mLevel > unit2.mLevel)
        {
            res = 1;
        }
        else if (unit1.mLevel < unit2.mLevel)
        {
            res = -1;
        }
        return res;
    }
}

太长了,不过文章开头应该能理清大概步骤了,然后后面很长的代码就是具体的实现代码,这样应该会好理解一些。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值